본문 바로가기

카테고리 없음

[도서] Clean Code 요약

사에서 책을 읽을 기회가 있었다.

물론 도서 자체는 회사에서 구매해 준 것은 아니고.. 집에 모셔놓았던 것인데, 3장까지만 읽다가 1년을 모셔둔 책을 드디어 다시 꺼내어 본 것이다. 지금까지 전공 서적을 정리한 것은 학부생때 이후로 거의 처음이 아닐까 싶다.

기회가 된다면 학부생때 쓰던 칼라펜과 포스트잇의 봉인을 풀고 다시 책을 더럽혀도 좋을 그런 도서 같다.

 

책보다 게임이 더 좋은 내게 꽤 맞춤형(?) 지도 교사가 온 것 같아 한편으로는 즐겁다. (다른 한편은 노코멘트 하겠다)

 


1. 깨끗한 코드

1.1. 나쁜 코드? 좋은 코드?

  • 이 책의 전반적인 내용은 깨끗한 코드 VS 나쁜 코드에 대한 내용
  • 르블랑의 원칙
    • 나중은 결코 오지 않는다 = 나중에 수정해야지~는 없다
    • 물론 14장 점진적인 개선에서는 우선 개발하고 리팩토링 개념을 소개함

1.2. 나쁜 코드가 초래하는 대가

  • 유지보수/업무 파악(화자는 해독이라 표현)에 들어가는 생산성에 문제 → 회사 망함

1.3. 깨끗한 코드 (여기서는 나쁜 코드의 상반되는 단어)

  • 보기에 즐거운, 효율적, 한가지를 잘하는 집중
  • 가독성, 명쾌
  • 테스트다른 사람이 고치기 쉬운
  • 주의 깊게 작성한 코드
  • 중복을 피하라, 한 기능만 수행하라
  • 짐작한대로, 놀랄 일이 없어야 한다

1.4. 보이스카우트 규칙

  • 언제나 처음 왔을때보다 더 깨끗하게
  • 14장 점진적인 개선을 소개

2. 의미 있는 이름

2.1. 의도를 분명히 밝혀라

  • 함축성 → 변수/함수 등 이름을 지을 때는 유의미한 정보를 제공
  • 별도의 주석이 없어도 의도가 드러나도록

2.2. 그릇된 정보를 피하라

  • 약어를 피하라
    • 내가 생각하고 작성한 약어가 다른 개발자도 동일한 약어로 바라볼 것이라는 것은 오산
  • 유사한 개념유사한 표기법을 사용해라
    • 표현에는 일관성을 유지해라

2.3. 의미 있게 구분하라

  • 불용어 쓰지 마라
  • 불용어 → 없어도 큰 의미가 없는 것들
  • ex) moneyAmount VS money, nameString VS name …

2.4. 발음하기 쉬운 이름을 사용하라

  • 쉬운 단어와 이해 가능한 이름을 만들어라

2.5. 검색하기 쉬운 이름을 사용하라

2.6. 인코딩을 피하라

  • IDE 를 활용하자
  • 접두 접미사를 인터페이스나 구현클래스에 활용하는 것은 지양 → 하지만 IShapeFactory 보다는 ShapeFactoryImpl 이 차라리 좋다 (꼭 써야한다면 의미있게~)

2.7. 기억력을 자랑하지 마라

  • 과시하려고 어려운 단어 괜히 꺼내 쓰지 말고 그냥 명료하게 해라

2.8. 클래스 이름

  • 명사/명사구 추천
  • 불용어와 동사 비추천

2.9. 메서드 이름

  • 동사/동사구 추천

2.10. 기발한 이름은 피하라

  • 의도를 분명히하고 농담(장난)은 피해라

2.11. 한 개념에 한 단어를 사용하라

  • 똑같은 기능이면 명칭이 같아야 하지 않냐

2.12. 말장난 하지마라

  • 한 단어를 여러가지 개념으로 재활용하지 마라

2.13. 해법 영역에서 가져온 이름을 사용해라

  • 코드를 읽는 독자도 결국 같은 개발자다 → 너무 어려운 도메인 용어를 사용하기 보다는 그냥 기술용어를 써라
  • ex) Job Queue 에 jobQueue

2.14. 문제 영역에서 가져온 이름을 사용해라

  • 적절한 용어가 없으면 도메인 지식을 물어봐서 지어라 → 결국 수정할 개발자도 도메인 지식은 물어볼거다
  • DSL 부분 참고

2.15. 의미 있는 맥락을 추가하라

  • 접두/접미를 추가해 의미를 좀 더 분명히 하기 -> name 앞에 addr 접두를 붙인 경우
  • 메소드에 의미를 주어 쪼개는 방법도 권장

2.16. 불필요한 맥락은 없애라

  • 불필요한 접두/접미 빼라
  • 짧은 이름이 긴 이름보다는 좋다

3. 함수

3.1. 작게 만들어라

  • 들여쓰기가 너무 많으면 읽기 불편하다
  • 가능한 짧고 단순하게

3.2. 한 가지만 해라!

  • 함수는 한가지만 잘 기능하는 게 좋다

3.3. 함수 당 추상화 수준은 하나로!

  • 내려가기 규칙
    • 함수내에는 같은 수준의 추상화, 깊이 들어갈 수록 보다 구체화된 수준의 추상 수준이 표현될 수 있다
    • 이 부분은 추상 단계에 대한 이해와 고민이 필요해 보임

3.4. Switch 문

  • 길이도 길어지고, 기능을 분기하거나 객체를 생성하는데 사용하면 너무 많은 기능을 부담하게 되어 문제
  • 다형성을 이용해서 숨기기 권장

3.5. 서술적인 이름을 사용하라!

3.6. 함수 인수

  • 입력 인수가 적을 수록 의미는 명확해진다
  • 단항형식
    • 질문형 - existFile(String fileName)
    • 변환형 - fileOpen(String fileName) → InputStream
    • 이벤트 함수는 주의해서 사용해라
  • 플래그 인수 ← 권장하지 않는다
  • 이항함수
    • equals 나 point 같은 누가봐도 두 개가 필요한 것이 아니라면 피하라
  • 인수객체
    • 인수를 개념으로 묶을 수 있다면 묶어보자 → 함수 인수가 줄어든다

3.7. 부수 효과를 일으키지 마라!

  • 시간적인 결합순서 종속성 ← 한 가지 기능에만 충실하자

3.8. 명령과 조회를 분리하라!

3.9. 오류 코드보다 예외를 사용하라!

  • 오류 코드 보다는 try-catch 쓰자
  • try-catch 쓸 거면 좀 한 군데서 써라 ← 안 이쁘다 (핸들러 같은거 만들어 쓰던 메소드로 뽑던 해라)

3.10. 반복하지 마라!

  • 중복되는 코드는 제거해라

3.11. 구조적 프로그래밍

  • 하나의 함수에서 return break continue 는 최대한 적게 써라
  • 함수가 충분히 작다면, return break continue 가 여러번 나와도 좋다

3.12. 함수를 어떻게 짜죠?

  • 글쓰기다 → 초안을 작성하면 인수도 많고 중복도 많을 테지만 조금씩 다듬는 것 어떠냐

4. 주석

4.1. 주석은 나쁜 코드를 보완하지 못한다

  • 코드에 주석을 추가하는 이유는 품질이 나쁘기 때문이다

4.2. 코드로 의도를 표현하라!

4.3. 좋은 주석

  • 법적인 주석 <- 계약/ 저작권 같은 명시
  • 정보 제공 <- 왠만하면 코드로 표현해라지만 도메인 지식이 필요한 경우, 도메인 지식을 제공하면 어떨까?
  • 의미를 명료하게 밝히는 주석 <- 변경하지 못하는 코드를 사용할때, 의미를 설명하면 좋을 수 있다
  • TODO 주석

4.4. 나쁜 주석

  • 주절거리는 주석
    • 불필요한 정보 제공
  • 같은 이야기를 반복하는 주석
  • 오해할 여지가 있는 주석
    • 살짝 잘못된 정보 는 위험하다
  • 의무적으로 다는 주석
    • 불필요한 javadocs 는 문제
  • 이력을 기록하는 주석
    • 형상관리 툴을 써라
  • 있으나 마나한 주석
  • 무서운 잡음
    • 복붙 문제
  • 위치를 표시하는 주석
  • 닫는 괄호에 다는 주석
  • 주석으로 처리한 코드
  • html 주석
    • 특히 안드로이드 자동완성…
  • 전역정보
    • 정보는 근처의 내용만 전달
  • 너무 많은 정보
    • TMI
  • 모호한 관계
    • 주석 자체가 논쟁거리나 설명이 필요한 내용이 되어서는 본말전도
  • 비공개 코드에서 javadocs
    • 때로는 알고리즘이 난해하면 간단한 설명을 적어주는 것도 좋다

5. 형식 맞추기

5.1. 형식을 맞추는 목적

  • 유지보수 용이
  • 확장성

5.2. 적절한 행 길이를 유지하라

  • 신문 기사처럼 작성하라
    • 첫 문단은 내용을 요약한다 → 추상화 수준을 관리
    • 이름만 봐도 무슨 내용을 말하려는지 알 수 있도록 분명히 한다 ← 2장
  • 세로 밀집도
    • 서로 밀접한 코드 행은 붙여 두자
  • 수직 거리
    • 변수 선언
      • 최대한 사용하는 곳 근처에 선언해라
    • 인스턴스 변수
      • 얘는 시작과 동시에 선언해라 ← C++ 가위 규칙
    • 종속 함수
      • 콜리는 콜러 근처에 두자
    • 개념적 유사성
      • 유사한 개념끼리 모아두자

5.3. 가로 형식 맞추기

  • 가로 길이 맞추기
  • 가로 정렬
  • 들여쓰기
  • 가짜 범위
    • ; 만 가볍게 찍은 반복문이나 분기문

5.4. 팀 규칙

6. 객체와 자료 구조

6.1. 자료 추상화

  • private 변수에 무작정 get set 을 추가하지 말자
  • 구현을 모른채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다

6.2. 자료/객체 비대칭

  • 새로운 자료 타입이 필요한 경우 → 클래스와 객체 지향
  • 새로운 함수가 필요한 경우 → 절차적 코드와 자료구조

6.3. 디미터 법칙

  • 모듈은 자신이 조작하는 객체의 속사정을 몰라야만 한다
  • 기차 충돌
    • 체이닝은 조잡해 보이니 왠만하면 피하라
    • 빈즈는 모든 자료구조에 get set 을 강요하기에 예외
  • 잡종 구조
    • 객체면서 자료구조인 이런 구조는 이상하다

6.4. 자료 전달 객체

  • DTO 공개 변수만 있고 함수가 없는, DB 통신이나 소켓 메시지 등을 분석할때 사용하는 객체
  • 활성 레코드
    • DTO에 savefind 같은 탐색을 추가할 수도 있다

7. 오류 처리

7.1. 오류 코드보다 예외를 사용하라

  • 오류 코드로 제어하기 보다 그냥 예외를 터트려라 ← 논리오류와 뒤섞여서 헷갈린다

7.2. Try-catch-finally 문부터 작성하라

  • 어떤 의미에서 try 블록은 트랜잭션과 비슷하다
  • 강제로 예외를 일으키는 TC를 먼저 작성한 후 코드를 작성해보자

7.3. 미확인 예외를 사용하라

7.4. 예외에 의미를 제공하라

  • 정보를 함께 전달하자

7.5. 호출자를 고려해 예외 클래스를 정의하라

  • 감싸기 기법
    • 라이브러리를 사용하는 경우, API 설계 방식에 얽매이지 않도록

7.6. 정상 흐름을 정의하라

  • 특수 사례 패턴 → 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식

7.7. null 을 반환하지 마라

  • null을 던져서 콜러에게 일을 떠맡기는 행위는 위험하다

7.8. null 을 전달하지 마라

  • 고려된 스펙이 아니라면 함수에 null 을 인자로 받지마라

8. 경계

8.1. 외부 코드 사용하기

  • map 계열을 사용할때 삭제(clear) 추가/수정(put)이 가능하므로 여기저기 넘기는 것은 경계

8.2. 경계 살피고 익히기

  • 외부 패키지 테스트의 책임은 없으나 사용하는 부분의 테스트는 필요
  • 학습 테스트 → 간단한 테스트 케이스를 작성해 외부 코드를 익히는 방법

8.3. log4j 익히기

8.4. 학습 테스트는 공짜 이상이다

  • 학습 테스트는 패키지가 예상대로 도는지 확인한다
  • 새 버전의 호환 이슈도 학습 테스트가 포착한다

8.5. 아직 존재하지 않는 코드를 사용하기

  • 아직 개발되지 않은 코드에 대해 API 명칭과 인수만 공유받고 개발 진행

9. 단위 테스트

9.1. TDD 법칙 세가지

  • 실패하는 단위 테스트를 작성할때까지 실제 코드를 작성하지 않는다
  • 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다
  • 현재 실패하는 테스트를 통과하는 정도로만 실제 코드를 작성한다

9.2. 깨끗한 테스트 코드 유지하기

  • 테스트 코드는 실제 코드 못지 않게 중요하다
  • 테스트 코드가 제공하는 것
    • 유연성
    • 유지보수성
    • 재사용성

9.3. 깨끗한 테스트 코드

  • 가독성이 중요하다
  • 빌드 오퍼레이트 체크 패턴
    • given 테스트 자료를 만든다
    • when 테스트 자료를 조작한다
    • then 조작한 결과가 예상대로인지 확인한다
  • 이중 표준
    • 테스트 코드도 품질이 좋아야 하나 실제 코드만큼 효율적일 필요는 없다
    • 의미만 안다면 규칙 위반이 조금 허용된다 (p 163)

9.4. 테스트당 assert 하나

  • 함수마다 assert 문이 하나면 결론이 하나라는 의미라 명확하고 좋다
  • 하나의 테스트당 하나의 개념만 테스트 하자

9.5. F.I.R.S.T

  • 빠르게(fast)
    • 빨라야 한다
  • 독립적으로(Independent)
    • 테스트는 다른 테스트에 종속되면 안된다
  • 반복가능하게(Repeatable)
    • 어떤 환경이든 반복 가능해야 한다
  • 자가검증하는(Self-validating)
    • True/False 로 떨어져야 한다
  • 적시에(Timely)
    • 실제 코드 작성 직전에 먼저 작성해야 한다
    • 작성 후에 테스트 코드를 짜려하면 어렵다

10. 클래스

10.1. 클래스 체계

  • 캡슐화

10.2. 클래스는 작아야 한다

  • 함수와 마찬가지로 작아야 한다
  • SRP
  • 응집도가 높아지도록 적절히 함수 분리와 클래스를 쪼개야 한다

10.3. 변경하기 쉬운 클래스

  • 하나의 수정에 다른 기능도 위험을 떠 안을 필요가 없다
  • 너무 많은 일을 하는 클래스 → 닫힌 클래스 집합으로 분리 해보자

11. 시스템

11.1. 도시를 세운다면?

  • 큰 그림을 모르더라도 개개인의 구성요소는 맞물려 돌아간다 ← 저스티스 리그

11.2. 시스템 제작과 시스템 사용을 분리하라

  • 준비과정(의존성 연결)과 런타임 로직은 분리되어야 한다
  • 초기화 지연, 계산 지연 기법은 본의 아니게 SRP를 깰 수 있다
    • get 에서 null 인 경우 init 또는 set 을 처리해버리는 경우
  • Main 분리
    • main (setup) → application 으로 객체 생성은 main 이 하고 application 은 사용만
  • 팩토리
    • 객체가 생성되는 시점이 main 이 모르는 경우, main 은 factory 만 만들어 둔다
    • application 은 factory를 사용하여 객체를 생성하므로 상세 생성 내용을 알지는 못하게 만드는게 핵심
  • 의존성 주입
    • 객체가 맡은 책임을 다른 객체에게 일임하면서 제어를 역전함 → 담당자로 특수 컨테이너
    • 권장하는 세 가지 *
      • 생성자 주입 ← 사내에서 서비스/컨트롤러에 사용하는 방식
      • setter 주입
      • 인터페이스 주입 ← 사내 서비스와 serviceImpl 구현체

11.3. 확장

  • 처음부터 올바르게 ← 이건 미신이다!
  • 횡단(cross-cutter) 관심사
    • 영속성
    • 관점(aspect)
      • AOP 에서 어떤 관점에서 개발할 것인가.
      • 보안이면 보안, 출납이면 출납, 물류면 물류만 유통이면 유통만 특정 시스템에서 해당 팀이 바라보는 주요 관점
      • 문제는 이런 관심사간 세부 구현 모듈끼리는 중복이 발생할 수 있다

11.4. 자바 프록시

  • 단순한 상황에 적합
  • 단점
    • 깨끗한 코드 작성이 어렵다
    • 시스템 단위로 실행 지점 을 명시하는 매커니즘을 제공하지 않는다

11.5. 순수 자바 AOP 프레임 워크

  • POJO
    • 간단한 자바 객체
      • 상속을 강제하지 않아야 하고
      • 인터페이스 구현을 강제하지 않아야 하고
      • 어노테이션 사용을 강제하지 않음
    • 순수하게 도메인에 초점을 둔다

11.6. AspectJ 관점

  • 관심사를 관점으로 분리하는 언어

11.7. 테스트 주도 시스템 아키텍처 구축

  • 건축과 달리 구조 수정이 용이 하므로 관점만 잘 분리된다면 BDUF 가 필요하지 않다
  • BDUF (Big Design Up Front)
    • 개발전 디자인을 먼저 ← 약간 워터풀

11.8. 의사 결정을 최적화하라

  • 때로는 가능한 마지막 순간까지 결정을 미루는 방법이 최선일 수 있다

11.9. 명백한 가치가 있을때 표준을 현명하게 사용하라

  • 최적화된 방법과 표준을 활용하라
  • 그러나 너무 낡고 어울리지 않는, 과장된 표준에는 집착하지 마라

11.10. 시스템은 도메인 특화 언어가 필요하다

  • DSL (Domain Specific Language)
    • 도메인 특화 언어는 관련 특정 분야에 최적화된 프로그래밍 언어
    • DSL은 해당 분야 또는 도메인의 개념과 규칙을 사용
  • 좋은 DSL 은 의사소통 간극을 줄여 준다

12. 창발성

  • 창발성 (創發)
    • 창발 또는 떠오름 현상은 하위 계층(구성 요소)에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출현하는 현상 (??? 이러면 도리어 이슈 아닌가???)

12.1. 창발적 설계로 깔끔한 코드를 구현하라

  • 단순한 설계 규칙
    • 모든 테스트를 실행한다
    • 중복을 없앤다
    • 프로그래머 의도를 표현한다
    • 클래스와 메서드 수를 최소로 줄인다

12.2. 모든 테스트를 실행하라

  • 설계 의도대로 돌아가는 시스템을 내놓아야 한다
  • 일단 돌아가야 한다 ← 그래야 검증을 하지
  • 테스트가 가능한 시스템을 구현하다 보면 자연스레 코드는 점차 클린해질 것이다(?)

12.3. 리팩터링

  • TC를 작성하고 점진적으로 리팩토링 해라
    • 코드 정리로 생기는 리스크는 TC가 있다
    • 그만큼 TC를 잘 만들어 둬야 하지 않나…

12.4. 중복을 없애라

  • 적은 줄이라도 중복을 줄이면 리팩터링이 보인다!

12.5. 표현하라

  • 자신이 이해하는 코드를 짜는 것은 쉽다
    • 개발자의 의도를 분명하게 하라
  • 의도를 표현하는 방법
    • 좋은 이름을 사용하라
    • 함수, 클래스 크기를 가능한 줄여라
    • 표준 명칭을 사용해라
    • UT 를 꼼꼼히 작성해라

12.6. 클래스와 메서드 수를 최소로 줄여라

  • 앞서 12.4, 12.5 를 너무 사용하면 너무 많아지니까 이건 좀 적당히 해라(??)

13. 동시성 

13.1. 동시성이 필요한 이유?

  • 결합을 없애는 전략이다
    • 무엇언제를 분리하는 전략
  • 타당한 생각
    • 다소 부하를 일으킨다
    • 복잡하다
    • 동시성 버그는 재현하기 어렵
    • 동시성을 구현하려면 흔히 근본적인 설계 전략을 고민해야 한다

13.2. 난관

  • Thread → 전위연산자 후위연산자 예제

13.3. 동시성 방어 원칙

  • SRP
    • 동시성 코드다른 코드와 분리하라
  • 자료 범위를 제한하라
    • 자료를 캡슐화하라. 공유 자료를 최소화해라
  • 자료 사본을 사용하라
    • 애초에 사본을 이용하여 본 자료를 공유하지 않는 방법도 있다
  • 스레드는 가능한 독립적으로 구현하라
    • 독자적인 스레드로, 가능하면 다른 프로세서에서 돌려도 괜찮도록 독립적인 단위로 분할하라 (???)

13.4. 라이브러리를 이해하라

  • 스레드 환경에 안전한 컬렉션 (자바 over 5 버전)
  • 서로 무관한 작업을 수행할때는 executor 프레임워크를 추천
  • 가능하다면 스레드 blocking되지 않는 방법을 써라
  • 일부 라이브러리스레드 환경에서 안전하지 않다!
    • concurrent atomic locks ← (???)

13.5. 실행 모델을 이해하라

  • 생산자-소비자 (Producer-consumer)
    • 한정된 대기열(buffer)에 생산자가 작업을 올리고 대기하던 소비자가 구매하는 방식
    • 잠자는 이발사
    • 임계구역에 대한 상호배타 처리를 하거나 운영체제에서는 세마포어를 사용해 해결한다
  • 읽기-쓰기 (Read-write)
    • 공유 자원을 읽기 스레드와 쓰기 쓰레드가 각각 사용할 때 처리율 문제
    • 처리율
      • 단위 시간당 몇개의 작업(프로세스)을 처리하였는지
    • 상호 배제를 너무 강조할 경우, 처리율에 문제가 발생
    • 처리율만 고집하면, 상대적으로 작업 비용이 큰 작업은 기아 상태에 빠짐
      • 에이징 같은 우선순위를 고려한 해결책이 필요
  • 식사하는 철학자들 (Dining philosophers)
    • 철학자들은 원형 식탁에 앉아 생각하거나 식사하는데, 자기 앞의 포크를 남과 공유하는 상황
    • 교착상태

13.6. 동기화화는 메서드 사이에 존재하는 의존성을 이해하라

  • synchronized 공유 객체 하나에는 메서드 하나만 사용하라

13.7. 동기화하는 부분을 작게 만들어라

  • lock 은 반드시 임계구역에만 걸자

13.8. 올바른 종료 코드는 구현하기 어렵다.

  • 동시성이 예상되는 시스템은 종료 코드도 고민하자

13.9. 스레드 코드 테스트 하기

  • 말이 안되는 실패는 잠정 스레드 문제
    • 일회성이 아닐 수 있다.
  • 다중 스레드 이전에 돌아가는 코드부터 만들어라
    • 동시성 테스트기본 테스트분리해야 한다
  • 다중 스레드를 쓰는 코드 부분을 상황에 맞게 조정할 수 있게 해라
  • 프로세서 수보다 많은 스레드를 돌려봐라 ← 톰캣의 경우 work thread 보다 많이 동시 요청 해봐라
    • JMeter 같은 툴이 있다
  • 강제로 실패를 일으켜 봐라
반응형