![[Clean Code] 4. 주석](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYQ3Ho%2FbtsDRbaQ1Pg%2FUdDWxpPC6GrS5KVVtzIJtK%2Fimg.png)
본 게시글은 도서, Clean Code를 읽고 정리한 글입니다.
Clean Code(클린 코드) | 로버트 C. 마틴 - 교보문고
Clean Code(클린 코드) | 프로그래머, 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가에게 도움이 될 더 나은 코드를 만드는 책『Clean Code(클린 코드)』은 오브젝트 멘토(Object Mentor)의
product.kyobobook.co.kr
0. 프롤로그
나쁜 코드에 주석을 달지 마라. 새로 짜라.
잘달린 주석은 물론 그 어떤 정보보다 유용할 수 있다. 그런데 곰곰히 생각해보면,
주석이 필요한 상황을 만드는 것 자체가 문제가 아닌가?
우리는 코드로 내 의도를 100% 표현하지 못해서, 실패를 만회하기 위해서 주석을 사용하게 된다. 결국, 주석은 실패를 의미하게 되기도 한다.
또한 주석의 문제점은, 주석이 거짓말을 한다는 것이다.
코드는 정말 많이 유지 보수되고, 변화한다.
그런데 그에 반해 주석은 실질적으로 유지보수되고 변화되기 어렵다.
결국 주석이 코드를 따라가지 못하게 되어 코드에서 분리된다.
따라서 주석이 필요없을 만큼의 깔끔한 코드를 짜는 것을 목표로 두는 것이 훨씬 낫다.
결국 진실은 코드에 존재한다.
1. 주석은 나쁜 코드를 보완하지 못한다.
지저분한 코드를 보완하려고 주석을 쓰지 마라.
주석이 필요 없을 만큼의 깔끔하고 표현력이 풍부한 코드를 작성하는데 에너지를 쏟아내라.
2. 코드로 의도를 표현하라!
분명 코드만으로 의도를 설명하기 어려울 때도 있다.
// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if ((employee.flags & HOURLY_FLAG) && (employee.age > 65))
...
이런 코드를 보면 분명 주석이 필요해보이긴 한다. 직원의 나이가 65세 초과일때는 그렇다 쳐도 앞 조건은 코드를 처음 보는 사람 입장에서는 이해하기 분명 어렵다.
그런데 이 코드를 주석 대신 아래처럼 바꾼다면?
if(employee.isEligibleForFullBenefits())
...
그냥 함수 하나로 뺐을 뿐인데, 주석이 없어도 이해를 할 수 있다.
이처럼 많은 경우에 주석으로 달려고 하는 설명을 함수 이름으로서 해결할 수 있다.
3. 좋은 주석
가장 좋은 것은 주석을 달지 않는것이라고 계속 말해왔지만,
그럼에도 불구하고 어쩔 수 없이 달아야하는 주석이 있다.
그럴 땐 좋은 주석을 달아야 한다.
법적인 주석
소스 파일 처음에 주석으로 들어가는 저작권 정보나 소유권 정보의 경우, 필요하다.
정보를 제공하는 주석
때론 기본적인 정보를 주석으로 제공하면 편리할 때가 있다.
// kk:mm:ss EEE, MMM dd, yyyy 형식
Pattern timeMatcher = Pattern.compile(
"\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*"
);
위 코드의 주석은 코드에서 사용한 정규표현식이 시각과 날짜를 뜻한다고 설명한다.
위 코드도 이왕이면 시각과 날짜를 변환하는 클래스를 만들어서 코드를 옮기면 더 깔끔해질 것이다.
의도를 설명하는 주석
가끔 주석은 이해를 돕는 수준을 넘어서서, 결정의 의도까지 설명한다.
public void testConcurrentAddWidgets() throws Exception {
WidgetBuilder widgetBuilder = new WidgetBuilder(new Class[]{BoldWidget.class});
String text = "'''bold text'''";
PatternWidget parent = new BoldWidget(new MockWidgetRoot(), text);
AtomicBoolean failFlag = new AtomicBoolean();
fallFlag.set(false);
// 스레드를 대량 생성하는 방법을 통해 어떻게든 경쟁 조건을 만들려고 시도
for(int i=0; i<2500; i++) {
WidgetBuilderThread widgetBuilderThread = new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
Thread thread = new Thread(widgetBuilderThread);
thread.start();
}
assertEquals(false, failFlag.get());
}
위 코드처럼, 개발자의 의도를 강하게 표현하는 방식으로도 주석을 사용할 수 있다.
의미를 명료하게 밝히는 주석
인수나 반환값이 표준 라이브러리나 변경하지 못하는 코드에 속한다면, 해당 인수나 반환값을 더 명료하게 바꿀 수 없으므로 주석이 유용할 수 있다.
public void testCompareTo() throws Exception {
WikiPagePath a = PathParser.parse("pageA");
WikiPagePath b = PathParser.parse("pageB");
assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b
}
이처럼 compareTo() 라는 변경할 수 없는 표준 라이브러리의 코드에 속할 때 주석이 유용할 수 있다.
그러나 이 주석이 정확하다는 보장을 할 수 없어 위험성은 높은 편이다.
결과를 경고하는 주석
다른 프로그래머에게 결과를 경고하는 용도로써 주석을 사용할 수 있다.
public static SimpleDateFormat makeStandardHttpDateFormat() {
// SimpleDateFormat은 스레드에 안전하지 못하다.
// 따라서 각 인스턴스를 독립적으로 생성해야 한다.
SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
return df;
}
위 코드는 다른 개발자에게 경고를 남김으로써 좋은 주석이다.
TODO 주석
앞으로의 할 일을 주석으로 남기는 경우도 있다.
// TODO-MdM 현재 필요 X
// 체크아웃 모델을 도입하면 함수가 필요 없어진다.
protected VersionInfo makeVersion() throws Exception {
return null;
}
TODO 주석은 프로그래머가 필요하다고는 느끼지만, 당장 구현하지 못할 때 작성한다.
대신 주기적으로 TODO 주석을 점검하여 관리해야 한다.
중요성을 강조하는 주석
정말정말 중요한 부분일 때 중요성을 강조하기 위해서 쓰기도 한다.
{
// 여기서 trim은 정말 중요하다. 문자열에서 시작 공백을 제거한다.
// 문자열에 시작 공백이 있으면 다른 문자열로 인식되기 때문.
String listItemContent = match.group(3).trim();
new ListItemWidget(this, listItemContent, this.level+1);
return buildList(text.subString(match.end()));
}
공개 API에서의 Javadocs
설명이 잘 되어 있는 공개 API는 유용한데, 대표적으로 표준 자바 라이브러리에서 사용한 Javadocs다.
대신 Javadocs도 마찬가지로 잘못된 정보를 전달할 가능성이 있다.
4. 나쁜 주석
사실 위 좋은 주석은 정말 일부분일 뿐이고, 대부분의 주석은 나쁜 주석에 속한다.
주절거리는 주석
특별한 이유 없이 의무감이나 명령으로 인해 작성하는 주석은 최악이다.
주석을 달 때는 충분한 시간을 들여 정성스럽게 달아야 한다.
같은 이야기를 중복하는 주석
// this.closed가 true일 때 반환하는 유틸리티 메서드
// 타임아웃에 도달하면 예외를 던진다
public synchronized void waitForClose(final long timeoutMillis) throws Exception {
if(!closed) {
wait(timeoutMillis);
if(!closed)
throw new Exception("MockResponserSender could not be closed");
}
}
위 코드에서 주석은 코드 이상의 의미를 갖지 못한다.
즉, 코드만 읽어도 나오는 정보를 억지로 이어 붙여, 괜히 코드의 해석 시간을 늘릴 뿐이다.
오해할 여지가 있는 주석
의도는 좋았으나, 그 좋은 의도의 주석을 작성하는 프로그래머가 딱 알맞게 작성하지 못하여 그 의도에 맞지 않는 오해할 여지를 주는 주석을 달 수 있다.
주석에 담긴 아주 약간의 잘못된 정보로 인해 잘못된 결과가 발생하고, 그건 또 찾기도 어렵다. 조심해야한다.
의무적으로 작성하는 주석
굳이 모든 함수에 Javadocs와 같은 주석을 달 필요가 전혀 없다.
혼동과 무질서를 초래한다.
있으나 마나 한 주석
너무 당연한 사실을 언급하면서 뻔한 내용을 다시 말하는 주석이 있다.
/**
* 기본 생성자
*/
protected AnnualDateRule() {}
이 같은 주석은 쓸모 없을 뿐만 아니라, 개발자가 주석을 무시하는 습관을 형성하게 될 수 있다.
위치를 표시하는 주석
...
// Actions ---------------------------------------------
...
뭔가를 쉽게 찾기 위해서 위치를 표시하는 것처럼 해놓은 이 주석(주석도 아니다 이건)
가독성만 낮춘다.
닫는 괄호에 작성하는 주석
중첩이 심하고 장황한 함수라면 의미가 있을 수 있더라도,
클린 코드를 작성하다보면 결국 작고 캡슐화된 함수가 많아질 것이다.
이러한 코드에는 굳이 닫는 괄호에 표시할 필요가 없다.
공로를 돌리거나, 저자를 표시하는 주석
이런 정보는 소스 코드 관리 시스템을 활용하자.
주석으로 처리한 코드
개발한 사람이 깜빡하고 이렇게 내두면 다른 개발자가 보기엔 지우면 안되는 주석같아보인다.
전역 정보
주석을 달 때는 그 주석과 관련된 코드 근처에만 작성하는 것이 옳다.
코드 일부에 시스템 전반적인 정보를 작성하지 말고, 코드 일부와 관련된 주석을 제일 위해 마치 전역에 사용되는 내용처럼 작성해놓지 말라.
'Software Engineering > Clean Code' 카테고리의 다른 글
[Clean Code] 7. 오류 처리 (0) | 2024.02.19 |
---|---|
[Clean Code] 6. 객체와 자료 구조 (1) | 2024.01.25 |
[Clean Code] 5. 형식 맞추기 (0) | 2024.01.24 |
[Clean Code] 3. 함수 (1) | 2023.11.27 |
[Clean Code] 2. 의미 있는 이름 (0) | 2023.11.22 |
개발자가 되고 싶어요.