4장의 태도는 도발적이다. “주석은 잘해봐야 필요악이다.” 코드로 의도를 표현할 수 있다면 주석은 필요 없고, 표현하지 못했을 때에야 주석으로 그 실패를 메운다는 것이다. 그래서 이 장은 주석 잘 쓰는 법이 아니라, 주석을 줄이는 법과 남길 주석을 고르는 법에 가깝다.

주석은 코드의 실패를 메운다

주석은 도움이 될 때도 있지만, 코드의 부족함을 덮는 도구는 아니다. 실제로 실행되는 것은 코드이고, 주석은 시간이 지나면 코드와 어긋난다. 코드가 바뀌었는데 주석이 함께 바뀌지 않으면 설명은 오히려 잘못된 단서가 된다. 그리고 주석은 오래될수록, 코드에서 멀어질수록 거짓말이 될 확률이 높아진다 — 프로그래머가 주석까지 성실히 유지하기는 현실적으로 어렵기 때문이다.

그래서 먼저 해야 할 일은 주석을 잘 쓰는 것이 아니라 주석이 덜 필요하도록 코드를 쓰는 것이다. 이름을 명확히 붙이고, 함수를 적절히 나누고, 조건문을 읽기 좋게 정리하면 많은 설명이 자연스럽게 사라진다.

주석보다 표현이 먼저다

복잡한 조건 위에 설명 주석을 붙이는 대신, 그 조건을 이름 있는 함수로 뽑는다. 주석은 읽는 사람에게 한 번 더 해석을 요구하지만, 잘 지은 이름은 코드의 일부로 계속 검증된다.

// before — 주석이 조건을 해설한다
// 직원이 복지 혜택을 받을 자격이 있는지 확인
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
 
// after — 조건 자체가 문장이 된다
if (employee.isEligibleForFullBenefits())

주석으로 설명하려던 “복지 혜택 자격”이 이제 메서드 이름 안에 들어가 코드와 함께 산다. 조건이 바뀌면 이름도 함께 바뀔 뿐, 어긋난 주석이 남을 여지가 없다. 이것이 이 장이 반복하는 원칙이다 — 설명하려 하지 말고 표현하라.

어떤 주석을 남길 것인가

그럼에도 어떤 정보는 코드로 표현되지 않는다. 이 판단을 흐름으로 정리하면 이렇다.

flowchart TD
  Start["주석을 달고 싶다"] --> Q1{"코드·이름·함수로<br/>표현할 수 있나?"}
  Q1 -->|그렇다| Refactor["주석 대신 코드로 표현<br/>(이름 바꾸기·함수 추출)"]
  Q1 -->|아니다| Q2{"무엇을 담는 주석인가?"}
  Q2 -->|의도·결정 배경·경고<br/>법적 고지·TODO·증폭| Keep["남긴다 (좋은 주석)<br/>단, 정확히·짧게"]
  Q2 -->|코드 반복·이력·닫는 괄호<br/>주석 처리된 코드·잡음| Delete["지운다 (나쁜 주석)"]

핵심 갈림길은 첫 질문이다. 표현할 수 있으면 표현이 이기고, 표현할 수 없는 배경만 주석으로 남는다.

좋은 주석

좋은 주석은 독자가 놓치기 쉬운 의도나 제약을 짧게 알려준다.

  • 의도를 설명하는 주석: 왜 이 알고리즘을 택했는지, 왜 이 값이어야 하는지 같은 결정의 배경.
  • 경고성 주석: “이 테스트는 실행에 오래 걸린다”, “스레드 안전하지 않다” 처럼 함정을 미리 알린다.
  • 결과를 경고하거나 증폭하는 주석: 사소해 보이는 코드가 사실 중요하다는 것을 강조한다.
  • 법적 고지·TODO: 라이선스 헤더, 아직 처리하지 못한 일에 대한 표시.
// 좋은 주석 — 정규식이 무엇을 매칭하는지 의도를 밝힌다
// kk:mm:ss EEE, MMM dd, yyyy 형식과 일치하는지 검사
Pattern timeMatcher = Pattern.compile(
    "\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");
 
// 좋은 주석 — 무시하면 안 되는 이유를 증폭한다
String listItemContent = match.group(3).trim();
// trim은 중요하다. 앞쪽 공백이 있으면 다른 리스트로 오인된다.

판단 기준: 좋은 주석도 “코드로 옮길 수 있으면 옮긴다”가 우선이다. 남기기로 했다면 오래 버틸 만큼 정확해야 하고, 코드 가까이 두어 함께 관리한다.

나쁜 주석

대부분의 주석은 이 범주에 속한다.

  • 중복 주석: 코드가 하는 말을 그대로 되풀이한다. i++; // i를 1 증가는 아무것도 더하지 않는다.
  • 오해의 소지가 있는 주석: 미묘하게 부정확한 설명은 없는 것보다 나쁘다.
  • 주석 처리된 코드: 지우기 두려워 남겨둔 코드. 버전 관리 시스템이 이미 보관하므로 파일에 남길 이유가 없다.
  • 이력·잡음·닫는 괄호 주석: } // end of function 같은 표시는 함수를 작게 만들면 필요 없어진다.
// before — 함수 시그니처를 그대로 반복하는 잡음
/**
 * @param title CD 제목
 * @param author 저자
 * @return 없음
 */
public void addCD(String title, String author) { ... }
 
// after — 이름이 이미 말하는 것을 반복하지 않는다
public void addCD(String title, String author) { ... }

함정: 주석 처리된 코드는 시간이 갈수록 “누가 왜 남겼는지, 지워도 되는지” 아무도 모르는 지뢰가 된다. 다른 사람은 그것을 함부로 지우지 못하고 계속 쌓인다. 필요하면 되살릴 수 있다는 믿음으로 지금 지운다.

주석은 책임을 만든다

주석을 남기면 그 주석도 유지보수 대상이 된다. 코드만 고치고 주석을 잊으면 독자는 둘 중 무엇을 믿어야 할지 고민하게 된다. 그래서 주석은 적을수록 좋고, 남긴다면 오래 버틸 수 있을 만큼 정확해야 한다.

이 장의 핵심은 주석을 금지하자는 것이 아니다. 주석이 필요한 순간을 더 신중하게 고르고, 그 전에 코드 자체를 더 분명하게 만들자는 것이다. 설명보다 표현이 먼저다.

정리

  • 주석은 코드로 표현하지 못한 것을 메우는 필요악이다. 최선은 주석이 필요 없도록 표현하는 것.
  • 판단은 한 질문에서 갈린다: “코드·이름·함수로 표현할 수 있나?” 있으면 표현이 이긴다.
  • 좋은 주석: 의도·결정 배경, 경고, 증폭, 법적 고지, TODO. 코드로 못 옮기는 맥락만.
  • 나쁜 주석: 코드 반복, 오해, 이력, 닫는 괄호, 그리고 주석 처리된 코드. 뒤엣것은 지금 지운다.
  • 남긴 주석은 유지보수 책임을 만든다. 오래 버틸 만큼 정확하지 않을 거라면 애초에 남기지 않는다.

다음장으로 5장