본문으로 건너뛰기

How To Write Good Comments

Alex Kondovalexkondov.com ↗

How To Write Good Comments

핵심 전제

  • 프로그래밍 언어는 완전한 자기 문서화(self-documenting)가 불가능함
  • 코드는 “어떻게(how)“를 표현하고, 주석은 “왜(why)“를 표현하는 도구
  • 주석은 실행 코드의 한계를 벗어나 자연어의 풍부함을 활용하는 수단
  • 잘 작성된 주석은 코드베이스의 품질을 높임 — 주석을 제거해야 할 악(evil)으로 보는 관점에 동의하지 않음

Don’t Compensate with Comments

주석은 나쁜 코드를 보완하는 수단이 아니다

  • 먼저 코드로 의도를 완전히 표현할 수 있다면, 주석 없이 코드만으로 작성해야 함
  • 코드베이스에 익숙하지 않은 사람도 “무엇을(what)” 하는지 이해할 수 있도록 서술적인 이름과 구조를 갖춰야 함
  • 코드를 잘 작성한 이후에도 추가 정보가 필요하다고 판단될 때 주석을 작성할 것
  • 코드가 불명확해서 주석으로 보완하는 것은 이중 작업(double work)

Comments Should Tell The “Why”

  • “코드가 잘 작성되었다면 자기 문서화된다”는 주장은 부분적으로만 옳음
  • 코드는 프로그램이 어떻게 동작하는지의 유일한 진실(source of truth)이지만, 로직에 담긴 맥락까지 전달하지는 못함
  • 비즈니스 요구사항, 물리 법칙, 도메인 지식 등 외부 세계의 개념은 코드만으로 표현 불가
  • 이름 짓기와 포맷팅으로 일부 정보를 전달할 수 있지만, “왜 이 방식으로 구현했는가”의 근거는 전달 불가
표현 방식 전달 가능한 것
코드 어떻게(how) 동작하는가
변수/함수명 무엇을(what) 하는가
주석 왜(why) 이렇게 구현했는가
  • binaryTreeBecauseLinkedListSlower 같은 변수명은 쓰지 않지만, 자료구조 선택의 이유는 주석으로 남길 수 있음
  • 코드를 지나치게 서술적으로 만들면 오히려 가독성이 저하됨 — 주석이 “why”를 담당하면 코드는 “how”에만 집중할 수 있음

Comments Define Abstractions

  • 추상화(abstraction): 복잡한 내부를 숨기고 단순한 인터페이스를 제공하는 개념
  • 코드는 상세하지만, 추상화는 본질적으로 모호함 — 코드만으로 추상화를 설명하기에는 한계가 있음
  • 소스 코드를 전부 읽지 않아도 모듈이 무엇을 하는지 이해할 수 있어야 함
  • 주석은 인터페이스와 구현 사이의 중간 위치를 차지함:
인터페이스 → 기본 연산 제공
주석       → 사용 방법, 제약, 기능 설명 (중간 수준)
구현 코드  → 세부 동작 방식
  • 주석을 통해 모듈/클래스의 high-level 뷰를 제공 — 너무 추상적이지도, 너무 구체적이지도 않게

The Trouble With Inline Comments

주석의 두 가지 유형

  • 모듈/클래스/함수 수준 주석: 전체가 무엇을 하는지 설명하는 고수준 주석
  • 인라인(구현) 주석: 실제 구현 코드 사이에 삽입되어 각 연산의 세부 정보를 제공

인라인 주석 = 리팩터링 초대장

  • 개발자들이 주석의 유용성에 불만을 가질 때, 대부분 인라인 주석을 의미함
  • 인라인 주석으로 함수의 논리적 부분을 구분하고 있다면, 함수를 더 작게 분리하라는 신호
// 주석으로 함수의 파트를 구분하는 나쁜 예
function createUserAccount(email, password) {
  // 이메일 유효성 검사
  ...
  // 새 유저 엔티티 생성
  ...
  // 계정 확인 이메일 발송
  ...
}

// 더 작은 함수들로 분리하는 좋은 예
function createUserAccount(email, password) {
  validateEmail(email)
  createUserEntity(email, password)
  sendConfirmationEmail(email)
}

엣지 케이스도 함수로 추출

  • 엣지 케이스나 특별 처리 로직을 인라인 주석과 함께 두는 대신, 별도 함수로 추출하고 함수 수준 주석을 붙이는 것이 더 좋음
// 인라인 처리 — 장황해짐
if (typeof IntersectionObserver === undefined) {
  // Safari에서 IntersectionObserver 폴리필이 필요함
  await import('intersection-observer')
}

// 함수로 추출하고 함수 수준 주석 작성
/**
 * IntersectionObserver API 인스턴스를 반환한다.
 * 현재 브라우저에서 지원되지 않는 경우 폴리필을 임포트한다.
 */
async function initIntersectionObserver() {
  ...
  if (typeof IntersectionObserver === undefined) {
    await import('intersection-observer')
  }
  ...
}

Inline Comments Should be Precise

  • 인라인 주석은 저수준(low-level) 세부 정보를 제공하는 데 여전히 유용함
  • 구현 코드에 가깝기 때문에 더 정확하고 구체적일 수 있음
  • 도메인 특정 비즈니스 맥락, 특정 용어의 의미 등을 전달하는 데 적합
// D&D 캐릭터는 특정 스킬에 숙련도를 가지며 보너스를 받는다
const modifier = baseModifier + (isProficient ? 2 : 0)
  • 짧고 이름이 잘 지어진 함수에는 인라인 주석이 불필요하지만, 길거나 복잡한 함수에는 명확성을 위해 유용함
  • 인라인 주석도 구현 방법(“how”)은 코드에 맡기고, “what”과 “why”에만 집중해야 함

Knowing Where to Add Clarity

명백한 주석은 피해야 함

  • 불필요하게 명백한 주석은 추가 작성 및 유지보수 부담만 늘릴 뿐 이득이 없음
  • 작성자가 오랫동안 특정 문제를 다뤄왔다면, 자신에게 명백한 내용이 다른 사람에게는 혼란스러울 수 있음
  • 한 번 이해한 로직은 단순하게 보이기 때문에 복잡한 부분을 놓치고 문서화하지 않을 수 있음

코드 리뷰를 활용하라

  • 복잡한 로직에 충분한 설명이 있는지 확인하려면 코드 리뷰에 의존해야 함
  • 한 명 이상이 동일한 부분에 혼란을 느낀다면, 작성자에게 명백해 보여도 주석이 필요하다는 신호
  • “두 명 이상이 혼란을 느끼면 반드시 주석을 추가하라”

The Case of the Deep Copy

실제 사례: 수수께끼 같은 코드 한 줄

const data = JSON.parse(JSON.stringify(response))
  • 이 코드를 처음 본 사람은 즉시 “왜 이렇게 했지?“라는 의문을 갖게 됨
  • 주석이 없으면 동료에게 직접 물어봐야 하는 상황 발생 → 20분짜리 설명을 반복하게 됨

주석 한 줄이 텔레파시와 같다

// 참조에 의한 변경을 막기 위해 깊은 복사를 만든다
const data = JSON.parse(JSON.stringify(response))
  • 응답(response)은 여러 곳에서 사용되는 깊게 중첩된 구조 → 참조 변이를 막으려면 깊은 복사 필요
  • 주석 한 줄이 있었다면 불필요한 질문과 컨텍스트 전환이 없었을 것

더 나은 방법: 함수로 추출

/**
 * 깊게 중첩된 자료구조의 복사본을 반환한다.
 * 참조에 의한 수정을 막기 위해 사용된다.
 */
function makeDeepCopy(response) {
  return JSON.parse(JSON.stringify(response))
}

const data = makeDeepCopy(response)
  • 함수명만으로는 “왜” 이 방식을 쓰는지 알 수 없으므로, 이 경우 함수 수준 주석이 가치 있음
  • parse/stringify 호출을 “최적화”한다며 제거했다가 미묘한 버그가 발생할 수 있음
  • 주석은 시간여행과 같다 — 작성자가 회사를 떠난 뒤에도, 그 사람의 의도를 언제든 확인할 수 있음

Maintaining Comments

주석도 코드와 마찬가지로 유지보수가 필요하다

  • 코드는 거의 변하지 않고 고정되는 경우가 드뭄 — 요구사항 변화나 기술 변경으로 언제든 리팩터링 필요

  • 주석도 함께 업데이트해야 함:

    • 버그를 처리하기 위해 함수를 특이하게 변경했다면 → 주석으로 문서화
    • 드문 비즈니스 케이스를 처리한다면 → 이유를 주석으로 남겨야 함
    • 기존 코드를 변경했다면 → 관련 주석도 반드시 갱신
  • 동기화가 깨진 주석은 없는 것보다 더 나쁘다:

    • 주석이 없으면 → 도움을 찾으러 감
    • 주석이 틀렸으면 → 잘못된 방향으로 이끌려 더 큰 피해 발생

Keep Commenting

  • 주석은 도구이며, 사용 방식에 따라 결과가 달라짐
  • 과도한 주석: 유지보수 부담 증가
  • 주석 부재: 헤밍웨이처럼 코드를 쓰지 않는 한, 머릿속에 유지해야 할 멘탈 모델이 기능마다 커짐

주석이 다루어야 할 내용

  • 코드로 표현할 수 없는 모든 것
  • 특정 연산의 “why”
  • 자료구조나 알고리즘 선택의 이유
  • 알고리즘의 세부적인 작은 디테일

파일의 모든 문자는 이후에 유지보수해야 하는 문자다 — 신중하게, 그러나 아끼지 말고 사용하라