

Readable Code: 읽기 좋은 코드를 작성하는 사고법
Practical Testing: 실용적인 테스트 가이드
강의와 함께한 인프런 워밍업 클럽 스터디 3기 – 백엔드 클린 코드, 테스트 코드 (Java, Spring Boot)
2주차 회고 입니다.
학습 내용 요약
이번 주는 클린 코드 작성법에 이어 코드 구조와 리팩토링, 그리고 테스트 코드 작성법을 본격적으로 학습했습니다.
1. 주석의 양면성
- 주석은 코드 자체로 표현하기 어려운 의사결정의 히스토리를 기록할 때 사용해야 합니다.
- 자주 변하는 정보는 주석으로 표현하면 안 되고, 주석은 코드와 함께 꾸준히 관리해야 합니다.
- 부정확한 주석은 주석이 없는 코드보다 더 해롭습니다.
2. 변수와 메서드의 나열 순서
- 변수는 사용하는 순서대로 나열하고, 공개 메서드를 먼저 배치하여 외부 세계에 제공하는 기능을 명확히 드러냅니다.
- 비공개 메서드는 공개 메서드에서 호출되는 순서로 나열하여 코드 읽기 흐름을 자연스럽게 만듭니다.
- 나열 순서도 의도를 표현하는 중요한 수단이 될 수 있음을 배웠습니다.
3. 패키지 나누기
- 패키지 구조는 도메인 문맥을 반영하여 설계하며, 너무 잘게 나누거나 너무 크게 뭉치는 것을 지양해야 합니다.
- 팀 내 합의가 중요하며 초기 설계 단계에서 패키지 구조를 충분히 고민해야 합니다.
4. 적정 기술의 사용
- 모든 기술은 적정한 수준과 시점에서 활용해야 하며, 무리한 추상화나 오버 엔지니어링은 오히려 개발 효율성을 떨어뜨립니다.
- 클린 코드는 만능 해결책이 아니며, 상황과 목적에 맞는 균형 잡힌 접근이 중요합니다.
5. 테스트 코드와 TDD
- 자동화된 단위 테스트(JUnit5, AssertJ)를 통해 신뢰성과 유지보수성을 높이고, 빠른 피드백을 받을 수 있습니다.
- TDD(Test Driven Development)를 통한 선(先) 테스트 작성, 후(後) 기능 구현 방식을 이해했습니다.
- Given-When-Then 구조를 이용한 BDD(Behavior Driven Development) 스타일로 작성하면 도메인 로직을 명확히 표현할 수 있습니다.
미션 해결 과정
🎯 스터디카페 이용권 선택 시스템 리팩토링 미션
미션
‘스터디 카페 이용권 선택 시스템’의 기존 시스템에 중복된 로직과 혼재된 책임을 명확히 분리하고, 객체지향 원칙을 준수하여 코드 구조를 개선하는 리팩토링 미션입니다.
접근 관점
- 추상화 레벨 맞추기: 중복 로직을 별도 메서드로 추출했습니다.
- 객체의 책임 분리: StudyCafeOrder 객체를 추가해 할인 계산 로직을 전담했습니다.
- DIP(의존성 역전 원칙): 파일 접근 로직을 인터페이스(StudyCafeRepository)로 추상화하여 확장성을 확보했습니다.
해결 과정
1. 기존의 복잡한 분기 조건(if-else)을 메서드로 깔끔하게 정리했습니다.
- Before
- if (HOURLY) { … } else if (WEEKLY) { … } else if (FIXED) { … } 로직이 길고, 사물함 로직도 그 안에서 처리.
- After
public void run() {
// 1) passType 선택 (시간권/주단위/고정석)
StudyCafePassType passType = inputHandler.getPassTypeSelectingUserAction();
// 2) pass 선택
StudyCafePass selectedPass = selectPass(passType);
// 3) 사물함 선택 (고정석 only)
StudyCafeLockerPass lockerPass = maybeSelectLocker(selectedPass);
// 4) 주문 객체 생성 & 결과 출력
StudyCafeOrder order = new StudyCafeOrder(selectedPass, lockerPass);
outputHandler.showOrderResult(order);
}
Java- 이점: 각 단계가 메서드로 분리되어 이해하기 쉬움. passType별로 중복된 분기 로직이 크게 줄었음.
2. 할인 및 총금액 계산 로직을 전담하는 객체를 만들어 로직을 명확히 했습니다.
- Before
- 할인금액, 총액 계산 로직이 OutputHandler.showPassOrderSummary 안에 위치.
- After
- StudyCafeOrder라는 도메인 객체가 이 로직을 담당.
public int getDiscountAmount() {
return (int) (selectedPass.getPrice() * selectedPass.getDiscountRate());
}
public int getTotalPrice() {
int discountPrice = getDiscountAmount();
int lockerPrice = (lockerPass != null) ? lockerPass.getPrice() : 0;
return selectedPass.getPrice() - discountPrice + lockerPrice;
}
Java- 이점: 필요 시 “할인 정책”이 바뀌어도 StudyCafeOrder만 수정하면 되며, 출력부는 깔끔하게 유지.
3. 파일 접근 방식을 인터페이스와 구현체로 나누어 유지보수와 확장성을 높였습니다.
- Before
- StudyCafeFileHandler라는 클래스에서 직접 파일 접근 + 변환.
- After: StudyCafeRepository(추상) + StudyCafeFileRepository(구현).
- StudyCafePassMachine은 StudyCafeRepository repository에만 의존 → DIP 준수.
- 향후 DBRepo나 InMemoryRepo 추가 시 StudyCafeRepository만 구현하면 됨.
회고
리팩토링을 통해 코드 가독성과 유지보수성이 눈에 띄게 향상되었습니다. 특히 객체지향 원칙(SRP, DIP)을 철저히 적용한 결과, 각 객체가 단 하나의 명확한 책임을 가지면서, 향후 변경이 발생해도 최소한의 수정만으로 대응 가능해졌습니다. 규칙의 절대성이 아닌 “적정선”을 찾는 것이 매우 중요하다는 점을 깊이 깨달았습니다.
회고
스스로 칭찬하고 싶은 점
- 학습한 내용을 즉시 코드에 적용하며 원칙을 체화하고자 노력한 점
- 단순한 기능적 개선을 넘어, 구조적이고 근본적인 리팩토링을 고민한 점
아쉬웠던 점 & 보완할 점
- 코드 리뷰에서 코드 확장성과 유지보수성을 높이기 위한 설계가 부족했음을 느꼈습니다. 다음에는 코드 확장성과 유지보수성을 고려한 구조를 더욱 더 고민해 보겠습니다.
- 지나치게 추상화에 치우치는 경향을 느꼈고, 다음에는 더 실용적인 균형을 유지하려 합니다.
다음 주 학습 목표
- 다음 주부터는 TDD 기반으로 더욱 실질적이고 구체적인 테스트 코드 작성을 연습할 계획입니다.
- 리팩토링과 테스트가 결합된 실습을 통해, 코드를 더욱 안정적으로 관리하는 방법을 익힐 예정입니다.
이번 주 스터디를 통해 “코드는 언제나 적정한 수준의 추상과 구체 사이에서 균형을 잡아야 한다”는 중요한 원칙을 다시 한번 실감했습니다. 앞으로도 적절한 추상화와 구체적인 구현 사이의 “적정선”을 끊임없이 고민하며, 클린하고 안정적인 코드를 지속적으로 작성하는 개발자가 되도록 노력하겠습니다. 감사합니다!