이 장을 읽기 전에
이 장은 05~06장에서 다룬 함수 분해와 네이밍을 전제로 한다 — 주석 없이도 코드가 의도를 전달하려면 먼저 이름과 함수 구조가 명확해야 하기 때문이다. 특정 언어의 문서화 도구(Javadoc, docstring, JSDoc) 사용 경험은 필요 없다. 이 장은 주석의 필요성과 함정을 다루며, 코드 자체의 형식(들여쓰기, 줄 길이)은 09장에서 다룬다.
| 수준 | 읽을 부분 | 핵심 목표 |
|---|---|---|
| 입문자 | “주석의 역설"부터 “나쁜 주석"까지 | 어떤 주석이 코드를 돕고 어떤 주석이 해가 되는지 구분한다 |
| 실무자 | “판단 기준”, “비판적 시각” | 공개 API 문서화와 내부 구현 주석을 다른 기준으로 다룰 수 있다 |
주석의 역설
주석은 코드를 보완하기 위해 존재하지만, 역설적으로 주석이 필요하다는 사실 자체가 코드의 표현력이 부족하다는 신호일 때가 많다. 변수와 함수 이름만으로 의도를 전달할 수 있다면, 그 의도를 다시 자연어로 풀어 쓴 주석은 정보를 중복해서 전달할 뿐이다. 더 심각한 문제는 주석이 코드와 별도로 관리된다는 점이다. 코드는 리팩토링되고 로직이 바뀌지만, 주석은 그 변경에 맞춰 자동으로 갱신되지 않는다. 그 결과 시간이 지날수록 주석과 실제 코드 사이에 괴리가 생기고, 오래된 주석은 정보를 주기는커녕 독자를 잘못된 방향으로 이끄는 거짓말이 된다.
이 관찰은 “주석을 절대 쓰지 말라"는 결론으로 이어지지 않는다. 대신 “코드로 표현할 수 있는 것은 코드로 표현하고, 코드로 표현할 수 없는 것만 주석으로 남긴다"는 원칙으로 이어진다. 이 구분을 명확히 하는 것이 이 장의 핵심이다.
좋은 주석: 코드가 담을 수 없는 정보
일부 정보는 코드 문법 자체로는 표현할 수 없어서 주석이 유일한 전달 수단이 된다. 대표적으로 라이선스나 저작권처럼 법적으로 요구되는 법적 주석, 정규식처럼 그 자체로는 의도를 알기 어려운 코드에 붙이는 의도를 설명하는 주석, 그리고 다른 개발자에게 위험을 알리는 경고성 주석이 있다.
| |
정규식 주석이 좋은 이유는, 정규식 자체를 아무리 잘 읽어도 “왜 RFC 5322 전체를 검증하지 않기로 했는가"라는 설계 결정의 이유는 코드에서 드러나지 않기 때문이다. 이처럼 좋은 주석은 대개 “무엇을 하는가(what)“가 아니라 “왜 이렇게 했는가(why)“를 설명한다. TODO 주석도 “지금은 처리하지 않지만 이유가 있어서 남겨둔 작업"이라는 의도를 전달한다면 유효하지만, 이슈 트래커 없이 방치된 TODO는 시간이 지나면 신뢰할 수 없는 잡음이 된다.
공개 API의 경우, 사용자가 소스 코드를 열어보지 않고도 계약(파라미터, 반환값, 예외 조건)을 이해해야 하므로 Javadoc, Python docstring, JSDoc 같은 구조화된 문서 주석은 예외적으로 권장된다. 이는 구현 세부사항을 설명하는 인라인 주석과는 성격이 다르다 — 문서 주석은 “이 함수를 어떻게 쓰는가"라는, 코드 내부를 읽지 않고는 알 수 없는 계약을 전달한다.
| |
나쁜 주석: 코드가 이미 말하는 것을 반복하기
가장 흔한 나쁜 주석은 코드를 그대로 자연어로 옮긴 중복 주석이다.
| |
이런 주석은 코드를 읽는 속도를 늦출 뿐 아니라, 코드가 바뀔 때 갱신되지 않아 결국 오해를 낳는 주석으로 변질된다. 또 다른 흔한 안티패턴은 버전 관리 시스템이 이미 담당하는 정보를 주석으로 남기는 이력 기록(저널) 주석이다. // 2019-03-01 김철수: 버그 수정, // 2021-07-12 이영희: 리팩토링 같은 주석은 git blame, git log가 훨씬 정확하고 검색 가능한 형태로 같은 정보를 제공하므로 코드에 남길 이유가 없다. 주석 처리된 채 방치된 코드(// oldImplementation();)도 마찬가지로, “이 코드가 왜 아직 여기 있는지” 아무도 확신할 수 없게 되어 결국 아무도 지우지 못하는 죽은 코드로 남는다.
코드로 의도를 표현하기
주석이 필요하다고 느껴지는 지점은 대개 코드를 개선할 신호다. 아래 두 코드는 같은 조건을 표현하지만, 왼쪽은 주석에 의존하고 오른쪽은 함수 이름 자체가 조건을 설명한다.
| |
isEligibleForFullBenefits()라는 이름은 조건의 세부 로직(어떤 플래그, 어떤 나이 기준)을 감추면서도 “무엇을 판단하는지"는 정확히 전달한다. 이 접근은 03장에서 다룬 네이밍 원칙과 05장에서 다룬 함수 추출 기법을 그대로 활용한 것이며, 결과적으로 주석 하나를 없애면서 동시에 조건의 의미를 재사용 가능한 단위로 승격시킨다.
흔한 오개념
**“주석이 많을수록 문서화가 잘 된 코드다”**는 오해가 매우 흔하다. 실제로는 주석의 밀도와 코드 품질은 반비례하는 경향이 있다 — 코드가 스스로 의도를 전달하지 못할수록 이를 보완하기 위한 주석이 늘어나기 때문이다. 좋은 코드베이스는 주석이 적은 것이 아니라, 꼭 필요한 곳에만 정확한 주석이 있는 코드베이스다.
**“모든 함수에 문서 주석(Docstring/Javadoc)을 달아야 한다”**는 규칙도 맥락 없이 적용하면 문제가 된다. private 메서드처럼 그 클래스 내부에서만 쓰이고 이름 자체로 의도가 명확한 함수에 형식적인 문서 주석을 강제하면, 정작 중요한 공개 API의 문서와 사소한 내부 헬퍼의 문서가 동일한 무게로 취급되어 독자의 주의가 분산된다.
판단 기준
주석을 쓰기 전에 스스로 물어야 할 질문은 “이 정보를 이름이나 타입, 함수 구조로 옮길 수 있는가"이다. 옮길 수 있다면 코드를 개선하고 주석은 지운다. 옮길 수 없는 정보—법적 요구사항, 외부 시스템의 제약, “왜 이 방식을 선택하지 않았는가"라는 설계 결정의 배경—만 주석으로 남긴다. 공개 API 경계에서는 이 기준이 완화된다. 사용자가 구현을 읽지 않고도 계약을 이해해야 하므로, 파라미터 제약이나 예외 조건처럼 시그니처만으로 전달되지 않는 정보는 구조화된 문서 주석으로 명시하는 것이 정당하다.
비판적 시각
“주석은 실패다"라는 표어는 종종 극단적으로 받아들여져 “주석을 아예 쓰지 말라"는 규칙으로 오용되기도 한다. 하지만 이는 원저의 취지와 다르다. 복잡한 알고리즘(예: 특정 논문을 구현한 최적화 기법)이나 비직관적인 성능 트레이드오프는, 아무리 이름을 잘 지어도 “왜"에 해당하는 배경 지식을 코드 문법만으로 전달할 수 없다. 이런 경우 주석을 생략하는 것은 오히려 다음 유지보수자에게 불필요한 재조사 비용을 떠넘기는 것이다. 또한 Python의 PEP 257이나 JSDoc처럼 언어 생태계가 공식적으로 문서 주석 표준을 정의하고 이를 기반으로 IDE 자동완성·API 문서 사이트를 생성하는 관행을 고려하면, “주석 없는 코드가 이상적"이라는 해석은 공개 라이브러리 개발에는 맞지 않는다. 핵심은 주석의 총량이 아니라 주석이 대체 불가능한 정보를 담고 있는가이다.
다음 장에서는
08장: 주석 걷어내기 실습에서는 나쁜 주석으로 뒤덮인 코드를 이 장의 기준에 따라 정리해 본다.
평가 기준
- 좋은 주석(법적, 의도 설명, 경고)과 나쁜 주석(중복, 이력 기록, 죽은 코드)을 예제로 구분할 수 있다.
- 주석에 의존하던 조건문을 함수 추출로 대체할 수 있다.
- 공개 API 문서 주석과 내부 구현 주석에 다른 기준을 적용해야 하는 이유를 설명할 수 있다.
- “주석은 무조건 나쁘다"는 극단적 해석이 왜 틀렸는지 반박할 수 있다.
참고 및 출처
- Martin, R. C. (2008). Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall. 4장.
- PEP 257 — Docstring Conventions
- JSDoc 공식 문서
![[Rust] Comprehensive Rust 무료 강의 정리 및 코스 구조](/post/2022-12-30-comprehensive-rust/wordcloud_hu_d1420ff38434cdb6.webp)
![[Hardware] LattePanda Alpha에 Ubuntu 16.04 LTS 설치 가이드](/post/2018-12-06-install-ubuntu-16.04-on-lattepanda/wordcloud_hu_fc536f8de2cbd4bf.webp)
![[SoftwareTesting] 소스 코드 테스트 커버리지 메트릭과 활용](/post/2024-08-19-test-coverage/wordcloud_hu_5f670d08f61abc22.webp)
![[Tutorial] Learn Prompting - 프롬프트 엔지니어링 무료 가이드 정리](/post/2022-12-30-learn-prompting/wordcloud_hu_6a9d105de4834753.webp)