오늘은 클린코드에 대해 알아보려고 합니다. Clean Code의 대략적인 개념에 대해서 알아보고, 추가로 클린 코드의 중요성과 적용 방법에 대해서도 예시를 들어 설명드리겠습니다.
Clean Code
- Clean Code does one thing well, Clean Code is simple and direct
- 단순하여 읽기 쉽고, 각 역할마다 주어진 하나의 일만 담당하며, 복잡하거나 모호하지 않은 코드
- 이를 통해 프로그램의 동작을 보장하는 것뿐만 아니라, 코드 자체가 가독성이 뛰어나고 유지보수가 쉬워집니다
- 원하는 로직을 빠르게 찾을 수 있는 코드이자 모든 팀원이 이해하기 쉽도록 작성된 코드
Importance of Clean Code
- 코드의 가독성이 떨어진다면 해당 코드가 무슨 일을 하는 코드인지, 어떤 메커니즘으로 굴러가는지 파악하는 데 오랜 시간이 소요됩니다
- 반면, 코드의 가독성이 올라간다면, 코드 파악, 리뷰, 디버깅에 드는 시간이 단축됩니다
- 이를 통해 코드를 이해하고 수정하는데 걸리는 시간이 단축되기 때문에 새로운 기능을 빠르게 구현하고 배포할 수 있습니다
- 소프트웨어 개발 과정에서 코드의 품질로 인해 발생할 수 있는 다양한 문제를 예방합니다
Clean Code 작성법
- 함수는 특정한 동작을 수행하므로 동사로 작성하고, 클래스나 속성의 이름은 명사로 작성하고 변수는 어떤 것을 위한 값인지를 분명하게 나타낼 수 있게 이름 지어야 합니다
- 의미 있는 변수와 함수 사용함으로써 코드의 이해도 증가
- 가독성이 좋은 코드로 작성해야 합니다.
- 다른 사람이 봤을 때, 코드의 의도가 무엇인지 파악하기 수월합니다
public Class Calculator { public static void main(String[] args) { int a = 10, b = 5; int result = addNumbers(a, b); System.out.println("Sum is:" + result); } public static int addNumbers(int num1, int num2) { return num1 + num2; } }
- 다른 사람이 봤을 때, 코드의 의도가 무엇인지 파악하기 수월합니다
- 모듈화 적용 -> 코드를 작은 조각으로 나누어 각 모듈의 특정 기능이나 역할을 수행하도록 해야 합니다
- 이는 코드의 재사용성을 높이고 유지보수를 쉽게 만듭니다
- camelCase 사용 ( 단어 여러 개를 결합할 때 뒤에 오는 단어의 첫 자를 대문자로 쓰는 표기법)
- changeUserInfo, changeUserInfoWithoutPasswordChange
- 함수를 작성할 때 하나의 함수가 하나의 일만 수행하도록 작성해야 합니다
- SRP, OCP 원칙에 위반
- 오류 코드보다는 예외를 활용하는 게 좋습니다
- 오류코드를 반환하면 그에 따른 분기가 일어나게 되고, 또 분기가 필요한 경우에는 중첩이 될 가능성이 높습니다
- 각 함수에서 예외를 발생시켜 잡는다면 코드를 더욱 간결하게 작성할 수 있습니다
- 응집도를 높이고, 결합도를 낮게 작성해야 합니다
- 응집도: 클래스의 메서드와 변수가 얼마나 의존하여 사용되는지를 의미
- 결합도: 모듈이 다른 모듈에 의존하는 정도
- 주석 최소화
- TODO / 외적인 맥락 / 제한사항과 같이 코드로 설명할 수 없는 부분만 주석으로 설명하고, 가능하면 코드 자체가 스스로 설명할 수 있도록(Self-Descriptive) 작성
- 디미터의 법칙(Law of Demeter)을 준수
- 디미터의 법칙: 다른 객체가 어떠한 자료를 갖고 있는지 속사정을 몰라야 한다는 것을 의미합니다
- 객체 간의 결합도를 낮추고 코드의 유지보수성을 높이기 위해 객체는 자신이 직접 참조하는 객체와만 상호작용해야 한다는 것을 의미합니다
- 객체의 내부 구조를 외부로 드러내지 않는 것을 의미합니다
- 직관적으로는 여러 개의 도트(.)를 사용하지 말라는 법칙을 의미합니다
- Don’t Talk to Stranger (낯선 이에게 말하지 마라), Principle of least Knowledge(최조 지식 원칙)
디미터의 원칙을 준수한 경우와 그렇지 않은 경우
// 준수한 경우
class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
public void startEngine() {
engine.start(); // Car 객체가 Engine을 캡슐화하고 있습니다
}
}
class Driver {
public void startCar(Car car) {
car.startEngine(); // 디미터의 법칙 준수: Driver는 Car 객체에만 의존하고 있습니다
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
Driver driver = new Driver();
driver.startCar(car); // Driver가 Car 객체에만 접근합니다
}
}
// 준수하지 못한 경우
class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
public Car() {
this.engine = new Engine();
}
public Engine getEngine() {
return engine;
}
}
class Driver {
public void startCar(Car car) {
car.getEngine().start(); // 디미터의 법칙 위반: car 객체의 엔진에 직접 접근하고 있습니다
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
Driver driver = new Driver();
driver.startCar(car); // Driver가 Car의 Engine에 직접 접근합니다
}
}
- null 리턴 및 인수로 받지 않도록 작성해야 합니다
<참고 자료>
https://yozm.wishket.com/magazine/detail/2415/
https://www.nextree.io/basic-of-clean-code/
https://mangkyu.tistory.com/132
'Java > Java Concept' 카테고리의 다른 글
[Java] inner class를 static으로 선언해야 하는 이유 (0) | 2024.05.16 |
---|---|
[Java] enum 비교에서는 equals(), == 둘 중 어떤 것이 적합한가? (0) | 2024.05.16 |
[Java] HashMap, HashTable, ConcurrentHashMap 자료구조에 대해서 알아보자 (2) | 2024.01.22 |
[Java] EnumSet에 대해서 (1) | 2023.11.13 |
[Java] 객체지향 사실과 오해 1- 협력하는 객체들의 공동체 (0) | 2023.03.10 |