
part1.Code
Chapter5. Comments
Don't comment bad code - rewrite it.
- Brian W.Kernighan and P.J. Plaugher
Comments are the distracting footnotes of code
- Jeff Langr
잘 배치된 주석만큼 유용한 것도 없다.
거짓말과 잘못된 정보를 퍼뜨리는 낡고 퀴퀴한 주석만큼 해로운 것도 없다.
작가양반... old curfy comment라니...ㅋㅋㅋㅋ
Compensating for Failure
실패에 대한 보상
주석은 코드로 완전히 표현하는 것을 실패했을 때 이를 커버하기 위해 쓰는 것이다.
주석은 우리 언어나 능력의 실패이다.
와 시작부터 빡센데?
내가 네이버 인턴할 때 사수님이 주석 달지 말고 코드로 설명할 수 있게 짜라는 말씀이
이 clean code에서 나온거였나??
그렇다고 주석을 절대 쓰지 말라는 뜻은 아니라고 한다.
정말 필요한 상황에서 적절한 설명을 쓰는 것조차 하지 말라는 뜻은 아니라고 함.
Hidden or Obscured Comments
숨겨지거나 흐릿해진 주석
보통은 주석을 부가적인 요소 정도로 생각한다.
그래서 IDE에서도 대부분은 회색으로 설정하고, 종종 초록색(연두색)으로 설정하는 경우도 있다.
하지만, 저자는 '완전 강조된 빨간색'으로 설정한다고 한다.
저자는 '누군가 주석을 썼으면 읽어야 한다'고 생각하기 때문이다.
그래서, 도움이 되지 않는 주석이라고 판단하면 그 자리에서 즉시 삭제하고,
주석을 수정해야 한다면 그 자리에서 즉시 수정한다.
Lying Comments
거짓말하는 주석
저자는 모든 주석을 패는 건 아니고, 나쁜 주석에 대해서만 부정적이라고 한다.
개발자는 현실적으로 주석을 유지보수할 수 없기 때문에,
주석이 오래될수록, 설명하는 코드에서 멀어질수록 틀린 주석일 가능성이 높아진다.
(음 내가 레거시에서 많이 보는 패턴)
부정확한 주석이 존재하는 것보다는 주석이 없는게 낫다.
여기에서, 개발자가 신경을 더 써서 주석을 잘 관리해야 하는 것 아니냐라는 의문을 제기할 수 있는데
저자는 이에 대해 '애초에 주석이 필요 없을 만큼 코드를 명확하게 쓰는 훈련을 하라'고 말했다
Comments That Are Too Intimate
코드를 이미 안다는 가정 하에 쓰여진 주석
대부분의 주석은 코드의 맥락을 상세히 이해하고 있는 개발자가 작성한다.
하지만, 주석의 독자는 그 맥락을 모른 상태에서 읽기 때문에 이해하기 어렵다.
아이러니하게도, 개발자가 작성한 코드를 이해하고 나서야 주석을 이해할 수 있다.
(이런 생각은 안해봤는데, 코드를 이해해야 주석 맥락도 이해되면 완전 최악 주석이긴 하네요...)
Comments Do Not Make Up for Bad Code
주석은 나쁜 코드를 해결해줄 수 없다
코드를 작성하고 나서 너무 혼란스럽고 복잡하다는 생각이 들면
'아 주석을 달아야겠다!'라고 생각한다.
댓츠 노노!!! 정리를 하는 게 먼저다!!
주석이 적고 명확한 코드 > 주석이 많고 복잡한 코드
내가 만든 bad code를 설명하는 데 주석을 쓸 시간에 코드를 정리해라
(뼈를 너무 많이 맞아서 순살치킨 되는 중)
Explain Your Intent in Code
의도를 코드로 설명해라
// 직원이 전체 복지 혜택을 받을 자격이 있는지 확인
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
if (employee.isEligibleForFullBenefits())
의도를 코드에 표현하기 위해서는 약간의 리소스만 들이면 된다.
주석에서 하는 말을 대신하는 코드를 쓰면 그만이다.
코드가 설명에 적합한 수단은 아닐 수 있어도, 코드로 절대 설명을 할 수 없다는 의미는 아니다.
Good Comments
좋은 주석
1. Legal Comments
법적 이유로 회사 코드에 특정 주석을 작성해야할 수 있다.
// Copyright (C) 2003-2005 by Object Mentor, Inc. All rights reserved.
// Released under the GNU General Public License version 2 or later.
흠 모 대기업에서 본 코드에 이런 종류의 라이선스 주석이 있는 걸 보고 신기해했던 기억이 있음
2. Informative Comments
정보 제공 주석
1) 주석으로 기본 정보를 제공하는 것이 유용한 경우
// 테스트 중인 Responder의 인스턴스를 반환한다.
protected abstract Responder responderInstance();
Singlegton design pattern을 사용한 경우,
accessor function의 이름은 항상 Instance로 끝나야 한다.
그래서 디자인 패턴을 어기고 responderBeingTested라는 이름을 짓는 대신,
디자인 패턴을 따르고 주석을 쓰는 선택을 한 것이다.
2) 정규표현식을 설명하는 경우
// 매칭 형식 kk:mm:ss EEE, MMM dd, yyyy
Pattern timeMatcher = Pattern.compile(
"\\d+:\\d+:\\d+ \\w+, \\w+ \\d+, \\d+"
정규표현식은 한 번에 이해하기 어렵기 때문에, 이 용도가 타임스탬프 매칭임을 주석으로 명시한다.
3. Explanation of Intent
의도 설명
무엇(what)이 아니라 왜(why)를 설명하는 주석은 가치있다.
public void testConcurrentAddWidgets() throws Exception {
WidgetBuilder widgetBuilder = …
// 많은 수의 스레드를 생성하여 race condition을 얻으려는
// 최선의 시도다.
for (int i = 0; i < 25000; i++) {
WidgetBuilderThread widgetBuilderThread =
new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
Thread thread = new Thread(widgetBuilderThread);
thread.start();
}
assertEquals(false, failFlag.get());
}
}
전 개발자가 작성한 코드에 동의를 못할수도 있지만,
적어도 왜 무슨 맥락에서 무슨 의도로 이 코드가 탄생했는지 알 수 있다.
비즈니스 로직을 작성하다보면 짜치는(?) 코드가 많이 나온다.
(와 정말 비즈니스 아니면 지워버리고 싶다 울고싶은 코드)
(레거시에 정말 많음)
FE 동료 수빈님도 비슷한 내용의 코드 리뷰를 남겨주신 적이 있다.
주석은 '비즈니스적인 판단 때문에 이런 코드가 있었음'의 용도로 쓰는 것을 제안하셨다.
코드가 무슨 동작을 하는지는 개발자라면 금방 파악할 수 있는데,
비즈니스 맥락은 서술되지 않으면 히스토리까지 찾아야 해서 시간이 많이 걸린다.
이 챕터가 그 내용인듯 하고, 나도 그 내용에 동의한다!
4. Clarification
명확화
변경할 수 없는 코드(표준 라이브러리, 외부 라이브러리)의 모호한 부분을 읽기 쉽게 번역해주는 주석
1) 주석이 없는 경우
assertTrue(a.compareTo(b) == -1);
-1의 의미가 뭐지? a랑 b의 비교관계가 뭐지?
2) 주석이 있는 경우
assertTrue(a.compareTo(b) == -1); // a < b
아하 a가 b보다 작다는 걸 검증하는구만
근데, 주의해야 하는 점. 주석이 틀리면 답없음.
주석까지 달아놨으면 얘가 진짜 이 주석의 의미라는 거겠지? 하고 철썩같이 믿을 텐데,
알고보니까 전임 개발자 실수로 주석이 틀린거면... 원인 찾기가 진짜 어렵다.
일단 그 주석이 진실이라고 생각하고 그 부분을 배제하고 오류를 찾으려고 시도하기 쉽기 때문이다.
5. Warning of Consequences
특정 결과에 대해 경고함
예를 들어, 엄청 큰 테스트 케이스를 실행하는 함수가 있다고 하면
이를 경고하는 주석을 작성한다.
// 시간이 남아돌지 않으면 실행하지 마라.
public void _testWithReallyBigFile(){
// ...
}
6. Amplification
강조
사소해서 무시될 수 있는 중요한 부분을 강조할 때 사용한다
String listItemContent = match.group(3).trim();
// trim이 정말 중요하다. 항목이 다른 리스트로
// 인식되게 할 수 있는 시작 공백을 제거한다.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));
trim()은 자주 쓰이니까 중요하지 않게 생각하고 넘어갈 수 있음
하지만 이 코드에서는 trim()을 빼면 버그가 발생함
그래서 2줄의 구구절절 주석으로 강조함
7. Javadocs (and Their Ilk) in Public APIs
public API의 Javadocs
대부분의 언어에는 소스 코드에서 특수 주석을 추출해 문서를 생성하는 시스템이 있음
public API 사용자는
- 소스 코드를 볼 수 없거나, 보기 싫어함
- 함수의 동작을 빠르게 파악해야 함
- 엣지 케이스, 예외 상황을 알아야 함
Bad Comments
나쁜 주석
나쁜 주석은 보통 빈약한 코드의 변명이다.
또는, 불충분한 결정의 정당화를 위한 개발자의 혼잣말에 지나지 않는다.
(ㄷㄷ 빡세네)
1. Mumbling
중얼중얼...불완전하고 모호한 주석
'일단 주석은 달아야 할 것 같아서' 대충 쓴 주석은 오히려 혼란만 가중시킨다.
본문 예시가 잘 안와닿아서 claude에게 예시를 달라고 했더니 아래와 같은 결과를 줌
// 중얼거림 - 왜? 어떻게?
// 이렇게 해야 함
const result = data.map(x => x.value).filter(Boolean);
// 중얼거림 - 무슨 버그?
// 버그 수정
if (items.length > 0) {
process(items);
}
// 중얼거림 - 뭐가 중요한데?
// 중요!
const token = localStorage.getItem('auth_token');
// 중얼거림 - 왜 임시야? 언제 고칠 건데?
// 임시 코드
const userId = window.__USER_ID__ || 'anonymous';
이런 주석은 쓸바에 쓰지 말라는 뜻..
// 명확하게 개선
// API가 null을 반환할 수 있어서 falsy 값 필터링 필요
const result = data.map(x => x.value).filter(Boolean);
// 명확하게 개선
// 빈 배열에 process() 호출 시 서버에서 400 에러 반환
if (items.length > 0) {
process(items);
}
// 명확하게 개선
// 토큰 없으면 아래 API 호출에서 401 발생 - AuthGuard에서 처리됨
const token = localStorage.getItem('auth_token');
뭐가 중요한지, 뭐가 문제인지, 언제 고칠건지 '명확하게' 쓰기
2. Redundant Comments
중복된 주석
코드가 이미 말하고 있는 것을 반복하는 주석은 쓸모없고 해롭다.
그냥 코드 읽으면 이해되는 내용을 굳이 써놓을 필요가 있나? 아니다.
오히려 주석을 읽도록 유도해서 코드를 직접 이해하며 본질을 파악하는 걸 방해한다.
It is rather like a glad-handing used-car salesman
assuring you that you don’t need to look under the hood.
후드 아래를 볼 필요가 없다고 안심시키며 악수하는 중고차 세일즈맨 같다.
(아니 진짜 작가양반 표현 개빡세네)
3. Misleading Comments
오해의 소지가 있는 주석
아래는 claude가 생성해준 예제
// 오해의 소지가 있는 주석
// 사용자가 로그인할 때 홈으로 이동
function checkAuth() {
if (isLoggedIn) {
navigate('/home');
}
}
주석이 암시하는 것: 로그인 할 때 (로그인 이벤트 발생 시)
실제 동작: 이미 로그인 되어 있으면
후임자는 '아 로그인 성공하면 자동으로 이동하겠군!'이라고 생각하고 코딩하다 낭패볼 수 있음.
4. Redundancy and Imprecision
중복과 부정확
1) 완전한 중복
/**
* The processor delay for this component.
*/
protected int backgroundProcessorDelay = -1;
변수명: backgroundProcessorDelay
주석: 이 컴포넌트의 프로세서 지연
변수명과 주석이 같음. 거의 제곧내세요 지금.
주석에서 새로운 정보를 얻을 수 없어서 무쓸모.
2) 용어 혼란
// 어떤 건 "component"
The processor delay for this component.
The lifecycle event support for this component.
// 어떤 건 "Container" (대문자)
The container event listeners for this Container.
The Loader implementation with which this Container is associated.
// 어떤 건 "container" (소문자)
// (다른 주석에서)
component랑 Cotainer랑 다른가?
Container랑 container랑 뭐가 다르지?
의도인가 실수인가...
코드를 정확하게 작성해도, 주석이 부정확하면 혼란을 야기함.
그리고 독자(후임 개발자)로 하여금
'코드도 대충 짠거 아냐?'
'이 개발자가 짠 코드 믿어도 돼?'
'전체적으로 코드 퀄리티 자체가 낮은 거 아냐??'
라는 생각을 들게 할 수 있다.
안돼...누가 내 코드 보고 이렇게 생각하면 너무 슬플 것 같아...
주석의 품질 = 코드의 신뢰도
5. Mandated Comments
의무적인 주석
모든 함수에 주석 필수!라는 규칙은 코드를 어수선하게 만듦
진짜 '읽어야 되는 주석'도 지나치게 만들 수 있음
초등학교 때 강제로 일기 쓰면
'오늘은 고기를 먹었다! 행복했다.'이런 일기 나오는거랑 비슷한 맥락 아닐까
6. Journal Comments
이력 주석
변경 이력을 소스 코드에 주석으로 남기지 마라.
어차피 이런건 github이 다 해줌.
커밋 기록 보는 게 훨씬 나음.
저자는 변경 이력 기록 로그만 수십페이지인 모듈도 봤다고 함
(진짜 끔찍하다)
7. Noise Comments
/** The day of the month. */
private int dayOfMonth;
아니 변수명과 주석이 같잖슴
이것도 제곧내잖아!!!
이게 반복되면 '주석은 쓸모없다'는 게 무의식 중에 학습됨.
모든 주석을 무시하기 시작함.
코드 변경 시 주석 업데이트를 누락하기 시작함.
주석이 코드와 일치하지 않기 시작함.
새 개발자가 주석만 읽고 코드를 이해해서 코딩한 후 버그를 발생시킴
최악.
8. Scary Noise
noise comments + 복붙 실수
/** The version. */
private String version;
/** The licenceName. */
private String licenceName;
/** The version. */
private String info;
info 변수 위에 version 변수 주석이 붙어있음
복붙 실수한거지...
작성자가 코드를 꼼꼼히 작성하지 않았다는 증거임.
코드 전체의 신뢰도를 떨어뜨림
9. TODO comments
TODO 주석을 남기고, 나중에 해야지 하고 잊어버리는 경우가 많음.
몇 달, 몇 년 후에도 그대로인 걸 봄.
사실 TODO의 의미가 Don't Do 였던 것임...??
레거시에서 22년 5월 후에 제거라는 주석을 본 적 있는데
내가 그걸 발견한게 25년 8월이었음
기기괴괴...
그래서 그 자리에서 지웠음
TODO를 만나면
1) 즉시 TODO를 지우고, 코드를 완성시키거나
2) TODO가 필요없도록 요구사항을 변경하고, 설계를 개선하거나
3) 백로그(Jira ticket, Github issue, Notion board)에 넣는다
10. Use a Function or a Variable Instead of a Comment
주석 대신 함수나 변수를 사용하라
본문 예제가 잘 안와닿아서 claude로 생성함
// 주석으로 설명
// 24시간(밀리초)이 지났으면 만료
if (Date.now() - createdAt > 86400000) {
expire();
}
// 변수로 설명
const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
const elapsed = Date.now() - createdAt;
const isExpired = elapsed > MILLISECONDS_PER_DAY;
if (isExpired) {
expire();
}
주석을 쓰기 전에 코드로 표현할 수 없을지 먼저 생각하는 습관을 가지자!
11. Position Markers
만약 섹션을 나누기 위해 배너를 사용한다면...
정말 필요한 곳에서만 사용할 것!
// Actions //////////////////////////////////
이런 류의 배너는 눈에 정말 잘 뛰기 때문에 정말 중요한 구분에서만 사용할 것.
배너를 남발하면 양치기 소년처럼 그냥 무시하게 될 것...
12. Attributions and Bylines
저자 표시와 서명
어차피 github에서 누가 어떤 코드를 건드렸는지 추적한다.
pr 번호와 jira 티켓 번호도 커밋 주석에 넣고, 소스 코드에 넣지 마라.
13. Commented-Out Code
주석 처리된 코드
Few practices are as odious as commenting-out code.
Commented-out code is an abomination before nature and nature’s God.
Don’t check this stuff in!
본문이 너무 빡세서 번역하기가 두렵네요;
(대충 주석처리 코드 역겨우니까 이딴거 하지 말라는 뜻)
(흠... 저자 데려와서 우리 레거시 한 번 보게 하고 싶다)
주석처리한 본인이 아니라면, 타인은 이걸 쉽게 삭제하기 어렵다.
너무 중요하거나? 혹은 정말 지우면 안돼서 남겨둔건가? 이런 고민이 들게 만든다.
어쨌든 필요없는 사고 시간을 늘려서 리소스를 낭비하는 주범이라고 생각함.
난 그냥 주석처리된 코드 웬만하면 마주칠 때마다 그냥 시원~하게 지운다.
어차피 내가 안지우고 넘어가도 누가 딱히 살려서 쓰지 않을 게 보일정도로 무의미한 주석처리기 때문이다.
그리고, 만일 이 코드를 다시 살려야 한다고 해도 github commit history에서 찾아서 살리면 된다.
그니까 주석처리 하지말고 제발 지우자!
13. HTML Comments
주석은 코드에서 가장 읽기 쉬운 부분이어야 한다.
HTML 태그가 이 가독성을 망친다.
소스 코드 주석에 HTML 태그를 넣지 마라.
14. Nonlocal Information
비지역 정보
주석은 바로 옆의 코드만 설명해야 한다.
다른 곳에 있는 코드를 설명하지 마라.
아래는 claude가 만들어준 예시
// ❌ 비지역적 - 다른 파일의 값을 설명
/**
* API 타임아웃을 설정합니다. 기본값은 5000ms입니다.
*/
function setTimeout(ms: number) {
this.timeout = ms;
}
// 실제 기본값은 config.ts에 있음
// export const DEFAULT_TIMEOUT = 5000;
// 나중에 누가 3000으로 바꾸면? 주석은 거짓말
15. TMI
주석에 흥미롭지만 불필요한 정보를 장황하게 쓰지 마라.
아래는 calude의 예시
/*
* 디바운스 함수 구현
*
* 디바운스는 1980년대 하드웨어 버튼의 채터링 문제를 해결하기 위해
* 처음 고안되었다. 기계식 스위치가 눌릴 때 접점이 여러 번 튀면서
* 신호가 여러 번 발생하는 것을 방지하기 위해...
* 이 편지는 영국에서 최초로 시작되어...
*/
function debounce(fn: Function, delay: number) {
let timeoutId: number;
return (...args: any[]) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn(...args), delay);
};
}
/**
* 마지막 호출 후 delay(ms)가 지나야 실행
* @see https://lodash.com/docs/4.17.15#debounce
*/그냥 이정도로만 써도 충분함.
16. Unobvious Connection
주석의 목적은 스스로를 설명하지 않는 코드를 설명하는 것이다.
주석 자체가 설명이 필요하면 안된다!
/*
* 모든 픽셀을 담기에 충분히 큰 배열로 시작한다
* (필터 바이트 포함), 그리고 헤더 정보를 위한 추가 200 바이트
*/
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];
주석이 써있는데도, filter byte가 뭐지? 왜 +200이지? 이런 의문이 남음
상수로 명확하게 설명하거나, 주석을 제대로 써야 한다.
아래는 claude가 고쳐준 예시
// 상수 이름이 설명
private static final int BYTES_PER_PIXEL = 3;
private static final int FILTER_BYTE_PER_ROW = 1;
private static final int PNG_HEADER_SIZE = 200;
this.pngBytes = new byte[
((this.width + FILTER_BYTE_PER_ROW) * this.height * BYTES_PER_PIXEL)
+ PNG_HEADER_SIZE
];
17. Function Headers
짧고 명확한 함수에는 긴 설명 주석이 필요없다.
좋은 이름을 쓰는 것이 주석을 다는 것보다 낫다.
아래는 claude가 만들어준 예시
// 과한 헤더 주석
/**
* Formats a price value as a currency string.
* Takes a number and returns it formatted with
* the won symbol and thousands separators.
*
* @param price - The price value to format
* @returns The formatted price string
*/
function format(price: number): string {
return `₩${price.toLocaleString()}`;
}이런 식으로 format이라는 짧은 함수 이름 쓰고 구구절절 주석 달 바에
// 이름이 충분히 설명
function formatPriceAsWon(price: number): string {
return `₩${price.toLocaleString()}`;
}formatPriceAsWon이라는 명확한 이름을 쓰는 게 낫다.
18. Javadocs in Nonpublic Code
public API에는 javadoc이 유용하지만,
소규모 팀이 유지보수하고 nonpublic인 코드에는 오히려 단점이 많다.
| public API | nonpublic code |
| 외부 개발자가 사용 | 팀 내부만 사용 |
| 소스 코드 못 볼 수도 있음 | 언제든 소스 볼 수 있음 |
| 문서가 유일한 가이드 | 코드가 가이드 |
| Javadoc 필수 | Javadoc 불필요 |
결론
The only language of “truth” at our disposal is our code.
우리가 가진 유일한 진실의 언어는 코드이다.
코드는 실행한 그대로 결과를 뱉기 때문에 거짓말을 하지 못한다.
하지만, 때로는 이 코드만으로 완전한 설명을 하기 어려울 때가 있다.
이 때문에 때때로 주석을 써야 할 때가 있다.
하지만, 주석은 잘못 쓰면 위에서 주구장창 서술한 문제점이 발생할 수 있으니,
조심해서 작성하자~
(개인적인 견해: 저자가 나쁜 주석에 데인 경험이 많은 듯 하다.)
요약
| 좋은 주석 | 나쁜 주석 |
| 라이선스 주석 | 중복 주석(코드 내용과 동일) |
| 의도 설명(왜 이런 결정했는지) | 의미 불분명 주석(mumbling) |
| 결과 경고(스레드 안정성 등) | 변경 이력 주석(git이 대체 가능) |
| 정규표현식에 대한 서술(한 번에 이해하기 힘든 코드) | TODO 주석(쓰기만 하고 어차피 미룰거라) |
| public API의 Javadoc | 주석 처리된 코드 |
| 저자 서명, 위치 표시 배너 |
1차 작성
작성 시작: 2025.12.19. 09:21
작성 종료: 2025.12.19. 10:08
2차 작성
작성 시작: 2025.12.20. 21:56
작성 종료: 2025.12.21. 00:45

'개발자 강화 > 개발 독서' 카테고리의 다른 글
| [Clean Code 2판] 1-7: Clean Functions (1) | 2026.01.06 |
|---|---|
| [Clean Code 2판] 1-6: Formatting (1) | 2025.12.25 |
| [Clean Code 2판] 1-4: Meaningful Names (0) | 2025.12.19 |
| [Clean Code 2판] 1-3: First Principles (1) | 2025.12.18 |
| [Clean Code 2판] 1-2: Clean That Code! (2) | 2025.12.16 |