4장. 주석
4장에선 주석에 대한 의견, 좋은 주석과 나쁜 주석에 대해 설명한다.
주석은 실패를 만회하기 위해 사용한다.
주석은 기껏해야 필요악이다.
주석은 너무 자주 거짓말을 한다.
부정확한 주석은 아예 없는 주석보다 더 나쁘다.
위의 내용은 책 내용의 일부인데 주석을 좋아하지 않는 것처럼 보인다. 그 이유를 알아보자.
주석은 나쁜 코드를 보완하지 못한다
코드에 주석을 추가하는 일반적인 이유는 코드 품질이 나쁘기 때문이며 주석을 다는 것이 아니라 코드를 정리해야 한다고 한다.
코드로 의도를 표현하라!
주석으로 의도를 표현하는 경우
// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if ((employee.flags && HOURLY_FLAG)) && (employee.age > 65)
코드로 의도를 표현하는 경우
if (employee.isEligibleForFullBenefits())
주석으로 설명하려는 것을 코드로 표현하는 것이 더 좋은 수단이라고 한다.
좋은 주석
어떤 주석은 필요하거나 유익하며 그 경우를 설명한다.
1. 법적인 주석
저작권 정보나 소유권 정보 등 회사가 정립한 표준에 맞춰 법적인 이유로 넣은 주석
2. 정보를 제공하는 주석
기본적인 정보를 제공하는 주석
// 테스트 중인 Responder 인스턴스를 반환한다.
protected abstract Responder responderInstance();
위 주석은 추상 메서드가 반환할 값을 설명하고 이는 유용하다.
다만, 아래와 같이 구현한다면 주석이 필요 없어진다.
protected abstract Responder responderBeingTested();
3. 의도를 설명하는 주석
코드를 이해하도록 도와주는 것을 넘어 이처럼 구현한 의도까지 설명하는 주석
public void testConcurrentAddWidgets() throws Exception {
WidgetBuilder WidgetBuilder = new ...
String text = ...
...
// 스레드를 대량 생성하는 방법으로 어떻게든 경쟁 조건을 만들려 시도한다.
for (int i = 0; i < 25000; i++) {
WidgetBuilderThread WidgetBuilderThread = ...
Thread thread = ...
thread.start();
}
assertEquals(false, failFlag.get());
}
4. 의미를 명료하게 밝히는 주석
라이브러리 활용 등의 이유로 코드를 변경할 수 없을 때 의미를 명료히 밝히는 주석
assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b
assertTrue(a.compareTo(b) == -1); // a < b
assertTrue(a.compareTo(b) == 1); // a > b
단, 이 경우 그릇된 주석을 달 위험이 높고 주석이 올바른지 검증하기 쉽지 않기에 주의해야 한다고 한다.
5. 결과를 경고하는 주석
다른 프로그래머에게 결과를 경고할 목적인 주석
// 여유 시간이 충분하지 않다면 실행하지 마십시오.
public void testWithReallyBigFile() {
...
}
6. TODO 주석
앞으로 할 일을 알리는 주석
IDE에서 TODO 주석을 찾아주는 기능이 있어서 이를 잊을 염려가 없고 구현 우선순위가 나중인 경우 활용하면 좋으나, TODO가 너무 많은 것은 바람직하지 않기에 주기적으로 점검해 없애도 되는 것은 없애는 것이 좋다고 한다.
7. 중요성을 강조하는 주석
대수롭지 않다고 여겨질 것의 중요성을 강조하기 위한 주석
String listItemContent = match.group(3).trim();
// 여기서 trim은 정말 중요하다. trim 함수는 문자열에서 시작 공백을 제거한다.
// 문자열에 시작 공백이 있으면 다른 문자열로 인식되기 때문이다.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end));
8. 공개 API에서 Javadocs
API를 잘 설명하는 경우. 단, Javadocs 또한 그릇된 정보를 전달할 가능성이 있다는 것을 무시하면 안 된다고 한다.
나쁜 주석
허술한 코드를 지탱, 변명, 합리화하는 대부분의 주석에 대해 설명한다.
1. 주절거리는 주석
특별한 이유 없이 마지못해 다는 주석
주석을 달기로 결정했으면 충분히 시간을 들여 최고의 주석을 달아야 한다고 주장한다.
예를 들어 아래 코드는 주석을 제대로 달았으면 상당히 유용했을 것이지만 정확한 의미를 알 수 없어 아쉬운 경우이다.
public void loadProperties() {
try {
String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
FileInputStream propertiesStream = new FileInputStream(propertiesPath);
loadedProperties.load(propertiesStream);
} catch(IOException e) {
// 속성 파일이 없다면 기본값을 모두 메모리로 읽어 들였다는 의미다.
}
}
2. 같은 이야기를 중복하는 주석
코드 내용을 그대로 중복한 주석
아래는 주석 내용과 코드 내용이 동일한 경우로 자칫 코드보다 주석을 읽는 시간이 더 오래 걸리고 주석의 목적을 알 수 없는 경우라고 한다.
// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
if(!closed) {
wait(timeoutMillis);
if(!closed)
throw new Exception("MockResponseSender could not be closed");
}
}
코드를 정당화하는 것도 아니고
더 많은 정보를 주는 것도 아니고
의도를 설명하는 거도 아니고
코드보다 읽기 쉽지도 않기에 이런 주석은 없는 것이 낫다는 설명이다.
3. 오해할 여지가 있는 주석
의도는 좋았으나 프로그래머가 딱 맞을 정도로 엄밀하게 달지 못한 주석
위의 코드가 좋은 예로, 실제로는 this.closed가 true로 변하는 순간 반환되는 것이 아니라 this.closed가 true여야 반환된다.
즉, 주석이 약간 잘못된 정보를 주는 것이다.
이는 해당 메서드를 사용하는 프로그래머가 원하는 대로 동작하지 않는 이유를 찾기 매우 어려울 것이라고 설명한다.
4. 의무적으로 다는 주석
모든 함수, 모든 변수에 다는 주석
이러한 주석은 코드만 헷갈리게 만들며 거짓말할 가능성을 높이고 잘못된 정보를 제공할 여지만 만든다고 설명한다.
5. 이력을 기록하는 주석
모듈을 편집할 때마다 첫머리에 추가하는 주석
마치 로그처럼 모듈의 변경 내용을 설명하는 주석으로 현재는 소스 코드 관리 시스템이 있기에 이러한 주석은 필요 없다고 주장한다.
6. 있으나 마나 한 주석
너무 당연한 사실을 언급해 새로운 정보를 제공하지 못하는 주석
이러한 주석은 개발자가 주석을 무시하는 습관에 빠지기에 쓸모가 없다고 설명한다.
아래가 그 예시이다.
/**
* 기본 생성자
*/
protected AnnualDateRule() {
}
/**
* 월 중 일자
*/
private int dayOfMonth;
7. 무서운 잡음
단지 문서를 제공해야 한다는 잘못된 욕심으로 탄생한 잡음(주석)
/** The name. */
private String name;
/** The version */
private String version;
- 함수나 변수로 표현할 수 있다면 주석을 달지 말라: 가급적 주석이 필요하지 않도록 코드를 개선하는 편이 좋다.
// 주석이 있는 경우
// 전역 목록 <smodule>에 속하는 모듈이 우리가 속한 하위 시스템에 의존하는가?
if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
// 주석이 없는 경우
ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if (moduleDependees.contains(ourSubSystem));
8. 위치를 표시하는 주석
소스 파일에서 특정 위치를 표시하는 주석
// Actions ////////////////////////////////////
이러한 코드는 특정 기능을 모아놓으면 유용한 경우도 있지만,
일반적으로 가독성만 낮추므로 제거하는 것이 좋다고 설명한다.(특히 뒷부분의 슬래시(/))
가끔 사용하면 눈에 띄기에 아주 드물게 사용하는 편이 좋다고 주장한다.
9. 닫는 괄호에 다는 주석
만약 닫는 괄호에 아래처럼 주석이 필요하다고 느껴진다면 함수 크기를 줄이려고 노력해야 한다고 한다.
try {
...
while() {
...
} // while
} // try
10. 공로를 돌리거나 저자를 표시하는 주석
소스 코드 관리 시스템은 누가 언제 무엇을 추가했는지를 모두 알고 있기에 이러한 주석은 전혀 필요가 없다고 주장한다.
/* ~~가 추가함 */
11. 주석으로 처리한 코드
이러한 코드는 이유가 있어 남겨놓았다고 판단해 사람들이 지우기를 주저하며 점차 쌓여간다고 주장한다.
InputStreamResponse response = ...;
response.setBody(formatter.getResultStream(), ...);
// InputStreamresultStream = ...;
// StreamReader reader = ...;
// response.setContent(...);
12. HTML 주석
소스코드에 있는 HTML 주석
이러한 주석은 편집기/IDE에서조차 읽기가 어렵다고 한다.
/**
* 적합성 테스트를 수행하기 위한 과업
* 이 과업은 적합성 테스트를 수행해 겨로가를 출력한다.
* <p/>
* <pre>
* < taskdef name="...
* ...
*
* </pre>
*/
13. 전역 정보
코드 일부에 달면서 시스템의 전반적인 정보를 기술한 주석
14. 너무 많은 정보
관련 없는 정보를 장황하게 늘어놓은 주석
15. 모호한 관계
설명하고자 하는 코드와의 관계가 명백하지 않은 주석
필터 바이트는 무엇인지, +1은 무엇인지, *3은 무엇인지, 서로는 무슨 관계인지에 대한 설명이 없다.
주석이 다시 설명을 요구하므로 잘못된 주석이라는 설명이다.
/**
* 모든 픽셀을 담을 만큼 충분한 배열로 시작한다.(여기에 필터 바이트를 더한다).
* 그리고 헤더 정보를 위해 200바이트를 더한다.
*/
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];
16. 함수 헤더
짧고 한 가지만 수행하며 이름을 잘 붙이면 함수 헤더가 필요 없다는 설명이다.
17. 비공개 코드에서 Javadocs
공개 API는 Javadocs가 유용하지만 공개하지 않을 코드라면 Javadocs는 쓸모가 없으며 되려 포맷을 지켜야 해 코드를 보기에 산만해질 뿐이라고 설명한다.
느낀 점
돌이켜보면 그동안 코드를 구현하면서 주석을 활용한 경우가 거의 없었던 것 같다. 다른 사람과의 협업이 적었던 것이 그 이유라고 생각하는데 어떤 상황에 주석을 활용하는 것이 좋은지, 그 반대는 어떤 경우인지를 잘 알 수 있었다.
읽고 보면 당연하게 고개가 끄덕여지는 내용들이었기에 어려운 부분은 없었다.
주석을 쉬운 마음으로 작성하지 말아야겠다는 생각이 든다.
'OOP & CleanCode' 카테고리의 다른 글
CleanCode - 6. 객체와 자료 구조 (1) | 2024.11.30 |
---|---|
CleanCode - 5. 형식 맞추기 (1) | 2024.01.11 |
CleanCode - 3. 함수 (1) | 2023.12.23 |
CleanCode - 1. 깨끗한 코드, 2. 의미 있는 이름 (2) | 2023.10.21 |