책 및 세미나
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)
- 확장에 개방적이고 수정에 폐쇄적이어야 한다는 원칙이다.
- 이상적인 시스템이라면 새 기능을 추가할 때 시스템을 확장할 뿐 기존 코드를 변경하지는 않는다.
창발성
- 창발적 설계로 깔끔한 코드를 구현하자
- 모든 테스트를 실행한다.
- 중복을 없앤다.
- 프로그래머 의도를 표현한다.
- 클래스와 메서드 수를 최소로 줄인다.