Skip to main content

16장 SerialDate 리팩터링

1. 📌 핵심 개념 정리

✅ 요약하기

1. 첫째, 돌려보자
  • SerialDateTest 또는 SerialDateTests라는 클래스는 단위 테스트 케이스를 몇 개 포함하며, 실행 시 실패하는 케이스는 없지만 모든 경우를 점검하지 않는다는 사실을 알 수 있습니다.
  • 저자는 SerialDateTest.java에 있는 모든 테스트 케이스가 통과할 수 있게 만들었습니다.
  • Clover라는 Code Coverage 분석 도구를 이용하여 실행 코드와 실행하지 않는 코드를 확인한 결과, SerialDateTests는 **50%**의 경우만 테스트함을 확인할 수 있었습니다. 구체적으로는 185개 중 **50%**만 실행하고 있었습니다. MonthCodeToQuarter 메서드는 전혀 호출되지 않았습니다.
  • 클래스를 철저히 이해하고 리팩터링하려면 훨씬 높은 테스트 커버리지가 필요했습니다.

2. 둘째, 고쳐보자
  • 쓸모없는 주석은 제거합니다.
    변경 이력, 작성자 등은 Git 등의 소스 코드 관리 도구로 대체할 수 있으므로 삭제합니다. 특히 변경 이력 주석은 1960년대 방식으로, 현재는 불필요합니다.
  • 법적 정보는 유지합니다.
    라이선스 및 저작권 관련 주석은 반드시 보존합니다.
  • 좋은 주석은 유지합니다.
    변수의 역사와 의미를 설명하는 주석은 코드 이해에 도움이 되므로 유지합니다.
  • Javadoc에는 하나의 언어만 사용합니다.
    자바, 영어, HTML 등 여러 언어가 혼용되면 오히려 가독성을 해칩니다. 필요 시 <pre> 태그로 통일된 형식을 사용합니다.
  • import java.text.*, import java.util.* 형태로 범용 패키지를 사용해 코드 라인을 줄입니다.
  • 기능을 명확히 표현합니다.
    클래스와 메서드는 이름만 보고도 역할이 파악되도록 작성합니다.
  • **단일 책임 원칙(SRP)**을 지킵니다.
    하나의 메서드는 하나의 역할만 하도록 분리하고, 복잡한 로직은 적절히 나눕니다.
  • 플래그 인수는 제거하고 서술형 이름으로 분리합니다.
    예: getMonths(boolean fullName)getMonthNames() / getMonthShortCodes() 등으로 분리.
  • 불필요한 상속은 제거합니다.
    예: DayDateMonthConstants를 상속하는 것은 적절하지 않으며, 상수는 enum으로 분리합니다.
  • 사용하지 않는 직렬화 제거
    클래스 직렬화 버전이 달라질 경우 문제가 발생하므로, 직렬화를 피하는 것이 안전합니다.
  • 변수 선언 시 final 키워드를 제거합니다.
    실질적인 코드 안정성 향상보다는 복잡성만 높이므로, 전체적으로 제거합니다.
  • 임시 변수와 설명을 통해 알고리즘 가독성을 높입니다.
  • 변수명은 의미가 명확하고 클래스 컨텍스트에 어울리도록 수정합니다.
  • 중복된 if 조건은 || 연산자로 합칩니다.
    불필요한 중복을 줄이고 가독성을 높입니다.
  • 정확한 의미를 반영한 이름으로 변경합니다.
    예: SerialDateDayDate.
    Serial은 제품 일련번호와 관련된 용어이므로 날짜를 표현하기에 적절하지 않습니다.
  • 중복 메서드는 통합하여 의미를 명확히 합니다.
    예: getMonths가 두 개 존재할 경우, 역할에 따라 메서드를 구분하거나 하나로 통합합니다.
  • 변수가 특정 클래스에서만 사용된다면 추상 클래스가 아닌 해당 클래스에 위치시킵니다.
    변수와 사용 코드 간 거리를 줄이면 가독성이 높아집니다.
  • 구현 정보를 추상 클래스에 담지 않습니다.
    자식 클래스와의 결합도를 낮추기 위해 ABSTRACT FACTORY 패턴을 적용합니다.
  • 정적 메서드는 추상 메서드로 위임하고, SINGLETON, DECORATOR, FACTORY 패턴과 조합하여 구현합니다.
    부모 클래스는 자식 클래스를 몰라도 동작해야 합니다.

필요하다면 이 원칙들을 바탕으로 리팩토링 전후 코드 예시도 도와드릴 수 있어요. 특정 클래스나 메서드가 있다면 알려주세요!


3. 결론
  • 보이스카우트 규칙을 따랐습니다. 체크아웃한 코드보다 좀 더 깨끗한 코드를 체크인할 수 있게 되었습니다.
  • 테스트 커버리지 증가, 버그 사항 개선 또는 수정, 코드 크기 감소, 코드가 명확해짐, 코드의 가독성 증가.
  • DayDate 코드 커버리지가 감소했지만, 이는 테스트하는 코드가 줄어서가 아니라 클래스 크기가 작아지는 바람에 테스트하지 않는 코드의 비중이 커졌기 때문입니다.
  • 다음 사람은 좀 더 코드를 쉽게 이해할 수 있게 되었습니다.
  • 다음의 코드 개선은 난이도가 조금 더 낮아질 것입니다.

2. 🤔 이해가 어려운 부분

🔍 질문하기

1. 커버리지 도구
  • 어려웠던 부분: Clover 같은 도구가 어떤 점에서 도움이 되고, 어디까지 신뢰할 수 있는가?
  • 알게된 점:
    • [Clover 와 같은 코드 커버리지 도구의 장점]
      • 테스트 사각지대 시각화: 테스트되지 않은 코드가 어디인지 하이라이트 색상 등으로 시각적으로 표시되어, 어떤 분기(if), 루프(for), 예외 처리 블록 등이 실행되지 않았는지를 바로 확인할 수 있어 테스트 보완에 유용합니다.
      • 커버리지 수치로 측정 가능: 코드 전체의 문장(Statement), 분기(Branch), 메서드, 클래스 커버리지를 %로 제공합니다.
      • 리팩토링하거나 기능 추가 시 테스트 범위 감소 여부 감지 용이.
      • 자동화된 리포트 생성: HTML, XML, PDF 등의 리포트를 제공해 CI/CD 파이프라인에 통합하거나 팀원들과 공유하기 좋습니다.
      • 테스트 품질 개선의 출발점: 어디에 테스트가 부족한지 알려주는 출발점 역할을 하며, 커버리지를 높이는 과정에서 예외 상황이나 엣지 케이스를 놓치지 않도록 도와줍니다.
    • [Clover를 무조건 신뢰하면 안 되는 이유]
      • "높은 커버리지 ≠ 좋은 테스트": 커버리지가 100%여도, assert 문 없이 메서드만 호출해도 수치는 올라갑니다. 로직 검증이 빠져있는 형식적인 테스트는 실제 버그를 걸러내지 못합니다.
      • 복잡한 로직 커버리지는 간과될 수 있음: 하나의 if 문이 여러 조건을 포함할 경우, 모든 조건 조합을 검사하지 않아도 커버리지가 올라갈 수 있습니다. 분기 커버리지(Branch Coverage)가 없거나 약한 경우 놓치는 부분이 생깁니다.
      • 리팩토링 과정에서 거짓 경고: 동일한 로직 구조이지만 코드 위치나 표현 방식이 바뀌면 새로운 코드처럼 인식되어 커버리지가 낮아질 수 있습니다.
    • Clover는 “어디를 테스트해야 할지”를 알려주는 유용한 도구이지만, “테스트가 충분한지”는 사람이 판단해야 합니다.
    • 커버리지는 테스트의 양적 지표이고, 품질은 정성적인 코드 리뷰와 시나리오 설계로 보완되어야 합니다.
    • Clover는 테스트 보완의 출발점, 품질 진단의 도구, 리팩토링 후 퇴행 방지의 방패막 역할을 합니다.

2. 커버리지
  • 어려웠던 부분: 코드 커버리지와 테스트 커버리지의 차이
  • 궁금한 점: 각 커버리지의 의미와 수치가 높아야 좋은 것인가?
  • 알게된 점:
    • 코드 커버리지: 코드의 어느 부분이 테스트 실행 중 실제로 실행되었는지를 측정합니다.
    • 테스트 커버리지: 전체 요구사항 또는 테스트 시나리오 중 얼마나 많은 테스트가 수행되었는지 측정합니다.
    • 둘은 함께 쓰여야 좋은 품질을 보장 가능합니다. 코드 커버리지가 높아도, 잘못된 시나리오만 테스트했다면 의미 없습니다. 테스트 커버리지가 높아도, 코드가 실제로 실행되지 않았다면 버그 발견 못합니다.

3. 둘째, 고쳐보자
  • 어려웠던 부분: 저자는 테스트 커버리지를 높이기 위해서 주석 처리된 테스트 코드까지 모두 통과하도록 코드를 변경했는데 이를 이해하기가 어려웠습니다.
  • 궁금한 점: 주석 처리된 테스트 코드 중에는 좋지 않은 예시도 포함되어 있는데 그렇다면 왜 그런 테스트 코드를 수정하지 않고 그대로 뒀을까?

4. final 키워드
  • 어려웠던 부분: final 키워드를 로버트 시몬스는 코드 전체에 사용하라고 강력히 권장하지만, 책의 저자는 모두 없앴다.
  • 궁금한 점: 어떤 기준인 것인지 이해가 가지 않았습니다.

3. 📚 참고 사항

📢 논의하기

1. 관련 자료 공유