2020년 5월 6일

주석

코드 구조에서 알아본 바와 같이 한 줄짜리 주석은 //로, 여러 줄의 주석은 /* ... */로 시작합니다.

주석(comment)은 어떻게 코드가 동작하는지, 왜 코드가 동작하는지를 설명하는 데 쓰입니다.

주석을 작성하는 게 쉬워 보일 수 있는데, 초보 개발자들은 종종 잘못된 방법으로 주석을 작성하는 실수를 범합니다.

좋지 않은 주석

초심자들은 주석에 '코드에서 무슨 일이 일어나는지’에 대한 내용을 적곤 합니다. 아래와 같이 말이죠.

// 이 코드는 (...)과 (...)을 수행합니다
// A라는 개발자가 이 기능에 대해 알고 있으며...
very;
complex;
code;

그러나 좋은 코드엔 ‘설명이 담긴(explanatory)’ 주석이 많아선 안 됩니다. 주석 없이 코드 자체만으로 코드가 무슨 일을 하는지 쉽게 이해할 수 있어야 합니다.

이와 관련된 좋은 규칙도 있습니다. “코드가 불분명해서 주석 작성이 불가피하다면 코드를 다시 작성해야 하는 지경에 이른 걸 수 있습니다.”

리팩토링 팁: 함수 분리하기

함수 내 코드 일부를 새로운 함수로 옮기는 게 유익할 때도 있습니다. 아래와 같이 말이죠.

function showPrimes(n) {
  nextPrime:
  for (let i = 2; i < n; i++) {

    // i가 소수인지를 확인함
    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert(i);
  }
}

코드 일부를 함수 isPrime으로 옮기면 더 나은 코드를 작성할 수 있습니다.

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if (n % i == 0) return false;
  }

  return true;
}

함수 이름 자체가 주석 역할을 하므로 코드를 쉽게 이해할 수 있게 되었습니다. 이런 코드를 자기 설명적인(self-descriptive) 코드라 부릅니다.

리팩토링 팁: 함수 만들기

아래와 같이 코드가 ‘아래로 죽 늘어져 있는’ 경우를 생각해 봅시다.

// 위스키를 더해줌
for(let i = 0; i < 10; i++) {
  let drop = getWhiskey();
  smell(drop);
  add(drop, glass);
}

// 주스를 더해줌
for(let t = 0; t < 3; t++) {
  let tomato = getTomato();
  examine(tomato);
  let juice = press(tomato);
  add(juice, glass);
}

// ...

이럴 땐 새로운 함수를 만들고, 코드 일부를 새로 만든 함수에 옮기는 게 좋습니다. 아래와 같이 말이죠.

addWhiskey(glass);
addJuice(glass);

function addWhiskey(container) {
  for(let i = 0; i < 10; i++) {
    let drop = getWhiskey();
    //...
  }
}

function addJuice(container) {
  for(let t = 0; t < 3; t++) {
    let tomato = getTomato();
    //...
  }
}

함수는 주석이 없어도 그 존재 자체가 무슨 역할을 하는지 설명할 수 있어야 합니다. 코드를 분리해 작성하면 더 나은 코드 구조가 되죠. 이런 가이드를 잘 지켜 코드를 작성하면 함수가 어떤 동작을 하는지, 무엇을 받고 무엇을 반환하는지가 명확해집니다.

그런데 실무에선, ‘설명이 담긴’ 주석을 작성하는 게 불가피한 경우도 있습니다. 알고리즘이 복잡한 코드를 작성하는 경우나 최적화를 위해 코드를 약간 비틀어 작성할 땐 설명을 적어주어야 합니다. 이런 경우를 제외하곤 간결하고 코드 자체만으로 설명이 가능하게 코딩해야 합니다.

좋은 주석

설명이 담긴 주석은 대개 좋지 않습니다. 그럼 좋은 주석이란 무엇일까요?

아키텍처를 설명하는 주석
고차원 수준 컴포넌트 개요, 컴포넌트 간 상호작용에 대한 설명, 상황에 따른 제어 흐름 등은 주석에 넣는 게 좋습니다. 이런 주석은 조감도 역할을 해줍니다. 고차원 수준의 아키텍처 다이어그램을 그리는 데 쓰이는 언어인 UML도 시간을 내어 공부해 보는걸 추천해 드립니다.
함수 용례와 매개변수 정보를 담고 있는 주석
JSDoc이라는 특별한 문법을 사용하면 함수에 관한 문서를 쉽게 작성할 수 있습니다. 여기엔 함수 용례, 매개변수, 반환 값 정보가 들어갑니다.

예시:

/**
 * x를 n번 곱한 수를 반환함
 *
 * @param {number} x 거듭제곱할 숫자
 * @param {number} n 곱할 횟수, 반드시 자연수여야 함
 * @return {number} x의 n 거듭제곱을 반환함
 */
function pow(x, n) {
  ...
}

이렇게 주석을 달면 코드를 읽어보지 않고도 함수의 목적과 사용법을 한눈에 알 수 있습니다.

WebStorm 등의 다양한 에디터는 이런 주석을 이용해 자동 완성 기능, 자동 에러 검출 기능 등을 제공합니다.

JSDoc 3이나 기타 유사한 툴을 사용하면 주석으로 HTML 문서를 만들 수 있습니다. 자세한 정보는 http://usejsdoc.org/에서 확인하시기 바랍니다.

왜 이런 방법으로 문제를 해결했는지를 설명하는 주석

무엇이 적혀있는지는 중요합니다. 그런데 무슨 일이 일어나고 있는지 파악하려면 무엇이 적혀있지 않은 지가 더 중요할 수 있습니다. '왜 이 문제를 이런 방법으로 해결했나?'라는 질문에 코드는 답을 해 줄 수 없기 때문입니다.

문제 해결 방법이 여러 가지인데 왜 하필이면 이 방법을 택했는지 의문이 들 때가 있습니다. 선택한 방법이 가장 나은 것도 아닌데 말이죠.

왜 이런 방법을 써서 문제를 해결했는지 알려주는 주석이 없으면 다음과 같은 일이 발생할 수 있습니다.

  1. 당신(혹은 동료)은 작성된 후 시간이 꽤 흐른 코드를 열어봅니다. 그리고 그 코드에서 선택한 방식이 ‘가장 좋은 방식은 아니란 걸’ 알아냅니다.
  2. "그때는 내가 멍청했구나. 하지만 지금은 더 똑똑해졌지"라고 생각하며, 이전보단 ‘더 명확하고 올바른’ 방법으로 코드를 개선합니다.
  3. 코드를 개선하려는 시도까지는 좋았습니다. 하지만 리팩토링 과정에서 '더 명확’하다고 생각했던 방법을 적용하면 문제가 발생한다는 걸 알아냅니다. 이미 시도해봤던 방법이기 때문에 왜 이 방법이 먹히지 않는지 희미하게 기억이 떠오릅니다. 새로 작성한 코드를 되돌렸지만, 시간이 낭비되었습니다.

해결 방법을 담고 있는 주석은 아주 중요한 역할을 합니다. 이전에 했던 실수를 방지하는 안내판 역할을 하기 때문입니다.

미묘한 기능이 있고, 이 기능이 어디에 쓰이는지를 설명하는 주석

직감에 반하는 미묘한 동작을 수행하는 코드가 있다면 주석을 달아주는 게 좋습니다.

요약

주석을 보면 좋은 개발자인지 아닌지를 어느 정도 알 수 있습니다. 주석을 언제 쓰고 언제 쓰지 않는지를 보면 되죠.

주석을 잘 작성해 놓으면 시간이 지난 후 코드를 다시 살펴볼 때 효율적으로 정보를 얻을 수 있습니다. 코드 유지보수에 도움이 되죠.

주석에 들어가면 좋은 내용

  • 고차원 수준 아키텍처
  • 함수 용례
  • 당장 봐선 명확해 보이지 않는 해결 방법에 대한 설명

주석에 들어가면 좋지 않은 내용

  • '코드가 어떻게 동작하는지’와 '코드가 무엇을 하는지’에 대한 설명
  • 코드를 간결하게 짤 수 없는 상황이나 코드 자체만으로도 어떤 일을 하는지 충분히 판단할 수 없는 경우에만 주석을 넣으세요.

주석은 JSDoc3 같은 자동 문서생성 도구에도 쓰입니다. 자동 문서생성 도구는 주석을 이용해 HTML 등의 포맷을 가진 문서를 자동으로 만들어줍니다.

튜토리얼 지도