우아한테크코스에서 말하는 클린코드
클린코드는 단순히 동작하는 코드가 아니라, 가독성 좋고 유지보수하기 쉬운 코드를 의미합니다. 우아한 테크코스에서 강조하는 객체지향 설계 원칙과 코드 작성 방법을 통해 클린코드 작성을 위한 구체적인 지침을 살펴보겠습니다. <GPT와 함께>
1. 자바 코드 컨벤션을 지키면서 프로그래밍했는가?
자바에서는 일관된 코드 스타일을 유지하는 것이 매우 중요합니다. 이를 위해 Google Java Style Guide와 같은 자바 코드 컨벤션을 준수하면 좋습니다.
IntelliJ나 Eclipse와 같은 IDE에서 자동으로 코드 포매팅을 적용하여 일관된 코딩 스타일을 유지하는 것이 바람직합니다. 이로써 협업 시 가독성과 일관성이 확보됩니다.
: IDE에서 구글 자바 스타일 포메팅을 적용하세요.
2. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?
들여쓰기가 여러 단계로 중첩되면 코드가 복잡해집니다. 메서드에 한 단계의 들여쓰기만 허용함으로써 복잡도를 줄이고 가독성을 높이는 것이 중요합니다. 이는 **단일 책임 원칙(SRP)**을 적용하여 메서드가 하나의 책임만 가지도록 설계하는 것과 연관됩니다.
:이중 for문을 주의하세요.
public void processMatrix(int[][] matrix) {
for (int i = 0; i < matrix.length; i++) {
processRow(matrix[i]);
}
}
private void processRow(int[] row) {
for (int j = 0; j < row.length; j++) {
// 처리 로직
}
}
3. else 예약어를 쓰지 않았는가?
else 예약어는 코드를 불필요하게 복잡하게 만들 수 있습니다. 대신, **가드 클로즈(guard clause)**를 사용하여 조건에 맞지 않는 경우 빨리 반환하는 방식으로 코드를 단순하게 만듭니다.
if (condition) {
return;
}
// 주 로직 실행
이런 방식은 코드를 명확하고 간결하게 만들어줍니다
: else는 잊으세요.
4. 모든 원시값과 문자열을 포장했는가?
자바의 **원시값(primitive type)**과 문자열을 그대로 사용하는 것보다는 이를 객체로 포장하여 사용하는 것이 좋습니다. 이를 통해 값에 대한 검증이나 행위를 객체 내부에서 처리할 수 있으며, 코드의 표현력과 유지보수성이 향상됩니다.
public class Money {
private final int amount;
public Money(int amount) {
if (amount < 0) {
throw new IllegalArgumentException("Amount cannot be negative");
}
this.amount = amount;
}
}
: 다소 다가오지 않는 부분이었습니다. 아래의 코드처럼 가격이라면 음수가 나오면 안되는 상황, 이름이라면 공백이 입력되면 안 되는 상황들에 대한 부분을 포장을 해 비즈니스 규칙과 연관된 객체로 처리하라는 의미였습니다.
public class Price {
private final int amount;
public Price(int amount) {
if (amount < 0) {
throw new IllegalArgumentException("Price cannot be negative");
}
this.amount = amount;
}
public int getAmount() {
return amount;
}
public Price add(Price other) {
return new Price(this.amount + other.amount);
}
}
public class CustomerName {
private final String name;
public CustomerName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("Customer name cannot be empty");
}
this.name = name;
}
public String getName() {
return name;
}
}
이렇게 된다면 클래스가 너무 많이 생기는 거 아닌가요?
-> 적절히 분리하세요. 단일책임원칙과 유효성 검증, 의미 명확화, 응집성등을 고려해서... 중복기능은 상속, 인터페이스를 통해 재사용하면서 잘 처리 하세요.
그것이 바로 코딩실력!?
5. 컬렉션에 대해 일급 컬렉션을 적용했는가?
일급 컬렉션은 컬렉션을 객체로 감싸는 방식으로, 이를 통해 불변성을 보장하고 컬렉션 관련 행위를 객체 내부에서 관리할 수 있습니다. 이를 사용하면 코드가 더욱 응집성을 가지며 유지보수성이 향상됩니다.
public class Items {
private final List<Item> items;
public Items(List<Item> items) {
this.items = new ArrayList<>(items); // 외부에서 넘어온 리스트를 복사해서 사용
}
public void addItem(Item item) {
items.add(item);
}
public int totalPrice() {
return items.stream().mapToInt(Item::getPrice).sum();
}
public List<Item> getItems() {
return Collections.unmodifiableList(items); // 외부에서 수정 못하게 읽기 전용 리스트 반환
}
}
public class Cart {
private final Items items;
public Cart(Items items) {
this.items = items;
}
public void addItem(Item item) {
items.addItem(item);
}
public int totalPrice() {
return items.totalPrice();
}
}
:일급 비밀도 아니고 이게 뭔소리인가 했습니다. First-Class Collection. 컬랙션을 래핑하라는 건데 그니까 뭔소리냐면
위의 Cart 클래스에서 Items에 대한 컬렉션 조절을 못하게 하라 라는 소리 같습니다. unmodifableList로 반환해버리니까요. 단일책임 원칙에도 준수한 내용 같습니다. ex) 반 - 반 학생들, 도서관 - 책목록 등등... 구현해보면서 알아가야할 것 같습니다
6. 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?
클래스는 가급적 인스턴스 변수를 3개 이하로 유지하는 것이 좋습니다. 많은 인스턴스 변수를 가진 클래스는 복잡도가 높아지고, 책임이 많아지는 문제가 생길 수 있습니다. 인스턴스 변수가 많다면, 이를 별도의 클래스로 분리하여 관리하는 것을 고려해야 합니다.
: 3개는 너무 극단적인 것 아닙니까!! 연관관계가 두개만 있어도 Id 까지 세개인데요!!!!!! 게다가 date까지 고려하면 !!! 은 농담이구요. 설계할때 잘 분리 하세요.
7. getter/setter 없이 구현했는가?
객체의 상태는 외부에서 직접 접근하는 것이 아니라, 객체 자체가 스스로 관리해야 합니다. getter와 setter를 사용하는 대신 행위를 통한 상태 관리를 해야 합니다. 단, DTO와 같은 단순 데이터 전달 객체에서는 getter/setter가 허용됩니다.
:getter까지 쓰지 말라구요? 네, 해볼게요...
8. 메소드의 인자 수를 제한했는가?
메소드 인자는 4개 이상 사용하지 않는 것이 원칙입니다. 인자가 많으면 코드의 가독성과 테스트의 복잡성이 증가합니다. 가능하면 관련된 인자들을 객체로 묶어서 전달하는 방식으로 인자 수를 줄이는 것이 좋습니다.
:네 하라면 해야죠, 모두 때려박던 나 반성해..4개가 되면 묶을 것을 찾아보는걸로...그래도 못 줄이면 빌더로
9. 코드 한 줄에 점(.)을 하나만 허용했는가?
**디미터의 법칙(Law of Demeter)**에 따라 코드 한 줄에 점(.)을 하나만 사용하는 것이 좋습니다. 이는 객체들이 자신이 직접 알고 있는 객체와만 통신하도록 제한하는 원칙으로, 결합도를 낮추고 코드의 유지보수성을 높입니다.
// 나쁜 예시: 여러 객체에 의존
String zipCode = user.getAddress().getCity().getZipCode();
// 좋은 예시: 객체가 스스로 처리하게 만듦
String zipCode = user.getZipCode();
:결합도를 점하나로 낮추세요.
10. 메소드가 한 가지 일만 담당하도록 구현했는가?
메소드는 한 가지 일만 해야 하며, 여러 책임을 가지지 않도록 주의해야 합니다. 이를 통해 코드를 더 단순하고 명확하게 유지할 수 있으며, 메서드의 재사용성도 높아집니다. 또한 테스트하기가 쉬워지고 오류 발생 가능성을 줄일 수 있습니다.
11. 클래스를 작게 유지하기 위해 노력했는가?
클래스는 작은 크기로 유지되어야 하며, 하나의 책임만 가져야 합니다. **SRP(단일 책임 원칙)**을 준수하여 클래스가 한 가지 일만 하도록 설계하면, 코드의 가독성과 유지보수성이 향상됩니다. 또한 작은 클래스는 변경 사항에 유연하게 대처할 수 있습니다.
한줄요약
- 자바 코드 컨벤션 준수: 일관된 스타일로 가독성과 협업을 향상시킵니다.
- 한 단계 들여쓰기: 복잡도를 줄이고 가독성을 높입니다.
- else 사용 자제: 가드 클로즈로 코드 흐름을 단순화합니다.
- 원시값과 문자열 포장: 객체로 포장해 표현력을 높입니다.
- 일급 컬렉션 적용: 컬렉션을 객체로 감싸 불변성과 응집성을 유지합니다.
- 3개 이상의 인스턴스 변수 제한: 인스턴스 변수를 최소화하여 책임을 분리합니다.
- getter/setter 사용 자제: 객체가 스스로 상태를 관리하도록 설계합니다.
- 인자 수 제한: 인자를 줄여 코드 복잡성을 낮추고 가독성을 높입니다.
- 점(.) 하나만 허용: 디미터 법칙을 지켜 결합도를 낮춥니다.
- 메소드의 단일 책임: 메서드는 한 가지 일만 하도록 설계합니다.
- 클래스 크기 최소화: 클래스를 작고 명확하게 유지해 유지보수성을 향상시킵니다.
-우아한테크코스 클린코드 가이드
https://github.com/woowacourse/woowacourse-docs/blob/main/cleancode/pr_checklist.md
woowacourse-docs/cleancode/pr_checklist.md at main · woowacourse/woowacourse-docs
우아한테크코스 문서를 관리하는 저장소. Contribute to woowacourse/woowacourse-docs development by creating an account on GitHub.
github.com