Clean Code - 냄새와 휴리스틱

Seoyong Lee·2023년 4월 30일
0

개발 관련 생각들

목록 보기
8/9

이번 장은 클린코드의 17장 '냄새와 휴리스틱'을 표로 요약하여 실무에서 참고할 수 있도록 정리해보았다.

N-이름

코드내용
N1서술적인 이름 사용
N2적절한 추상화 수준의 이름 선택
N3표준 명명법 사용
N4명확한 이름
N5범위에 비례하는 길이의 이름 사용
N6인코딩 금지
N7이름으로 부수 효과 설명

N1: 서술적인 이름을 사용하라

  • 신중하게 선택한 이름을 보고 모듈 내 함수가 하는 일을 짐작할 수 있어야 한다

N2: 적절한 추상화 수준에서 이름을 선택하라

  • 너무 구체적인 구현을 드러내는 이름은 피하라
  • 함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라

N3: 표준 명명법을 사용하라

  • 기존 명명법을 사용하는 이름은 이해하기 더 쉽다

N4: 명확한 이름

  • 함수나 변수의 목적을 명확히 밝히는 이름을 선택한다

N5: 긴 범위는 긴 이름을 사용하라

  • 이름 길이는 범위에 비례해야 한다

N6: 인코딩을 피하라

  • 이름에 유형 및 범위 정보를 넣어서는 안 된다
  • 헝가리안 표기법의 오염에서 이름을 보호하라

N7: 이름으로 부수 효과를 설명하라

  • 함수, 변수, 클래스가 하는 모든 일을 포함하는 이름을 사용하며 부수효과를 숨기지 않는다

C-주석

코드내용
C1부적절한 정보
C2쓸모 없는 주석
C3중복된 주석
C4성의 없는 주석
C5주석 처리된 코드

C1: 부적절한 정보

  • 메타정보 외에 변경 이력이나 날짜 등은 다른 시스템에 저장해도 되므로 주석에 작성하지 않는다

C2: 쓸모 없는 주석

  • 쓸모 없어진 주석은 빨리 삭제하는 편이 좋다

C3: 중복된 주석

  • 주석은 코드만으로 다하지 못하는 설명을 부언해야 한다
  • 코드로 설명 가능한 내용을 주석으로 반복하면 중복된 주석이다

C4: 성의 없는 주석

  • 주절대거나 당연한 소리를 반복하지 말아야 한다

C5: 주석 처리된 코드

  • 주석 처리된 코드는 즉각 삭제해야 한다
  • git이 기억하는 이상

E-환경

코드내용
E1한 단계로 빌드
E2한 명령으로 테스트

E1: 한 단계로 빌드해야 한다

  • 한 명령으로 전체를 체크아웃해 빌드할 수 있어야 한다

E2: 한 명령으로 테스트해야 한다

  • 한 명령으로 모든 단위테스트를 돌려야 한다

F-함수

코드내용
F1너무 많은 인수
F2출력 인수
F3플래그 인수
F4죽은 함수

F1: 너무 많은 인수

  • 함수가 받는 인수 개수는 작을수록 좋다
  • 아예 없으면 가장 좋다

F2: 출력 인수

  • 값 반환을 위해 인수로 전달된 함수를 사용하는 경우 전달된 함수를 출력 인수라고 한다
  • 일반적으로 독자는 인수를 출력이 아닌 입력으로 간주하므로 출력 인수는 가독성을 떨어트린다

F3: 플래그 인수

  • boolean 인수는 함수가 여러 기능을 수행한다는 명백한 증거로 피해야 한다

F4: 죽은 함수

  • 아무도 호출하지 않는 함수는 삭제한다

T-테스트

코드내용
T1불충분한 테스트
T2커버리지 도구 사용
T3사소한 테스트 생략 금지
T4무시한 테스트
T5경계 조건 테스트
T6버그 주변 테스트
T7실패 패턴 확인
T8테스트 커버리지 패턴 확인
T9빠른 테스트 속도

T1: 불충분한 테스트

  • 테스트는 잠재적으로 깨질 만한 모든 부분을 테스트해야 한다
  • 테스트 케이스가 확인하지 않는 조건이 있다면 테스트는 불완전하다

T2: 커버리지 도구를 사용하라

  • 커버리지 도구는 테스트가 빠뜨리는 공백을 알려준다

T3: 사소한 테스트를 건너뛰지 마라

  • 사소한 테스트가 제공하는 가치는 구현 비용을 넘어선다

T4: 무시한 테스트는 모호함을 뜻한다

  • 테스트 케이스를 주석 등으로 처리하였다면 이는 모호함을 나타낸다

T5: 경계 조건을 테스트하라

  • 경계 조건은 각별히 신경 서서 테스트 해야한다

T6: 버그 주변은 철저히 테스트하라

  • 버그는 서로 모이는 경향이 있다

T7: 실패 패턴을 살펴라

  • 테스트 케이스가 실패하는 패턴으로 문제를 진단할 수 있다

T8: 테스트 커버리지 패턴을 살펴라

  • 통과하는 테스트가 실행한 코드를 살펴보면 실패한 테스트 케이스의 원인이 드러난다

T9: 테스트는 빨라야 한다

  • 테스트 케이스가 느리면 실행하지 않게 된다

G-일반

코드내용
G1파일 내 언어 통일
G2당연한 동작을 구현하지 않는 경우
G3올바른 경계 처리
G4안전 절차 무시
G5중복
G6올바르지 못한 추상화 수준
G7기초 클래스가 파생 클래스에 의존
G8과도한 정보
G9죽은 코드
G10수직 분리
G11일관성 부족
G12잡동사니
G13인위적 결합
G14기능 욕심
G15선택자 인수
G16모호한 의도
G17잘못 지운 책임
G18부적절한 static 함수
G19서술적 변수 사용
G20이름과 기능이 일치하는 함수
G21알고리즘 이해
G22논리적 의존성 제거
G23if, switch 문의 다형성 사용
G24표준 표기법
G25매직넘버
G26정확성
G27구조를 통한 강제
G28조건의 캡슐화
G29부정 조건문
G30한 가지 기능만 하는 함수
G31숨겨진 시간적인 결합
G32일관성 유지
G33경계 조건 캡슐화
G34한 단계만 내려가는 함수의 추상화 수준
G35최상위 정보 설정
G36추이적 탐색

G1: 한 파일에선 하나의 언어를 사용하라

  • 한 소스 파일에 하나의 언어만 사용하는 것이 가장 이상적이다

G2: 당연한 동작을 구현하지 않는다

  • 당연한 동작을 구현하지 않으면 코드를 읽는 사람이 기능을 직관적으로 예상하기 어렵게 되면서 저자를 신뢰하지 못하게 된다.

G3: 경계를 올바로 처리하라

  • 모든 경계 조건을 찾아내고 테스트하는 테스트 케이스를 작성하라

G4: 안전 절차 무시

  • 컴파일러 경고 일부를 꺼버리면 빌드가 쉬워질지도 모르지만 자칫 끝없는 디버깅에 시달리게 될 수 있다

G5: 중복

  • 코드에서 중복을 발견할 때마다 추상화할 기히로 간주하라
  • 최근 15년 동안 나온 디자인 패턴은 대다수가 중복을 제거하는 방법에 불과하다

G6: 추상화 수준이 올바르지 못하다

  • 고차원 개념과 저차원 개념을 섞어서는 안 된다

G7: 기초 클래스가 파생 클래스에 의존

  • 클래스를 기초 클래스와 파생 클래스로 구분하는 이유는 고차원 기초 클래스의 개념을 저차원 파생 클래스로부터 분리해 독립성을 보장하기 위해서다

G8: 과도한 정보

  • 우수한 개발자는 클래스나 모듈 인터페이스에 노출할 함수를 제한할 줄 알아야 한다
  • 함수가 아는 변수 수가 작을수록 좋다. 자료를 숨기고 유틸을 숨겨라. 상수와 변수를 숨겨라.

G9: 죽은 코드

  • 실행되지 않는 코드인 죽은 코드는 시간이 지나면 악취를 풍기기 시작한다. 적절한 장례식을 치뤄주자(시스템에서 제거)

G10: 수직 분리

  • 변수와 함수는 사용되는 위치에 가깝게 정의한다

G11: 일관성 부족

  • 어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다

G12: 잡동사니

  • 사용하지 않는 변수, 호출하지 않는 함수, 정보를 제공하지 못하는 주석은 모두 잡동사니로 없애야 한다

G13: 인위적 결합

  • 서로 무관한 개념을 인위적으로 결합하지 않는다
  • 직접적 상호작용이 없는 두 모듈 사이에서 주로 변수, 상수, 함수의 인위적 결합이 일어난다

G14: 기능 욕심

  • 클래스 메서드는 다른 클래스의 변수와 함수에 관심을 가져서는 안된다

G15: 선택자 인수

  • 선택자 인수는 목적을 기억하기 어렵기 때문에 사용하지 않은 것이 좋다
  • boolean 뿐만 아니라 enum, int 등 함수 동작을 제어하려는 인수는 하나 같이 바람직하지 않다
  • 인수를 넘겨 동작을 선택하는 대신 새로운 함수를 만드는 편이 좋다

G16: 모호한 의도

  • 코드를 짤 때는 의도를 최대한 분명히 밝힌다

G17: 잘못 지운 책임

  • 코드는 독자가 자연스럽게 기대할 위치에 배치해야 한다
  • 때로는 개발자가 독자에게 직관적인 위치가 아니라 개발자에게 편한 위치에 배치한다

G18: 부적절한 static 함수

  • 재정의(override) 가능성이 조금이라도 있다면 static 함수가 아닌 인스턴스 함수로 정의한다

G19: 서술적 변수

  • 가독성을 높이는 가장 효과적인 방법 중 하나가 계산을 여러 단계로 나누고 중간 값으로 서술적인 변수 이름을 사용하는 것이다
  • 서술적인 변수 이름은 많으면 많을수록 좋다

G20: 이름과 기능이 일치하는 함수

  • 이름만으로 분명하지 않아 구현을 살펴야 한다면 이름을 바꾸거나 기능을 정리해야 한다

G21: 알고리즘을 이해하라

  • 구현이 끝났다고 선언하기 전에 함수가 돌아가는 방식을 확실히 이해하는지 확인하라
  • 모든 테스트 케이스 통과만으로는 부족하며 작성자가 알고리즘이 올바르다는 사실을 알아야 한다

G22: 논리적 의존성을 물리적으로 드러내라

  • 논리적 의존성을 가진 가정을 명시적으로 요청(물리적)하는 편이 좋다

G23: if/else 혹은 switch/case 문보다 다형성을 사용하라

  • 같은 선택을 수행하는 여러 코드에서 다형성 객체를 생성해 하나의 switch문만 사용한다

G24: 표준 표기법을 따르라

  • 팀은 업계 표준에 기반한 구현 표준을 팀원 모두가 따라야 한다

G25: 매직넘버는 명명된 상수로 교체하라

  • 코드에서 숫자를 바로 사용하지 말고 명명된 상수 뒤로 숨겨라

G26: 정확하라

  • 코드에서 무언가를 결정할 때는 정확히 결정한다
  • 결정을 내리는 이유와 예외를 처리할 방법을 분명히 알아야 한다

G27: 관례보다 구조를 사용하라

  • 설계 결정을 강제할 때는 명명 관례보다는 구조 자체로 강제하는 것이 좋다

G28: 조건을 캡슐화하라

  • 조건문의 의도를 분명히 밝히는 함수 등으로 표현하라

G29: 부정 조건은 피하라

  • 부정 조건은 긍정 조건보다 이해하기 어려우므로 가능하면 긍정 조건으로 표현한다

G30: 함수는 한 가지만 해야 한다

  • 함수를 짜다 보면 한 함수 안에 여러 단락을 이어 일련의 작업을 수행하고픈 유혹에 빠진다
  • 이런 함수는 한 가지만 수행하는 좀 더 작은 함수 여럿으로 나누어야 한다

G31: 숨겨진 시간적인 결합

  • 시간적 결합은 때로 필요하지만 숨겨서는 안 된다
  • 의도적으로 추가한 구문의 복잡성이 원래 있던 시간적 복잡성을 드러내는 경우가 존재한다

G32: 일관성을 유지하라

  • 코드 구조를 잡을 때는 이유를 고민하고 그 이유를 코드 구조로 일관성 있게 명백히 표현하라

G33: 경계 조건을 캡슐화하라

  • 경계 조건은 변수로 캡슐화하여 한 곳에서 별도로 처리해야 빼먹거나 놓치지 않는다

G34: 함수는 추상화 수준을 한 단계만 내려가야 한다

  • 함수 내 모든 문장은 추상화 수준이 동일해야 한다
  • 그리고 그 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다
  • 추상화 수준 분리는 리팩터링의 가장 중요한 수행 이유 중 하나이다

G35: 설정 정보는 최상위 단계에 둬라

  • 설정 관련 상수는 최상위 단계에 두어야 변경하기 쉽다
  • 저차원 함수에 상수 값을 정의하면 안 된다

G36: 추이적 탐색을 피하라

  • 일반적으로 한 모듈은 주변 모듈을 모를수록 좋다(Law of Demeter)
  • 내가 사용하는 모듈이 내게 필요한 서비스를 모두 제공해야 한다
  • 원하는 메서드를 찾느라 객체 그래프를 따라 시스템을 탐색할 필요가 없어야 한다

0개의 댓글