다음은 로버트 C. 마틴 Clean Code(클린 코드)를 읽고, 정리한 내용입니다.
나쁜 코드에 주석을 달지 마라.
새로 짜라.
- 브라이언 W.커니핸, P.J. 플라우거 -
주석은 필요악
주석은 '순수하게 선하지' 못하다. 사실 상 주석은 기껏해야 필요악이다.
프로그래밍 언어 자체가 표현력이 풍부하다면, 아니 우리에게 프로그래밍 언어를 치밀하게 사용해 의도를 표현할 능력이 있다면, 주석은 거의 필요하지 않으리라. 아니, 전혀 필요하지 않으리라.
우리는 코드로 의도를 표현하지 못해, 그러니까 실패를 만회하기 위해 주석을 사용한다. 여기서 내가 실패라는 단어를 썼다는 사실에 주목한다. 진심이다. 주석은 언제나 실패를 의미한다. 때때로 주석 없이는 자신을 표현할 방법을 찾기 못해 할 수 없이 주석을 사용한다. 그래서 주석은 반겨맞을 손님이 아니다.
그러므로 주석이 필요한 상황에 처하면 곰곰히 생각하기를 바란다. 상황을 역전해 코드로 의도를 표현할 방법은 없을까? 코드로 의도를 표현할때마다 스스로를 칭찬해준다. 주석을 달때마다 자신에게 표현력이 없다는 사실을 푸념해야 마땅하다.
주석을 무시하는 이유
내가 이렇듯 주석을 무시하는 이유가 무엇이냐고? 거짓말을 하니까. 항상도 아니고 고의도 아니지만 너무 자주 거짓말을 하니까. 주석은 오래될 수록 코드에서 멀어진다. 오래될 수록 완전히 그릇될 가능성도 커진다. 이유는 단순하다.
프로그래머들이 주석을 유지하고 보수하기란 현실적으로 불가능하니까.
진실은 한곳에만 존재한다. 바로 코드다. 코드만이 자기가 하는 일을 진실되게 말한다. 코드만이 정확한 정보를 제공하는 유일한 출처다. 그러므로 우리는 간혹 필요할지라도 주석을 가능한 줄이도록 꾸준히 노력해야 한다.
코드로 의도를 표현하라!
다음 코드 예제 2개를 살펴보자. 어느쪽이 더 나은가?
// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다
if((employee.flags & HOURLY_FLAG) &&
(employee.age > 65))
다음 코드는 어떤가?
if(employee.isEligibleForFullBenefits())
몇초만 더 생각하면 코드로 대다수 의도를 표현할 수 있다. 많은 경우 주석으로 달려는 설명을 함수로 만들어 표현해도 충분하다.
나쁜 주석
같은 이야기를 중복하는 주석
헤더에 달린 주석이 같은 코드 내용을 그대로 중복한다. 자칫하면 코드보다 주석을 읽는 시간이 더 오래걸린다.
// this.closed가 true로 변할때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다
public synchronized void waitForClose(final long timeoutMillis){
thorws Exception{
if(!closed)
{
wait(timeoutMillis);
if(!closed){
throw new Exception("MockResponseSender could not be closed");
}
}
}
코드보다 읽기 가 쉽지도 않다. 실제로 코드보다 부정확해 독자가 함수를 대충 이해하고 넘어가게 만든다. 엔진후드를 열어볼 필요가 없다면 고객에게 아양떠는 중고차 판매원과 비슷하다.
오해할 여지가 있는 주석
위의 코드의 주석이 어째서 오해의 여지가 있는지 알겠는가? this.closed가 true로 변하는 순간에 메서드는 반환되지 않는다. this.closed가 true여야 메서드가 반환된다. 아니면 무조건 타임아웃을 기다렸다가 그래도 this.closed가 true가 아니면 예외를 던진다.
(코드보다 읽기 어려운) 주석에 담긴 '살짝 잘못된 정보'로 인해 this.closed가 true로 변하는 순간에 함수가 반환되리라는 생각으로 어느 프로그래머가 자기 코드가 굼벨리 기어가듯 돌아가는 이유를 찾느라 골머리를 앓는다.
있으나 마나 한 주석
때때로 있으나 마나 한 주석을 접한다. 쉽게 말해, 너무 당연한 사실을 언급하며 새로운 정보를 제공하지 못하는 주석이다.
/**
* 기본 생성자
*/
protected AnnualDateRule(){
}
/*** 월 중 일자 */
private int dayOfMonth;
/**
* 월 중 일자를 반환한다
*
* @return 월 중 일자
*/
public int gerDayOfMonth(){
return dayOfMonth;
}
위와 같은 주석은 지나친 참견이라 개발자가 주석을 무시하는 습관에 빠진다. 코드를 읽으며 자동으로 주석을 건너 뛴다. 결국은 코드가 바뀌면서 주석은 거짓말로 변한다.
주석으로 처리한 코드
주석으로 처리한 코드만큼 밉살스러운 관행도 드물다. 주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다. 이유가 있어 남겨놓았으리라고, 중요하니까 지우면 안된다고 생각한다.
좋은 주석
어떤 주석은 필요하거나 유익하다. 하지만 명심하기를 바란다. 정말로 좋은 주석은, 주석을 달지 않을 방법을 찾아낸 주석이라는 사실을!
정보를 제공하는 주석
때로는 기본적인 정보를 주석으로 제공하면 편리하다. 예를 들어, 다음 주석은 추상 메서드가 반환할 값을 설명한다.
// 테스트 중인 Responder 인스턴스를 반환한다.
protected abstract Responder responderInstance();
가능하다면, 함수 이름에 정보를 담는 편이 더 좋다. 예를 들어, 위코드는 함수 이름을 responderBeingTested로 바꾸면 주석이 필요없어진다.
// kk:mm:ss EEE, MMM dd, yyyy 형식이다.
Pattern timeMatcher = Pattern.compile(
"\\d*:\\d*:\\d* \\w*,\\w* \\d*, \\d*");
위에서 제시한 주석은 코드에서 사용한 정규 표현식이 시작과 날짜를 뜻한다고 설명한다. 구체적으로는 주어진 형식 문자열을 사용해 SimpleDateFormat.format 함수가 반환하는 시각과 날짜를 뜻한다. 이왕이면 시각과 날짜를 변환하는 클래스를 만들어 코드를 옮겨주면 더 좋고 더 깔끔하겠다. 그러면 주석이 필요없어진다.
의도를 설명하는 주석
때때로 주석은 구현을 이해하게 도와주는 선을 넘어 결정에 딸린 의도까지 설명한다. 다음은 주석으로 흥미로운 결정을 기록한 예제다.
privtae static int determineIterationLimit(){
// 배열에 있는 모든 배수는 배열 크기의 제곱근보다 작은 소수의 인수다.
// 따라서 이 제곱근보다 더 큰 숫자의 배수는 제거할 필요가 없다.
double inteationLimit = Math.sqrt(crossedOut.length);
return (int) iterationLimit;
}
루프 한계값으로 제곱근을 사용한 이유를 설명한다. 나로서는 변수 이름을 바꾸거나 코드 구조를 조장해 이유를 명확하게 설명할 방법을 찾지 못했다. 다른 한쳔으로 제곱근의 사용은 나만의 생각일지도 모르겠다. 제곱근까지만 루프를 돌면 정말로 시간을 절약할까? 제곱근 계산에 오히려 시간이 더 들진 않을까? 숙고할 가치가 있는 문제다.
'Clean Code' 카테고리의 다른 글
5장 형식 맞추기 (0) | 2024.02.19 |
---|---|
3장 함수 (0) | 2024.02.09 |
2장 의미있는 이름 (0) | 2024.02.08 |
1장 깨끗한 코드 (0) | 2024.02.07 |
클린 코드를 향한 첫 걸음 (0) | 2024.02.07 |