책 및 세미나

CleanCode 책 정리

쓱은감자 2024. 1. 1. 14:57

https://www.yes24.com/Product/Goods/11681152

깨끗한 코드란?

  • 비야네 스트롭스트룹(C++창시자)
    • 보기에 즐거운 코드
  • 그래디 부치
    • 추측이 아니라 사실에 기반한 코드
  • '큰' 데이브 토마스(OTI 창립자)
    • 가독성이 좋고 다른 사람이 고치기 쉬운 코드
  • 마이클 페더스
    • 세세한 사항까지 꼼꼼하게 신경 쓴 주의를 기울인 코드
  • 론 제프리스
    • 중복을 피하라, 한 기능만 수행하라, 제대로 표현하라, 작게 추상화하라
  • 워드 커닝햄
    • 읽으면서 짐작한 그대로 돌아가는 코드
    • 프로그램을 단순하게 보이도록 만드는게 아닌 언어를 단순하게 보이도록 만든 코드

보이스카우트 규칙

  • 잘 짠 코드가 전부가 아니다.
  • 시간이 지나도 언제나 깨끗하게 유지해야 한다.
  • 체크아웃할 때보다 좀 더 깨끗한 코드를 체크인한다면 코드는 절대 나빠지지 않는다.

의미있는 이름

  • 의도를 분명히 밝혀라
    • 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.
  • 그릇된 정보를 피하라
    • 직각삼각형의 빗변(hypotenuse)를 구현할 때 hp가 훌륭한 약어처럼 보일지라도 hp라는 변수는 독자에게 그릇된 정보를 제공한다.
  • 의미있게 구분하라
    • zork라는 변수가 있다는 이유만으로 theZork라는 이름을 지어서는 안된다.
    • 읽는 사람이 차이를 알도록 이름을 지어라.
  • 발음하기 쉬운 이름을 사용하라
    • 문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다.
    • 긴 이름이 짧은 이름보다 좋다. 검색하기 쉬운 이름이 상수보다 좋다.
  • 인코딩을 피하라
    • 헝가리식 표기법
      • IDE에서 다 제공한다. 굳이 넣지 않아도 된다.
    • 멤버 변수 접두어
      • IDE에서 다 제공한다. 굳이 넣지 않아도 된다.
    • 인터페이스 클래스와 구현 클래스
      • 구현 클래스에 인코딩을 적용해 Impl을 넣는다.
  • 자신의 기억력을 자랑하지 마라
    • 전문가 프로그래머는 명료함이 최고라는 사실을 이해한다.
    • r이라는 변수가 호스트와 프로토콜을 제외한 소문자 URL이라는 사실을 언제나 기억한다면 확실히 똑똑한 사람이다.
  • 클래스 이름
    • 클래스 이름과 객체 이름은 명사나 명사구가 적합하다.
  • 메서드 이름
    • 메서드 이름은 동사나 동사구가 적합하다.
  • 한 개념에 한 단어를 사용하라
    • 똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다.
    • 메서드 이름은 독자적이고 일관적이어야 한다.
  • 말장난을 하지마라
  • 해법 영역에서 가져온 이름을 사용해라
    • 코드를 읽을 사람도 프로그래머라는 사실을 명심한다.
    • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다.
  • 의미 있는 맥락을 추가하라
    • firstName, lastName, street라는 변수를 가지면 주소라는 사실을 알 수 있다.
    • 하지만 street하나만 있다면 불분명하기 때문에 addrStreat로 명명한다.
  • 불필요한 맥락을 없애라
    • 고급 휘발유 충전소(Gas Station Deluxe)라는 앱을 짜는데 모든 클래스 이름을 GSD로 시작하는 생각은 바람직하지 못하다.
    • IDE에서 G를 입력하고 자동 완성 키를 누르면 IDE는 모든 클래스를 열거한다. IDE를 방해할 이유는 없다.

함수

  • 작게 만들어라!
    • 함수를 만드는 첫째 규칙은 '작게!'다. 함수를 만드는 둘째 규칙은 '더 작게!'다.
    • 블록과 들여쓰기
      • if문/else문/while문 등에 들어가는 블록은 한 줄이어야 한다.
      • 중첩 구조가 생길만큼 함수가 커져서는 안된다는 뜻이다.
  • 한 가지만 해라!
    • 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
    • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
  • 함수 당 추상화 수준은 하나로!
    • 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
    • 코드는 위에서 아래로 읽혀야 좋다. 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 단계씩 낮아진다.
  • Switch 문
    • 다형성을 이용해 각 switch문을 저차원 클래스에 숨기고 반복하지 않게 해야한다.
  • 서술적인 이름을 사용하라!
    • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
    • 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
  • 함수 인수
    • 함수에서 이상적인 인수 개수는 0개다. 다음은 1개, 다음은 2개이다.
    • 3개는 가능한 피하는 편이 좋다. 4개 이상은 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안된다.
    • 플래그 인수는 추하다. 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표하는 셈이니까!
  • 명령과 조회를 분리하라!
    • 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.
  • 오류 코드보다 예외를 사용하라!
    • 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.
  • 반복하지 마라!
    • 중복은 문제다!! 코드길이가 늘어날 뿐 아니라 알고리즘이 변하면 네 곳이나 손봐야 한다.

주석

  • 사실상 주석은 기껏해야 필요악이다.
  • 우리에게 프로그래밍 언어를 치밀하게 사용해 의도를 표현할 능력이 있다면, 주석은 거의 필요하지 않으리라.
  • 코드를 깔끔하게 정리하고 표현력을 강화하는 방향으로, 그래서 애체오 주석이 필요없는 방향으로 에너지를 쏟겠다.

형식 맞추기

  • 형식을 맞추는 목적
    • 맨 처음 잡아놓은 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다.
  • 적절한 행 길이를 유지하라
    • 500줄을 넘지 않고 대부분 200줄 정도인 파일로도 커다란 시스템을 구축할 수 있다
  • 신문 기사처럼 작성하라
    • 이름은 간단하면서도 설명이 가능하게 짓는다.
  • 개념은 빈 행으로 분리하라
    • 빈 행은 새로운 개념을 시작한다는 시각적 단서다.
  • 세로 밀집도
    • 세로 밀집도는 연관성을 의미한다.
    • 서로 밀접한 코드 행은 세로로 가까이 놓여야 한다는 뜻이다.
  • 가로 형식 맞추기
    • 프로그래머는 명백하게 짧은 행을 선호한다.
    • 개인적으로는 120자 정도로 행 길이를 제한한다.

객체와 자료 구조

  • 자료 추상화
    • 구현을 감추려면 추상화가 필요하다!
    • 조회 함수와 설정 함수로 변수를 다룬다고 클래스가 되지는 않는다!
    • 추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다.
  • 자료/객체 비대칭
    • 객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개한다.
    • 자료 구조는 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다.
    • 객체와 자료 구조는 근본적으로 양분된다.
      • 절차적인 코드는 기존 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다. 반면, 객체 지향 코드는 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다.
      • 절차적인 코드는 새로운 자료 구조를 추가하기 어렵다. 그러려면 모든 함수를 고쳐야 한다. 객체 지향 코드는 새로운 함수를 추가하기 어렵다. 그러려면 모든 클래스를 고쳐야 한다.
  • 디미터 법칙
    • 모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다는 법칙이다.
    • 객체는 조회 함수로 내부 구조를 공개하면 안된다.
  • 자료 전달 객체
    • 자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스다.
    • 이런 자료 구조체를 때로는 자료 전달 객체(DTO)라 한다.
  • 결론
    • 시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다.
    • 새로운 동작을 추가하는 유연성이 필요하면 자료 구조와 절차적인 코드가 더 적합하다.

오류 처리

  • 오류 코드보다 예외를 사용하라
  • Try-Catch-Finally 문부터 작성하라
  • 미확인 예외를 사용하라
  • 예외에 의미를 제공하라
    • 실패한 연산 이름과 실패 유형을 언급한다.
    • catch 블록에서 오류를 기록하도록 충분한 정보를 로깅에 넘겨준다.
  • 호출자를 고려해 예외 클래스를 정의하라
  • 정상 흐름을 정의하라
  • null을 반환하지 마라
  • null을 전달하지 마라

단위 테스트

  • TDD 법칙 세가지
    • 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다.
    • 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
    • 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
  • 깨끗한 테스트 코드 유지하기
    • 테스트코드는 실제 코드 못지 않게 중요하다.
    • 테스트는 유연성, 유지보수성, 재사용성을 제공한다.
      • 코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목이 바로 단위 테스트다.
  • 깨끗한 테스트 코드
    • 깨끗한 테스트 코드를 만들 때 가장 중요한 것은 가독성이다.
  • 테스트 당 assert 하나
    • JUnit으로 테스트 코드를 짤 때는 함수마다 assert문을 단 하나만 사용해야 한다고 주장하는 학파가 있다.
  • 테스트 당 개념 하나
  • F.I.R.S.T
    • 빠르게(Fast) : 테스트는 빨라야한다. 테스트는 빨리 돌아야 한다.
    • 독립적으로(Independent) : 각 테스트는 서로 의존하면 안된다. 한 테스트가 다음 테스트가 실행될 환경을 준비해서는 안된다.
    • 반복가능하게(Repeatable) : 테스트는 어떤 환경에서도 반복 가능해야 한다. 실제 환경, QA 환경, 집으로 길, 네트워크가 안되는 환경에서도 실행할 수 있어야 한다.
    • 자가검증하는(Self-Validating) : 테스트는 부울 값으로 결과를 내야한다. 성공 아니면 실패다.
    • 적시에(Timely) : 테스트는 적시에 작성해야 한다. 단위 테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다.

클래스

  • 클래스 체계
    • 정적 공개 상수 - 정적 비공개 변수 - 비공개 인스턴스 변수 - 공개 함수 - 비공개 함수는 자신을 호출하는 공개 함수 직후
  • 클래스는 작아야 한다!
    • 클래스를 만들 때 첫 번째 규칙은 크기다. 클래스는 작아야 한다.
    • 클래스 설명은 만일, 그리고, ~(하)며, 하지만을 사용하지 않고서 25단어 내외로 설명이 가능해야 한다.
  • 단일 책임 원칙(Single Responsibility Principle)
    • 클래스나 모듈은 변경할 이유가 하나, 단 하나뿐이어야 한다는 원칙이다.
  • 응집도
    • 클래스는 인스턴스 변수 수가 작아야 한다.
  • OCP(Open-Closed Principle)
    • 확장에 개방적이고 수정에 폐쇄적이어야 한다는 원칙이다.
    • 이상적인 시스템이라면 새 기능을 추가할 때 시스템을 확장할 뿐 기존 코드를 변경하지는 않는다.

창발성

  • 창발적 설계로 깔끔한 코드를 구현하자
    • 모든 테스트를 실행한다.
    • 중복을 없앤다.
    • 프로그래머 의도를 표현한다.
    • 클래스와 메서드 수를 최소로 줄인다.