Skip to main content

4장 주석

written by
shinyeong
shinyeong 🏆Front End Engineer

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

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

  • 주석은 '순수하게 선하지' 못하다. 기껏해야 필요악이다.
  • 우리는 의도를 표현하지 못해 실패한 코드를 만회하기 위해 주석을 사용한다.
  • 그러나, 상황을 역전해 코드로 의도를 표현할 방법은 없을까?
  • 코드는 변화하고 진화하며 옮겨지기도 하기에 주석은 오래될 수록 코드에서 멀어질 수 밖에 없다. (주석을 유지하며 보수하기란 현실적으로 불가능하다)
  • 애초에 주석이 필요없는 방향으로 하라. 진실은 한 곳에만 존재하며 그것이 바로 '코드'이다. 즉, 코드는 단일 진실 공급원(Single source of truth)이어야 한다.

2) 코드로 의도를 표현하라!#

아래와 같이 몇 초만 더 생각하면 코드로 대다수 의도를 표현할 수 있어 주석이 불필요하다.

// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.if ((employee.flags & HOURLY_FLAG) &&     (employee.age > 65))
  • 위 코드는 아래와 같이 수정할 수 있다.
if (employee.isEligibleForFullBenefits())

3) 좋은 주석#

어떤 주석은 필요하거나 유익하다. 그러나 정말로 좋은 주석은 주석을 달지 않을 방법을 찾아낸 주석이다!

(1) 법적인 주석#

때로는 회사가 정립한 구현 표준에 맞춰 법적인 이유로 특정 주석을 넣으라고 명시한다.

// Copyright (C) 2003, 2004, 2005 by Object Mentor, Inc. All rights reserved.

(2) 정보를 제공하는 주석#

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

물론 이 또한 responderBeingTested로 바꾸면 없앨 수 있다.

(3) 의도를 설명하는 주석#

때때로 주석은 구현을 이해하게 도와주는 선을 넘어 결정에 깔린 의도까지 설명한다.

// 스레드를 대량 생성하는 방법으로 어떻게든 경쟁 조건을 만들려 시도한다. for (int i = 0; i > 2500; i++) {    WidgetBuilderThread widgetBuilderThread =         new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);    Thread thread = new Thread(widgetBuilderThread);    thread.start();}

(4) 의미를 명료하게 밝히는 주석#

때로는 모호한 인수나 반환 값은 그 의미를 읽기 좋게 표현하면 이해하기 쉬워진다.

assertTru(a.compareTo(a) == 0); // a == aassertTru(a.compareTo(b) == -1); // a < aassertTru(a.compareTo(aa) == 1); // a > aa

(5) 결과를 경고하는 주석#

특정 테스트 케이스를 꺼야 하는 이유를 설명하는 등, 다른 프로그래머에게 결과를 경고할 목적으로 주석을 사용한다.

// 여유 시간이 충분하지 않다면 실행하지 마십시오.public void _testWithReallyBigFile()

(6) TODO 주석#

TODO 주석은 프로그래머가 필요하다 여기지만 당장 구현하기 어려운 업무를 기술한다. 주기적으로 TODO 주석을 점검해 업무 완료 후 주석을 제거하는 것이 좋다.

// ToDo: 에러처리 추가 필요, 현재는 에러메세지 alert 출력하는 식으로만 처리if (error) alert(error.graphQLErrors[0]);

VSCode TODO Highlight

(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를 작성한다.

Javadoc Java 소스에 문서화를 하는 방법으로 클래스나 메소드에 주석으로 기술한 내용을 javadoc 명령어나 또는 이를 이용한 빌드 툴을 이용하여 문서화할 수 있다.

4) 나쁜 주석#

(1) 주절거리는 주석#

해당 주석이 이해가 안되어 다른 모듈까지 뒤저야 하는 주석은 독자와 제대로 소통하지 못하는 주석이다. 그런 주석은 바이트만 낭비할 뿐이다.

(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) 오해할 여지가 있는 주석#

위 (2) 코드 예시와 같이 의도는 좋았으나 딱 맞을 정도로 엄밀하게는 주석을 달지 못한 경우이다.

(4) 의무적으로 다는 주석#

모든 함수에 Javadocs 를 달거나 모든 변수에 주석을 달아야 한다는 규칙은 어리석다. 위의 주석은 아무런 가치가 없다.

/** * * @param title CD 제목 * @param author CD 저자 * @param tracks CD 트랙 숫자 * @param durationInMinutes CD 길이(단위: 분) */public void addCD(String title, String author, int tracks, int durationInMinutes) {    CD cd = new CD();    cd.title = title;    cd.author = author;    cd.tracks = tracks;    cd.duration = durationInMinutes;    cdList.add(cd);}

(5) 이력을 기록하는 주석#

이전에는 소스 코드 관리 시스템이 없었기에 주석을 이용해 일지 혹은 로그를 작성하였다. 그러나 현재는 '깃'을 이용하도록 하자.

* 변경 이력 (11-Oct-2001부터)* ------------------------------------------------* 11-Oct-2001 : 클래스를 다시 정리하고 새로운 패키징* 05-Nov-2001: getDescription() 메소드 추가* 이하 생략

(6) 있으나 마나 한 주석#

너무 당연한 사실을 언급하며 새로운 정보를 제공하지 못하는 주석이다.

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

(7) 무서운 잡음#

어떻게든 정보를 제공해야한다는 잘못된 욕심으로 탄생한 주석은 아래와 같이 잘못된 정보를 제공함으로써 무서운 잡음을 만들 수 있다.

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

(8) 함수나 변수로 표현할 수 있다면 주석을 달지 마라#

아래와 같이 주석이 필요하지 않도록 함수 혹은 변수로 표현함으로써 코드를 개편해야한다.

// 전역 목록 <smodule>에 속하는 모듈이 우리가 속한 하위 시스템에 의존하는가?if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()));
ArrayList moduleDependencies = smodule.getDependSubSystems();String ourSubSystem = subSysMod.getSubSystem();if (moduleDependees.contains(ourSubSystem))

(9) 위치를 표시하는 주석#

너무 자주 사용하지 않는 다면 배너는 눈에 띄며 주의를 환기시키기도 하지만 배너를 남용하면 독자가 흔한 잡음으로 여겨 무시하게 된다. 고로 꼭 필요한 경우에만 아주 드물게 사용하는 편이 낫다.

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

(10) 닫는 괄호에 다는 주석#

중첩이 심하고 장황한 함수라면 닫는 괄호에 주석이 의미가 있을지 모르지만, 작고 캡슐화된 함수를 만든다면 이러한 주석은 불필요하다. 대신에 함수를 줄이려 시도해라.

const a = () => {  if(state) {    while(t) {      break;    } //while  } // if}

(11) 공로를 돌리거나 저자를 표시하는 주석#

소스 코드 관리 시스템은 누가 언제 무엇을 추가했는지 모두 저장이 된다. 고로 저자 이름으로 코드를 오염시킬 필요가 없다.

/* 릭이 추가함 */

(12) 주석으로 처리한 코드#

주석으로 처리된 코드는 다른 사람들이 지우기를 주저한다. 이유가 있어 남겨놓았으리라고, 중요하니까 지우면 안된다고 생각한다. 그래서 쓸모 없는 코드가 점차 쌓여간다. 하지만 이제 소스코드 관리 시스템이 우리의 코드를 대신 기억해준다. 그러므로 주석 코드는 그냥 삭제하라. 잃어버릴 염려는 없다.

(13) HTML 주석#

소스 코드에서 HTML 주석은 혐오 그 자체다.

/* * <pre> * {@code * Foo foo = new Foo(); * foo.foo(); * } * </pre> */

Javadocs 와 같은 도구로 주석을 뽑아 웹 페이지에 올려야 한다면 주석에 HTML 태그를 삽입하는 것은 도구가 해야한다.

(14) 전역 정보#

주석을 달아야한다면 근처에 있는 코드만 기술하라. 코드 일부에 주석을 달면서 시스템의 전반적인 정보를 기술하지 마라.

(15) 너무 많은 정보#

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

RFC 2045 - Multipurpose Internet Mail Extensions (MIME)1부 인터넷 메시지 본체 혀식6.8절. 인코딩 과정은 입력 비트 중 24 비트 그룹을 인코딩 된 4글자로 구성된출력 문자열로 표현한다. ...

(16) 모호한 관계#

주석과 주석이 설명하는 코드는 둘 사이 관계가 명백해야 한다.

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

위 코드는 필터 바이트란 무엇인지 이 것이 +1과 관련이 있는지 +3과 관련이 있는지 모호하다. 설명을 요구하는 주석은 안타깝기 그지없다.

(17) 함수 헤더#

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

(18) 비공개 코드에서 Javadocs#

공개 API는 Javadocs가 유용하지만 공개하지 않을 코드라면 쓸모가 없다.