[Book] 읽기 좋은 코드가 좋은 코드다

<읽기 좋은 코드가 좋은 코드다> 정리

Part 1. 표면적 수준에서의 개선

2. 이름에 정보 담기

  • 특정한 단어를 사용하라 : get 대신 fetch나 download를 사용하는 것이 나을 수 있다
  • 보편적인 이름의 사용을 피하라 : tmp, rtnVal 같은 것은 지양
  • 구체적인 이름을 이용
  • 변수명에 중요한 세부 정보를 덧붙여라 : 단위와 같은 것들
  • 사용 범위가 넓으면 긴 이름을 사용

3. 오해할 수 없는 이름들

  1. 경계에 대한 변수 사용
    • 경계를 포함하는 한계값을 다룰 때는 min / max를 사용
    • 경계를 포함하는 범위에는 first / last를 사용
    • 경계를 포함하고/배제하는 범위에는 begin / end를 사용
  2. boolean
    • is 또는 has와 같은 단어를 사용
    • 의미를 부정하는 단어는 피하는게 좋음

4. 미학

미학적으로 보기 좋은 코드가 사용하기 더 편리하다

  • 여러 블록에 담긴 코드가 모두 비슷한 일을 수행하면 ,실루엣이 동일해 보이게 만들어라
  • 코드 곳곳을 ‘열’로 만들어서 줄을 맞추면 코드를 한 눈에 훑어보기 편하다 : Yang’s comment 최근에는 일관된 코드 스타일을 위해 코드 컨벤션을 정의하고 IDE에 적용하고 있음. 이에 따라 최근에는 줄을 맞추는 것이 어렵다.
  • 빈 줄을 이용하여 커다란 블록을 논리적인 ‘문단’으로 나누어라

5. 주석에 담아야 하는 대상

주석의 목적은 코드를 읽는 사람이 코드를 작성한 사람만큼 코드를 잘 이해하게 돕는 데 있다

  1. 설명하지 말아야 할 것
    • 코드 자체에서 재빨리 도출될 수 있는 사실
    • 나쁜 함수명과 같이 나쁘게 작성된 코드를 보정하려고 애쓰는 주석. → 코드를 수정하라
  2. 기록해야 하는 생각
    • 코드가 특정한 방식으로 작성된 이유
    • 코드에 담긴 결함. TODO: 혹은 XXX:와 같은 표시를 사용하라
    • 어떤 상수가 특정한 값을 갖게 된 사연
  3. 제 3자 입장에서 필요한 경우
    • 코드를 읽는 사람이 자기가 작성한 코드의 어느 부분을 보고 ‘뭐라고?’라는 생각을 할지 예측해보고, 그 부분에 주석을 추가
    • 평범한 사람이 예상하지 못할 특이한 동작을 기록
    • 파일이나 클래스 수준 주석에서 ‘큰 그림’을 설명하고 각 조각이 어떻게 맞춰지는지 설명
    • 코드에 블록별로 주석을 달아 세부 코드를 읽다가 나무만 보고 숲은 못 보는 실수를 저지르지 마라

6. 명확하고 간결한 주석 달기

  • ‘it’이나 ‘this’같은 대명사가 여러 가지를 가리킬 수 있다면 사용하지 않는 것이 좋다
  • 함수의 동작을 실제로 할 수 있는 한도 내에서 최대한 명확하게 설명하라
  • 신중하게 선택된 입/출력 예로 주석을 서술
  • 코드가 가진 의도를 너무 자세한 내용이 아니라 높은 수준에서 개괄적으로 설명
  • 많은 의미를 함축하는 단어로 주석을 간단하게 만들어라

Part 2. 루프와 논리를 단순화하기

7. 읽기 쉽게 흐름제어 만들기

흐름을 제어하는 조건과 루프 그리고 여타 요소를 최대한 ‘자연스럽게’ 만들도록 노력하라. 코드를 읽다가 다시 되돌아가서 코드를 읽지 않아도 되게끔 만들어야 한다.

  • 조건문
    • 왼쪽 : 값이 더 유동적인 ‘질문을 받는’ 표현
    • 오른쪽 : 더 고정적인 값으로, 비교대상으로 사용되는 표현
  • if/else
    • 부정이 아닌 긍정을 다루어라
    • 간단한 것을 먼저 처리
    • 더 흥미롭고, 확실한 것을 먼저 처리
  • 삼항연산자
    • 매우 간단할 때만 적용
    • 코드 라인을 줄이려다 다른 사람이 이해하기 어려운 코드를 만들 수도 있다
  • do/while 문을 피하라
  • 함수 중간에서 반환하기
    • 함수 중간에서 반환 처리를 해 로직 파악 용이하도록 한다
  • 중첩 최소화 : 선형적인 코드를 추구!!
    1. if 문의 중첩
      • 함수 중간에서 반환해 중첩을 제거
    2. Loop 문의 중첩
      • continue 를 활용하라

8. 거대한 표현 잘게 쪼개기

거대한 표현을 더 소화하기 쉬운 여러 조각으로 나눈다

  1. 설명 변수
    • 하위표현을 담을 추가 변수. 하위표현의 의미를 설명하기 때문에 ‘설명 변수’라고도 불림
  2. 요약 변수
    • 커다란 코드의 덩어리를 짧은 이름으로 대체하여 더 쉽게 관리하고 파악하는 목적을 가진 변수
  3. 드모르간의 법치 사용
    • not을 분배하고 and/or를 바꿔라
  4. Short-Circuit Logic 오용 말기
    • or 조건에서 선 조건이 true이면 후 조건은 평가되지 않는다.

9. 변수와 가독성

  • 잘못된 변수 사용의 문제점
    1. 변수의 가 많을 수록 기억하고 다루기 어려워짐
    2. 변수의 범위가 넓어질수록 기억하고 다루는 시간이 더 길어짐
    3. 변수값이 자주 바뀔수록 현재값을 기억하고 다루기가 더 어려워짐
  1. 변수 제거하기
    • 가독성에 도움되지 않는 변수 제거
    • ie1. 불필요한 임시 변수
      • 필요한 임시 변수 판단 기준
        1. 복잡한 표현을 잘게 나누는가?
        2. 명확성에 도움이 되는가?
        3. 여러 번 사용되어 중복된 코드를 압축하고 있는가?
    • ie2. 흐름 제어 변수 제거
  2. 변수의 범위를 좁혀라
    • 변수가 적용되는 범위를 최대한 좁게 만들어라
  3. 값을 한 번만 할당하는 변수를 선호하라
    • 변수값이 달리지는 곳이 많을수록 현재값을 추측하기 더 어려워진다
    • 가능하다면 immutable 불변 객체를 사용하자

Part3. 코드 재작성하기

  • 함수 수준의 변경 방법

10. 상관없는 하위문제 추출하기

전체 목적에 직접 상관없는 하위문제를 다루는 코드를 적극적으로 찾으려고 노력하는 것

  • 메소드를 분리하면 단위 테스트 더 용이
  • 일반적인 목적의 코드를 프로젝트의 특정 코드에서 분리하라
    1. 순수한 유틸리티 코드
    2. 일반적인 목적의 코드

11. 한 번에 하나씩

한 번에 하나의 작업만 수행하게 코드를 구성해야 한다.

함수를 나누는 작업뿐만 아니라 함수 내 코드를 재조직해 논리적 영역을 나누어 문단을 구성하는 것도 포함된다.

  1. 코드가 수행하는 모든 작업을 나열
  2. 작업을 분리해 서로 다른 함수로 혹은 적어도 논리적으로 구분되는 영역에 놓을 수 있는 코드로 만들어라

12. 생각을 코드로 만들기

“할머니에게 설명할 수 없다면 당신은 제대로 이해한 게 아닙니다”
- 알버트 아인슈타인-

논리를 쉬운 말로 표현해서 정리하라. 그리고 이를 코드에 적용하라.

고무 오리 기법

  • 도움을 요청하기 전에 문제를 혼자서 말로 설명해보기
  • 자신의 문제를 쉬운 말로 설명할 수 없으면, 해당 문제는 무언가 빠져 있거나 아니면 제대로 정의되지 않은 것

13. 코드 분량 줄이기

가장 읽기 쉬운 코드는 아무것도 없는 코드

  1. YAGNI - You Ain’t Gonna Need It
    미래에 필요할 것이라고 추측되는 기능을 미리 만들지 말라는 원칙. 또한, 지나치게 멋진 기능을 구현할 필요가 없다.

  2. 요구사항에 질문을 던지고 질문을 잘게 나누어 분석하라

  3. 코드베이스를 작게 유지하기
    프로젝트가 진행될 수록 코드 베이스는 점차 늘어남. 최대한 코드베이스를 작고 가볍게 유지하도록 하는 노력이 필요.
    • 일반적인 ‘유틸리티’를 많이 생성해 중복된 코드를 제거
    • 사용하지 않는 코드 혹은 필요 없는 기능을 제거
    • 프로젝트가 서로 분절된 하위프로젝트로 구성되게 하라
    • 코드베이스의 ‘무게’를 항상 의식하여 가볍고 날렵하게 유지시켜라
  4. 주변에 있는 라이브러리에 친숙해져라
    • 매일 15분씩 자신의 표준 라이브러리에 있는 모든 함수/모듈/형들의 이름을 읽어라
      외울 필요는 없다. 그저 익숙해지고 나중에 필요할 때 떠올릴 수 있도록 만들자
    • 라이브러리의 코드는 설계, 디버깅, 재작성, 문서화, 최적화, 테스트를 모두 거친 훌륭한 코드다.
    • ie
      • Spring의 StringUtils : 스프링은 String과 관련된 많은 기능을 core에 포함시켜 제공해주고 있다.
      • Java의 Map
        1. getOrDefault()
        2. putIfAbsent() 등 다양한 API를 제공하고 있음

Part 4. 선택된 주제들

14. 테스트와 가독성

  1. 읽거나 유지보수하기 쉽게 테스트를 만들어라
    • 다른 프로그래머가 수정하거나 새로운 테스트를 더하는 걸 쉽게 느낄 수 있게 테스트 코드는 읽기 쉬워야 한다
      • 테스트 코드가 어렵게 작성되면 코드를 수정하기를 꺼려하는 일이 발생한다.
      • 또한, 새로운 테스트를 작성하는 것 역시 꺼려하게 된다.
    • 가능하면 가장 간단한 입력으로 코드를 완전히 검사할 수 있어야 한다
    • 필요한 작업을 수행하는 범위에서 가장 명확하고 간단한 테스트 값을 선택하라
  • 테스트 하기 어려운 코드 특징
    1. 전역 변수 사용
    2. 코드가 많은 외부 컴포넌트를 사용
    3. 코드가 비결정적인 행동을 가짐
      • 테스트가 변덕스럽고 안정적이지 못하다. 가끔 실패하는 테스트가 그냥 무시된다.
  • 테스트에 적합한 설계 특징
    1. 클래스들이 내부 상태를 거의 가지고 있지 않다
    2. 클래스/함수가 한 번에 하나의 일만 수행한다
    3. 클래스가 다른 클래스에 의존하지 않고, 서로 상당히 떨어져 있다
    4. 함수들이 간단하고 잘 정의된 인터페이스를 가지고 있다
  • 테스트를 개선하기 위한 구체적인 항목
    1. 각 테스트의 최상위수준은 최대한 간결해야. 이상적으로는 각 테스트의 입출력이 한 줄의 코드로 설명될 수 있어야 한다.
    2. 테스트에 실패하면, 버그를 추적해서 수정하는 데 도움이 될 만한 에러 메시지를 출력해야 한다.
    3. 코드의 구석구석을 철저하게 실행하는 가장 간단한 입력을 사용하라
    4. 무엇이 테스트되는지 분명하게 드러나도록 테스트 함수에 충분한 설명이 포함된 이름을 부여하라.

무엇보다도, 테스트의 수정이나 추가가 쉬워야 한다