우아한 테크코스 8기 프리코스 1주차[2] - Clean Code

BrokenFinger98·2025년 10월 14일

우아한 테크코스

목록 보기
3/5

우아한 테크코스 Clean Code 원칙

프리코스를 시작하기에 앞서 우아한 테크코스 미션에서 매번 코드리뷰 기준으로 등장하는 Clean Code 10대 원칙을 정리했습니다.
이 원칙들은 단순히 "코드 예쁘게 작성"이 아니라, 객체지향적 사고와 유지보수 가능한 구조를 훈련하는 기준이라고 생각합니다.

1. 한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가?

의미

한 메서드 안에서 if, for, while 등이 여러 번 중첩되면 복잡도가 증가합니다.
"들여쓰기 1단계까지만 허용"은 결국 메서드를 분리하라는 신호로 볼 수 있습니다.

나쁜 예

public int sum(String input) {
    if (input != null) {
        String[] numbers = input.split(",");
        int result = 0;
        for (String n : numbers) {
            if (!n.isEmpty()) {
                result += Integer.parseInt(n);
            }
        }
        return result;
    }
    return 0;
}

좋은 예

public int sum(String input) {
	if (isBlank(input)) return 0;
    return sumNumbers(parse(input));
}

private boolean isBlank(String input) { ... }
private List<Integer> parse(String input) { ... }
private int sumNumbers(List<Integer> nums) { ... }

중첩을 없애고 "메서드 추출"로 가독성 확보

2. else 예약어를 쓰지 않았는가?

의미

else는 불필요한 분기를 만들어 코드 흐름을 복잡하게 합니다.
"빠른 반환(early return)"을 사용하면 흐름이 단순해집니다.

나쁜 예

if (numbers.isEmpty()) {
	return 0;
} else {
	return calculate(numbers);
}

좋은 예

if (numbers.isEmpty()) return 0;
return calculate(numbers);

조건이 맞으면 바로 반환(guard clause)으로 단순하게!

3. 모든 원시값과 문자열을 포장했는가?

의미

int, String같은 원시값을 그대로 쓰면 의미가 불명확하고, 검증 로직이 여기저기 흩어집니다.
의미 단위로 클래스를 감싸는 것(Primitive Obsession 제거)이 중요합니다.

나쁜 예

public class Member {
	private String name;
    private int age;
}

좋은 예

public class Name {
	private String value;
    
    public Name(String value) {
   		if (value == null || value.isBlank()) throw new IllegalArgumentException();
        this.value = value;
	}
}

public class Age {
	private int value;
    
    public Age(int value) {
    	if (value < 0) throw new IllegalArgumentException();
        this.value = value;
    }
}

public class Member {
	private Name name;
    private Age age;
}

Name, Age가 도메인 단위의 의미 있는 타입으로 진화합니다.

4. 콜렉션에 대해 일급 콜렉션을 적용했는가?

의미

List, Map 같은 컬렉션을 직접 들고 있지 말고, 그 자체를 클래스로 감싸서 도메인 규칙을 내재화하세요.

나쁜 예

private List<LottoNumber> numbers;

좋은 예

public class LottoNumbers {
	private final List<LottoNumber> numbers;
   	
    public LottoNumbers(List<LottoNumber> numbers) {
    	// numbers에 대한 검증을 담당하는 method
    	validate(numbers);
        this.numbers = new ArrayList<>(numbers);
    }
}

이제 "로또 숫자는 6개여야 한다"와 같은 규칙을 객체가 스스로 보장할 수 있습니다.

5. 3개 이상의 인스턴스 변수를 가진 클래스를 구현하지 않았는가?

의미

인스턴스 변수가 많으면 클래스가 너무 많은 책임을 진다는 뜻.
SRP(단일 책임 원칙) 위반 가능성이 큽니다.

나쁜 예

public class Car {
	private String name;
    private int speed;
    private int fuel;
    private int tirePressure;
}

좋은 예

public class Car {
	private Name name;
    private Engine engine;
    private Tire tire;
}

속성끼리 묶어 클래스로 분리하면 책임이 명확해집니다.

6. getter / setter 없이 구현했는가?

의미

getter / setter 는 내부 상태를 노출시켜 객체지향의 캡슐화 원칙을 깨드립니다.
객체는 데이터를 주고받는 것이 아니라, 행동을 수행해야 합니다.

나쁜 예

int speed = car.getSpeed();
car.setSpeed(speed + 10);

좋은 예

car.accelerate();

public class Car {
	private int speed;
    
    public void accelerate() {
    	this.speed += 10;
    }
}

데이터를 밖으로 꺼내지 말고, 행동을 메시지로 요청하세요.
단, DTO(데이터 전달용 객체)는 예외적으로 getter 허용.

7. 메소드의 인자 수를 제한했는가?

의미

인자가 많을수록 메서드의 역할이 불명확해지고, 테스트가 어려워집니다.
3개 이상이면 DTO나 VO로 묶는 것을 고려하세요.

나쁜 예

public void createUser(String name, int age, String email, String phone) { ... }

좋은 예

public void createUser(UserInfo userInfo) { ... }

인자를 묶으면 메서드 시그니처가 단순해지고 확장도 쉬워집니다.

8. 코드 한 줄에 점(.)을 하나만 허용했는가?

의미

디미터 법칙(Law of Demeter): "친구의 친구에게 말하지 말라."
객체 간 결합도를 낮추는 원칙이에요.

나쁜 예

user.getAddress().getCity().getName();

좋은 예

user.getCityName();

내부 구조를 숨기고, 외부엔 단순한 인터페이스만 제공하세요.

9. 메서드가 한 가지 일만 담당하도록 구현했는가?

의미

하나의 메서드는 단일 책임(SRP)을 가져야 합니다.
여러 일을 하면 테스트하기 어렵고 이름도 애매해집니다.

나쁜 예

public void registerUser(String name, int age) {
	validate(name, age);
    saveUser(name, age);
    sendEmail(name);
}

좋은 예

public void registerUser(String name, int age) {
	User user = createUser(name, age);
    saveUser(user);
    notifyRegistration(user);
}

역할 단위를 분리하면 테스트, 확장, 수정이 쉬워집니다.

10. 클래스를 작게 유지하기 위해 노력했는가?

의미

클래스가 커지면 하나의 개념에 너무 많은 책임을 넣게 됩니다.
"작은 단위로 나누면 응집도 높고 변경에 강한 코드"가 됩니다.

가이드 라인

  • 클래스 길이 200줄 이하 유지
  • 하나의 개념(도메인 단위)에만 책임 부여
  • 서비스 클래스가 비대해지면 도메인으로 위임

💬 요약

원칙핵심 포인트
1들여쓰기는 한 단계까지만
2else 대신 early return
3원시값 포장 (검증 책임 내재화)
4일급 컬렉션으로 규칙 내재화
5인스턴스 변수는 최대 3개
6getter/setter 대신 행동
7인자 수는 3개 이하
8점(.)은 한 줄에 하나
9메서드는 한 가지 일만
10클래스는 작고 응집도 있게
profile
나는야 개발자

2개의 댓글

comment-user-thumbnail
2025년 10월 28일

깔끔한 정리 감사합니다! 객체지향적 사고와 유지보수성을 높이는 데 핵심이 되는 원칙들이네요. 특히 들여쓰기 제한, 원시값 포장, 일급 컬렉션 적용은 실무에서도 큰 차이를 만듭니다. 프리코스 시작 전에 꼭 숙지해야 할 내용입니다. aessuccess

1개의 답글