본문 바로가기
개발/Clean Code

[Clean Code] 4장. 주석(comments)

by 달사쿠 2020. 11. 24.
반응형

Intro

나쁜 코드에 주석을 달지 마라. 새로 짜라.
- 브라이언 W. 커니핸, P. J. 플라우거

주석을 사용하면 좋을까요, 나쁠까요?

예상하셨듯 잘달린 주석이라면 그 어떤 정보보다 유용하지만, 

코드로 의도를 잘 표현하지 못해 실패를 만회하기 위한 주석은 절대 좋지 못합니다.

 

주석이 필요한 상황에 처하면 상황을 역전해 코드로 의도를 표현할 방법이 없는지 곰곰히 생각하고,

주석을 유지보수하기 힘들고, 오래될수록 완전히 그릇될 가능성도 커지기 때문에 주석은 가능한 줄이도록 꾸준히 노력해야 합니다.

 

필자는 크게 2가지를 얘기합니다.

 

1. 주석은 나쁜 코드를 보완하지 못한다.

표현력이 풍부하고 깔끔하고 주석이 거의 없는 코드가 복잡하고 어수선하며 주석이 많이 달린 코드보다 훨씬 좋다.

 

2. 코드로 의도를 표현해라.

많은 경우 주석으로 달려는 설명을 함수로 만들어 표현해도 충분하다.

 

본인은 주석이 나쁜코드를 보완하지 못한다는 것에도 동의를 하고, 코드로 의도를 표현하는 것에도 어느정도 동의합니다. 책의 필자도 말했듯 코드만으로 의도를 설명하기 어려운 경우가 분명히 존재하지만, 어느정도 커스텀함수를 만들면 커버가 된다고 생각합니다.

책에서는 다음과 같은 예제를 소개합니다.

//직원에게 복지 혜택을 받을 작격이 있는지 검사한다.
if((employee.flags & HOURLY_FLAG) &&
   (employee.age > 65))
   
//refactoring 후 (*Eligible: 적격의)
if(employee.isEligibleForFullBenefits())

위의 예제처럼 함수로 주석의 의미를 내포하여 구성할 수 있습니다.

 

본격적으로 책에서 소개하는 좋은 주석과 나쁜 주석에 대해서 정리해보겠습니다. (참고로 기울여진 글들은 블로그 주인의 생각입니다.)

 


좋은 주석

- 법적인 주석
- 정보를 제공하는 주석
- 의도를 설명하는 주석
- 의미를 명료하게 밝히는 주석
- 결과를 경고하는 주석
- TODO 주석
- 중요성을 강조하는 주석
- 공개 API에서 Javadocs

법적인 주석

각 소스 파일 첫 머리에 주석으로 들어가는 저작권 정보와 소유권 정보

//예시
/**
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 
file except in compliance with the License. You may obtain a copy of the License at 
http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed 
to in writing, software distributed under the License is distributed on an "AS IS" 
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under the License.
**/

 

정보를 제공하는 주석

기본적인 정보를 주석으로 제공하면 편리하지만, 그래도 가능하면 함수로 표현하도록 하자.

//테스트 중인 Responder 인스턴스를 반환한다.
protected abstract Responder responderInstance();

//refactoring 후 주석이 필요없는 예제
protected abstract Responder responderBeingTested();

아래와 같은 주석이 있다면, 이왕이면 시각과 날짜를 변환하는 클래스를 만들어 코드를 옮겨주는 것이 더 깔끔해보인다고 했다.

//kk:mm:ss EEE, MMM dd, yyy 형식이다.
Pattern timeMatcher = Pattern.compile( "\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*");

약간 다른 생각이라면 클래스를 따로 만들더라도 정규표현식을 사용한다면, 어쨋든 어떤 형태의 정규표현식을 사용하고자 했는지 주석을 함께 달아놓으면 좋다고 생각한다. (클래스 명으로는 어떤 형태의 식을 사용하는지 표현하기에는 어쨋거나 한계가 존재하는 것은 매한가지라고 생각한다.)

 

의도를 설명하는 주석

어떤 의도로 이러한 코드를 넣게되었는지, 결정에 깔린 의도를 설명

//스레드를 대량 생성하는 방법으로 경쟁조건 생성
for(int i = 0;i < 25000; i++){
   WidgetBuilderThread widgetBuilderThread = new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
   Thread thread = new Thread(widgetBuilderThread);
   thread.start();
}

이 부분도 조금 의문이 드는 점이라면 위에서 필자가 언급했듯 코드로 의도를 설명할 수 있지 않나..? createRaceConditionUsingThread 정도로 메소드를 만들면 되지 않나 싶다..

 

의미를 명료하게 밝히는 주석

모호한 인수나 반환값을 그 의미를 읽기좋게 주석을 달면 이해하기 쉬워진다. 허나 그릇된 주석을 달아놓을 위험이 상당히 높기 때문에 정확히 달도록 각별히 유의해야한다.

assertTrue(a.compareTo(a) == 0);  //a == a
assertTrue(a.compareTo(b) == -1); //a < b

 

결과를 경고하는 주석

어떤 tc가 너무 오래걸리거나, 어떤 프로그램이 thread unsafe 할 경우에 다음과 같이 주석을 달 수 있다.

//시간이 충분하지 않다면 실행하지 마세요.
//또는 @Ignore("실행이 너무 오래 걸린다")
public void _testWithReallyBigFile(){ ... }

//SimpleDateFormat은 thread unsafe하므로, 각 인스턴스를 독립적으로 생성해야한다.
SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");

 

TODO 주석

때로는 '앞으로 할 일'을 //TODO 주석으로 남겨두면 편하다. 하지만 주기적으로 TODO 주석을 점검해서 필요없어진 TODO주석은 지우는 것도 중요하다.

 

중요성을 강조하는 주석

자칫 대수롭지 않다고 여겨질 뭔가의 중요성을 강조

String listItemContent = match.group(3).trim();
//여기서 trim은 정말 중요하다. trim 함수는 문자열에서 시작 공백을 제거한다.
//문자열에 시작 공백이 있으면 다른 문자열로 인식되기 때문이다.

개인적으로 이것도 필요한 주석인가 싶다. 중요성을 강조하는 주석이라면 commit 내용에 짦막하게 적은 것이 더 나을 것 같다는 생각이 든다. 그리고 책의 필자가 언급했듯 주석대신 코드로 표현할 수 있는 것이 아닌가 싶기도 하다. (match.group(3).trim()을 따로 뺀 함수를 만든다던가...)

 

다음으로는 나쁜주석에 대해서 알아보겠습니다.


나쁜 주석

- 주절거리는 주석
- 같은 이야기를 중복하는 주석
- 오해할 여지가 있는 주석
- 의무적으로 다는 주석
- 이력을 기록하는 주석
- 있으나 마나 한 주석
- 무서운 잡음
- 함수나 변수로 표현할 수 있다면 주석을 달지 마라
- 닫는 괄호에 다는 주석
- 공로를 돌리거나 저자를 표시하는 주석
- 주석으로 처리한 코드
- HTML 주석
- 전역 정보
- 너무 많은 정보
- 모호한 관계
- 함수 헤더
- 비공개 코드에서 Javadocs

주절거리는 주석

어떤 주석은 주석의 의미를 알아내기 위해 다른 코드들을 다 뒤져봐야할 수도 있다. 주석을 달기로 결정했다면 가능한한 충분한 시간을 들여 의미전달을 명확하게 하는 주석을 만들기로 하자.

 

같은 이야기를 중복하는 주석

예제를 먼저보자.

//예제1.
//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");
  }
}

//예제2. 톰캣의 소스코드 일부
public abstract class ContainerBase implements Container, ... {
  ...
  /**
   * 관련된 logger 이름
   */
  protected String logName = null;
  
  /**
   * 컨테이너와 관련된 Manager 구현
   */
  protected Manager manager = null;
  ...
}

먼저 예제 1을 보면, 위와 같은 주석은 코드의 의도나 근거를 설명하는 주석도 아니고, 더불어 코드의 내용을 그대로 주석에다 글로 풀어놓았다. 뿐 만 아니라 추가하자면 this.closed라는 프로퍼티의 의미까지 알아야한다. 

 

예제2를 보자. 쓸모없고 중복된 Javadocs가 매우 많아 코드만 지저분하고 정신없게 한다. 더불어 '관련된 logger 이름'이라고 했지만, 무엇과 관련되어있는지 기술되지 않았고, '컨테이너와 관련된 Manager 구현'이라고 주석이 달려있지만 구현과 관련된 코드가 아닌 그냥 단순 프로퍼티를 선언해놓은게 끝이다.

 

오해할 여지가 있는 주석

위의 예제1에서 봤던 주석에서 this.closed가 true로 변하는 순간에 베서드가 반환되지 않는다. 즉 주석에 담긴 '조금 잘못된 정보'로 인해 누군가는 원하는 동작이 나오지 않아 머리를 골싸맬 수 있다.

 

의무적으로 다는 주석

모든함수에 Javadocs를 달거나 모든 변수에 주석을 달아야한다는 규칙은 어리석다. 코드를 헷갈리게 만들 수 있고, 추후 유지보수를 하지 않는다면 주석은 쓰레기에 불과하다.

 

이력을 기록하는 주석

변경이력을 주석으로 관린하는 것은 과거의 관례. 지금은 소스코드 관리 시스템(ex. git)을 활용하여 이력을 관리하자!

 

있으나 마나 한 주석

너무나 당연한 사실을 언급하며 새로운 정보를 제공하지 못하는 주석은 사용하지 말자

/**
 * 기본 생성자
 */
protected AnnualDateRule() {}

/** 월 중 일자 */
private int dayOfMonth;

이런 주석은 너무 당연한 내용을 기술해놨다보니 개발자가 주석을 무시하는 습관에 빠져 주석을 건너뛰며 코드를 읽을 수 있다. 그렇게 되면 결국은 코드가 바뀌며 주석은 거짓말로 바뀌게 된다.

 

무서운 잡음

가끔은 javaDocs도 잡음이다. 아래와 같은 실제로 존재하는 오픈소스의 코드에는 복붙의 폐해가 남아있다.

/** The name. */
private String name;

/** The version. */
private String version;

/** The version. */
private String info;

 

함수나 변수로 표현할 수 있다면 주석을 달지 마라.

이건 위에서 책의 필자가 계속 얘기했던 부분이었다. 알아보기 힘든 코드에 주석을 다는 것보다는 코드를 분리해 알아보기 쉬운 코드로 바꾸자.

 

위치를 표시하는 주석

아래와 같은 배너 역할을 하는 주석은 유용할 때도 있지만, 너무 자주사용한다면 가독성을 낮춘다.

음,, 사실 공감이 가지 않는다. 영역을 구분지어 놓는 것이 좀 더 가독성을 높이지 않을까..?

//Actions ///////////////////

 

닫는 괄호에 다는 주석

닫는 괄호에 다음과 같이 특수한 주석을 달면,, 잡음에 불과하다. (이런 주석을 다는 사람이 있을까..... 있으니까 책에 썼겠지..?)

try{
  while((line = in.readLine()) != null) {
   ...
  } //while
  ...
} //try

 

공로를 돌리거나 저자를 표시하는 주석

소스 코드 관리 시스템이 누가 언제 무엇을 추가했는지 귀신처럼 기억한다.

 

주석으로 처리한 코드

주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다. 이유가 있어 남겨놨을 수도 있고, 중요해서 남겨놨을 수도 있고.. 이유야 알 수 없지만, 지우기를 주저하고 주석으로 처리한 코드가 늘어난다면 쓰레기에 불과하다.

모든건 소스코드 관리 시스템이 코드를 기억해주니 삭제해라.

 

HTML 주석

소스코드에서 HTML 주석은 혐오 그 자체이다. (뭘 말하고 싶은 예제인지 모르겠어서 생략한다)

 

전역 정보

주석을 달아야한다면 코드 근처에 기술해라. 주석 근처에 존재하는 코드가 아닌데도, 주석으로 작성하면 시스템 어딘가에 있는 다른 함수까지 뒤져봐야할 수 있다.

/**
 * 적합성 테스트가 동작하는 포트: 기본값은 <b> 8082 </>.
 * @param fitnessePort
 */
public void setFitnessePort(int fitnessePort){
  this.fitnessePort = fitnessePort;
}

 

너무 많은 정보

주석에다 흥미로운 역사나 관련없는 정보를 장황하게 늘어놓지 마라

 

모호한 관계

주석과 코드 둘 사이 관계가 명확해야 한다. 즉, 주석 자체가 다시 설명을 요구하면 안된다. 

아래의 예시를 보면 '필터바이트란 무엇일까?' 라는 질문을 할 수 있다. 이처럼 주석 자체가 다시 설명을 요구하면 안된다.

(약간 다른 관점에서 바라보자면, 필터 바이트가 어떤 도메인에서는 자주쓰이는 용어라면..? 이라는 생각이 든다. 내가 잘 모르는 용어가 담긴 주석이라도 해당 도메인에 지식이 있는 다수가 알아보는 주석이라면 나쁜 주석은 아니라는 생각이 든다)

/*
 * 모든 픽셀을 담을 만큼 충분한 배열로 시작한다(여기에 필터 바이트를 더한다).
 * 그리고 헤더 정보를 위해 200바이트를 더한다.
 */
this.pngBytes = new byte[((this.width + 1) * this.height * 3) + 200];

 

함수 헤더

짧은 함수는 긴 설명이 필요없다. 짧고 한가지만 수행하며 이름을 잘 붙인 함수가 주석으로 헤더를 추가한 함수보다 훨씬 좋다.

 

비공개 코드에서 Javadocs

공개 API는 Javadocs가 유용하지만 공개하지 않을 코드라면 Javadocs는 쓸모없다. 유용하지 않을 뿐만 아니라 코드가 보기 싫고 산만해질 뿐이다. 


회고

좋은 주석이라도 어쨋든 유지보수를 안하면 나쁜주석인 것 아닐까..?

물론 명백하게 나쁜 주석은 있을지라도, 코드와 주석을 함께 유지보수를 안하면 좋은주석이라는 의미가 있는지 잘 모르겠다.

결론은 최대한 네이밍이 훌륭한 코드로 잘 표현하자는 것 같다.

반응형

'개발 > Clean Code' 카테고리의 다른 글

[오브젝트] 9장 유연한 설계  (0) 2021.06.03
[Clean Code] 15장. JUnit 들여다보기  (0) 2021.03.08
[Clean Code] 10장. 클래스  (2) 2021.01.03
[Clean Code] 5장. 형식맞추기  (0) 2020.12.01
Clean Code에 대하여  (0) 2020.11.23

댓글