Hayden's Archive

클린코드(Clean Code) 9, 10장 - 로버트 C. 마틴 본문

Book

클린코드(Clean Code) 9, 10장 - 로버트 C. 마틴

_hayden 2021. 6. 7. 19:02

book.naver.com/bookdb/book_detail.nhn?bid=7390287

 

Clean Code

『CLEAN CODE(클린 코드)』은 오브젝트 멘토(OBJECT MENTOR)의 동료들과 힘을 모아 ‘개발하며’ 클린 코드를 만드는 최상의 애자일 기법을 소개하고 있다. 소프트웨어 장인 정신의 가치를 심어 주며

book.naver.com

 


9장 단위테스트

  • TDD 법칙 세 가지
    • 첫째, 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다
    • 둘째, 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다
    • 셋째, 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다
    • 위 세 가지 규칙을 따르면 개발과 테스트가 대략 30초 주기로 묶임
      • 테스트 코드와 실제 코드가 함께 나오면서 테스트 코드가 실제 코드보다 불과 몇초 전에 나옴
  • 깨끗한 테스트 코드 유지하기
    • 실제 코드가 진화하면 테스트 코드도 변해야 함
    • 새 버전을 출시할 때마다 팀이 테이스 케이스를 유지하고 보수하는 비용도 늘어남
      • 하지만 테스트 슈트가 없으면 개발자는 자신이 수정한 코드가 제대로 도는지 확인할 방법이 없고 결함율이 높아짐
    • 테스트 코드는 실제 코드 못지 않게 중요하다
      • 따라서 실제 코드 못지 않게 깨끗해야 한다
    • 테스트는 유연성, 유지보수성, 재사용성을 제공한다
      • 단위 테스트는 코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목
      • 테스트 케이스가 없다면 모든 변경이 잠정적인 버그임
        • 테스트 커버리지가 높을수록 변경이 쉬워짐
  • 깨끗한 테스트 코드
    • 깨끗한 테스트를 만들기 위해 필요한 것 : 가독성
      • 가독성은 실제 코드보다 테스트 코드에 더더욱 중요
      • 가독성을 높이기 위해 명료성, 단순성, 풍부한 표현력 필요
        • 최소의 표현으로 많은 것을 나타내야 함
      • BUILD-OPERATE-CHECK 패턴
        • BUILD : 테스트 자료를 만든다
        • OPERATE : 테스트 자료를 조작한다
        • CHECK : 조작한 결과가 올바른지 확인한다
      • 테스트 코드는 잡다하고 세세한 코드는 거의 다 없애고, 본론에 도입해 진짜 필요한 자료 유형과 함수만 사용함
        • 코드를 읽는 사람은 코드가 수행하는 기능을 재빨리 이해함
    • 도메인에 특화된 테스트 언어
      • 도메인 특화 언어(DSL, Domain Specific Language)로 테스트 코드를 구현함
        • 흔히 쓰는 시스템 조작 API를 사용하는 대신 API 위에다 함수와 유틸리티를 구현한 후 그 함수와 유틸리티를 사용
        • 이렇게 구현한 함수와 유틸리티는 테스트 코드에서 사용하는 특수 API가 됨
    • 이중 표준
      • 테스트 API 코드에 적용하는 표준은 실제 코드에 적용하는 표준과 다름
        • 단순하고 간결하고 표현력이 풍부해야 하지만 실제 코드만큼 효율적일 필요는 없음
        • 실제 환경이 아니라 테스트 환경에서 돌아가는 코드이기 때문
      • 실제 환경에서는 절대 안 되지만 테스트 환경에서는 전혀 문제없는 방식이 있음
        • 코드의 깨끗함과는 철저히 무관한, 메모리나 CPU 효율과 관련 있는 경우
  • 테스트당 assert 하나
    • assert 문이 단 하나인 함수는 결론이 하나라서 코드를 이해하기 쉽고 빠름
    • 하나의 테스트에서 하나의 assert 문을 사용하기 위해 테스트를 쪼개는 과정에서 given-when-then이라는 관례 사용
      • 테스트 코드를 읽기 쉬워지지만 given-when에서 중복되는 코드가 많아지게 됨
      • 해결법 1) TEMPLATE METHOD 패턴을 사용해서 중복 제거(given/when 부분을 부모 클래스에 두고 then 부분을 자식 클래스에 둔다)
      • 해결법 2) 완전히 독자적인 테스트 클래스를 만들어 @Before 함수에 given/when 부분을 넣고 @Test 함수에 then 부분을 넣음
      • 하지만 이러한 해결법은 배보다 배꼽이 커서 이럴 경우에는 assert 문을 여럿 사용하는 편이 나을 수도 있음
    • 하지만 assert 문의 개수는 최대한 줄이는 게 좋음
    • 테스트당 개념 하나
      • 테스트 함수는 개념 하나만 테스트한다
      • 이것저것 잡다한 개념을 연속으로 테스트하는 긴 함수는 피함
  • F.I.R.S.T.
    • Fast(빠르게)
      • 테스트는 빠르게 자주 돌아야 함
    • Independent(독립적으로)
      • 각 테스트는 서로 의존하면 안 됨 (한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안됨)
      • 각 테스트는 독립적으로 그리고 어떤 순서로 실행해도 괜찮아야 함
      • 그래야 원인을 진단하기 쉽고, 결함을 찾기 편함
    • Repeatable(반복가능하게)
      • 테스트는 어떤 환경에서도 반복 가능해야 함
        • 실제 환경, QA 환경, 네트워크가 연결되지 않은 노트북 환경 등
    • Self-Validating(자가검증하는)
      • 테스트는 부울 값으로 결과를 내야 함(성공 아니면 실패)
      • 통과 여부를 알기 위해 로그 파일을 읽게 만들거나 텍스트 파일 두 개를 수작업으로 비교하게 만들어서는 안 됨
      • 테스트가 스스로 성공과 실패를 가늠해야 함
    • Timely(적시에)
      • 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현함
        • 그렇지 않으면, 어떤 실제 코드는 테스트하기 너무 어렵다고 판명날 수 있고, 테스트가 불가능하도록 실제 코드를 설계하게 될 수도 있음

 


10장 클래스

  • 클래스 체계
    • 변수 목록
      • 정적static 공개public 상수가 있다면 맨 처음에 나옴
      • 다음으로 정적 비공개private 변수가 나옴
      • 이어서 비공개 인스턴스 변수가 나옴
      • (공개 변수가 필요한 경우는 거의 없음)
    • 변수 목록 다음에는 공개 함수가 나오고, 비공개 함수는 자신을 호출하는 공개 함수 직후에 넣음
    • 추상화 단계가 순차적으로 내려가면서, 프로그램은 신문 기사처럼 읽힘
    • 캡슐화
      • 변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫다
      • 캡슐화를 풀어주는 결정은 언제나 최후의 수단
      • 때로는 같은 패키지 안에서 테스트 코드가 함수를 호출하거나 변수를 사용해야 한다면, 그 함수나 변수를 protected로 선언하거나 패키지 전체로 공개하기도 함
  • 클래스는 작아야 한다!
    • 함수는 물리적인 행 수로 크기를 측정했다면, 클래스는 클래스가 맡은 책임을 센다
    • 클래스 이름은 해당 클래스 책임을 기술해야 함
      • 작명은 클래스 크기를 줄이는 첫 번째 관문
      • 클래스 이름이 모호하다면(예: Processor, Manager, Super) 클래스 책임이 너무 많은 것
      • 클래스 설명은 만일(if), 그리고(and), 하며(or), 하지만(but)을 사용하지 않고서 25단어 내외로 가능해야 함
    • 단일 책임 원칙(SRP, Single Responsibility Principle)
      • 클래스나 모듈을 변경할 이유(책임)가 단 하나뿐이어야 한다는 원칙
      • 만능 클래스를 단일 책임 클래스 여럿으로 분리해야 함
      • 큰 클래스 몇 개가 아니라 작은 클래스 여럿으로 이뤄진 시스템이 더 바람직하다
    • 응집도
      • 클래스는 인스턴스 변수 수가 작아야 함
      • 각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 함
        • 일반적으로 메서드가 변수를 더 많이 사용할수록 메서드와 클래스는 응집도가 더 높다
        • 모든 인스턴스 변수를 메서드마다 사용하는 클래스는 응집도가 가장 높다
      • 응집도가 높다 = 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다
      • '함수를 작게, 매개변수 목록을 짧게' 전략을 따르다 보면 때때로 몇몇 메서드만이 사용하는 인스턴스 변수가 아주 많아짐
        • 새로운 클래스로 쪼개야 한다는 신호
    • 응집도를 유지하면 작은 클래스 여럿이 나온다
      • 큰 함수를 작은 함수 여럿으로 쪼개는 과정 예시
        • 빼내려는 코드가 큰 함수에 정의된 변수 넷을 사용한다
        • 변수 네 개를 새 함수에 인수로 옮기지 말고, 대신 네 변수를 클래스 인스턴스 변수로 승격한다
        • 그런데 이렇게 하면 몇몇 함수만 사용하는 인스턴스 변수가 점점 늘어나서 클래스가 응집력을 잃는다
        • 그러므로 몇몇 함수가 몇몇 변수만 사용한다면 독자적인 클래스로 분리한다
  • 변경하기 쉬운 클래스
    • 새 기능을 수정하거나 기존 기능을 변경할 때 건드릴 코드가 최소인 시스템 구조가 바람직함
    • 변경으로부터 격리
      • 인터페이스와 추상 클래스를 사용해 구현이 미치는 영향을 격리
      • 상세한 구현에 의존하는 코드는 테스트가 어려움
      • 테스트가 가능할 정도로 시스템의 결합도를 낮추면 유연성과 재사용성도 더욱 높아짐
        • 결합도가 낮다 = 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어 있다
      • 결합도를 최소로 줄이면 자연스럽게 DIP(Dependency Inversion Principle)를 따르는 클래스가 나옴
        • 본질적으로 DIP는 클래스가 상세한 구현이 아니라 추상화에 의존해야 한다는 원칙