아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라

wisdom·2022년 10월 13일
0

Effetctive Java

목록 보기
56/80
post-thumbnail

자바독 (Javadoc)

API 문서를 사람이 직접 작성하면 코드가 변경될 때마다 매번 함께 수정해줘야 하는데, 자바독(Javadoc) 유틸리티는 이 귀찮은 작업을 도와준다.

자바독은 소스코드 파일에서 문서화 주석(doc comment; 자바독 주석)이라는 특수한 형태로 기술된 설명을 추려 API 문서로 변환해준다. 더 자세히 말하면, 문서화 주석을 HTML로 변환해준다. (문서화 주석 안에 HTML 요소를 사용하면 최종 문서에도 반영된다.)

문서화 주석을 작성하는 규칙은 문서화 주석 작성법(How To Write Doc Comments) 웹사이트에 기술되어 있다.


문서화 주석

API를 올바로 문서화하려면 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석을 달아야 한다.

기본 생성자에는 문서화 주석을 달 방법이 없으니 공개 클래스는 절대 기본 생성자를 사용하면 안 된다.

한편, 유지보수까지 고려한다면 대다수의 공개되지 않은 클래스, 인터페이스, 생성자, 메서드, 필드까지 문서화 주석을 달아야 할 것이다. (공개 API만큼 친절하게 설명하진 않더라도)

📚 메서드용 문서화 주석

메서드용 문서화 주석에는 해당 메서드와 클라이언트 사이의 규약을 명료하게 기술해야 한다.

  • 메서드가 무엇을 하는지, how가 아닌 what을 기술해야 한다.
  • 메서드를 호출하기 위한 전제조건(precondition)을 모두 나열해야 한다. 일반적으로 전제조건은 @throw 태그로 비검사 예외를 선언하여 암시적으로 기술한다. 또한 @param 태그를 이용해 그 조건에 영향받는 매개변수에 기술할 수도 있다.
  • 메서드가 성공적으로 수행된 후에 만족해야 하는 사후조건(postcondition)도 모두 나열해야 한다.
  • 부작용도 문서화해야 한다. 부작용이란, 사후조건으로 명확히 나타나지는 않지만 시스템의 상태에 어떠한 변화를 가져오는 것을 뜻한다.
  • 메서드의 계약(contract)을 완벽히 기술하려면, 모든 매개변수에 @param 태그를, 반환 타입이 void 가 아니라면 @return 태그를, 발생할 가능성이 있는 모든 예외에 @throw 태그를 달아야 한다. (다만, @return 태그의 설명이 메서드 설명과 같을 때 코딩 표준에서 허락한다면 @return 태그는 생략해도 좋다.)


@param, @return, @throw

관례상 @param , @return 태그의 설명은 해당 매개변수가 뜻하는 값이나 반환값을 설명하는 명사구를 쓰고, 드물게는 명사구 대신 산술 표현식을 쓰기도 한다.

@throw 태그의 설명에는 해당 예외를 던지는 조건을 쓴다.

또한, 관례상 @param , @return, @throw 태그의 설명에는 마침표를 붙이지 않는다.

/**
 * Returns the element at the specified position in this list.
 *
 * <p>This method is <i>not</i> guaranteed to run in constant
 * time. In some implementations it may run in time proportional
 * to the element position.
 *
 * @param  index index of element to return; must be
 *         non-negative and less than the size of this list
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException if the index is out of range
 *         ({@code index < 0 || index >= this.size()})
 */
E get(int index) {
    return null;
}
// 한글 버전
/**
 * 이 리스트에서 지정한 위치의 원소를 반환한다.
 *
 * <p>이 메서드는 상수 시간에 수행됨을 보장하지 <i>않는다</i>. 구현에 따라
 * 원소의 위치에 비례해 시간이 걸릴 수도 있다.
 *
 * @param  index 반환할 원소의 인덱스; 0 이상이고 리스트 크기보다 작아야 한다.
 * @return 이 리스트에서 지정한 위치의 원소
 * @throws IndexOutOfBoundsException index가 범위를 벗어나면,
 * 즉, ({@code index < 0 || index >= this.size()})이면 발생한다.
 */
E get(int index) {
    return null;
}

{@code}

{@code} 태그는 2가지 효과가 있다.

  • 태그로 감싼 내용을 코드용 폰트로 렌더링한다.
  • 태그로 감싼 내용에 포함된 HTML 요소나 다른 자바독 태그를 무시한다.

여러 줄로 된 코드 예시를 넣으려면, <pre>{@code ... 코드 ...}</pre> 형태로 쓰면 된다.

단, @ 기호에는 무조건 탈출문자를 붙여야 한다는 것을 주의하자.


{@literal}

{@literal} 태그는 HTML 마크업이나 자바독 태그를 무시하게 해준다.

API 설명에 <, >, & 등의 HTML 메타문자를 포함시키기 위한 가장 좋은 방법이 이 태그를 사용하는 것이다.

{@code} 태그와 비슷하지만 코드 폰트로 렌더링하지 않는다는 차이점이 있다.

// 문서화 주석에 HTML이나 자바독 메타문자를 포함시키기 위해 @literal 태그 사용
/**
 * A geometric series converges if {@literal |r| < 1}.
 */

@implSpec

클래스를 상속용으로 설계할 때는 자기사용 패턴(self-use pattern) 에 대해서도 문서에 남겨 그 메스드를 올바로 재정의하는 방법을 알려줘야 한다.

자기사용 패턴은 자바 8에서 추가된 @implSpec 태그로 문서화한다.

이 태그의 주석은 해당 메서드와 하위 클래스 사이의 계약을 설명하여, 하위 클래스들이 그 메서드를 상속하거나 super 키워드를 이용해 호출할 때 그 메서드가 어떻게 동작하는지를 명확히 인지하고 사용하도록 해줘야 한다.

// 자기사용 패턴 등 내부 구현 방식을 명확히 드러내기 위해 @implSpec 사용
/**
 * Returns true if this collection is empty.
 *
 * @implSpec This implementation returns {@code this.size() == 0}.
 *
 * @return true if this collection is empty
 */
public boolean isEmpty() {
    return false;
}
// 한글 버전
/**
 * 이 컬렉션이 비었다면 true를 반환한다.
 *
 * @implSpec 이 구현은 {@code this.size() == 0}의 결과를 반환한다.
 *
 * @return 이 컬렉션이 비었다면 true, 그렇지 않으면 false
 */
public boolean isEmpty() {
	return false;
}

❗️ 자바독 명령줄에서 -tag "implSpec:a:Implementation Requirements:" 스위치를 켜주지 않으면 @implSpec 태그를 무시해버린다는 점에 주의하자.


요약 설명 & {@summary}

문서화 주석의 첫 번째 문장은 해당 요소의 설명 (summary description)으로 간주된다. 요약 설명은 반드시 대상 기능을 고유하게 기술해야 함에 주의하자.

요약 설명은 첫 번째 마침표가 나오는 곳까지로 인식되므로 마침표(.)에 주의해야 한다. 만약 문장 중간에 마침표가 들어간다면 {@literal} 태그를 사용하거나, 자바 10부터 추가된 요약 설명 전용 태그인 {@summary} 태그를 사용하자.

// 문서화 주석 첫 '문장'에 마침표가 있을 때 요약 설명 처리
/**
 * A suspect, such as Colonel Mustard or {@literal Mrs. Peacock}.
 */
// 요약 설명 전용 태그
/**
 * {@summary A suspect, such as Colonel Mustard or Mrs. Peacock.}
 */

메서드와 생성자의 요약 설명은 동작을 설명하는 주어가 없는 동사구여야 하며, 3인칭 문장으로 써야 한다.

  • ArrayList(int initialCapcity) : Construct an empty list with the specified initial capacity.
  • Collection.size() : Returns the number of elements in this collection.

클래스, 인터페이스, 필드의 요약 설명은 대상을 설명하는 명사절이어야 한다.
클래스와 인터페이스의대상은 그 인스턴스이고, 필드의 대상은 필드 자신이다.

  • Instant : An instantaneous point on the time-line.
  • Math.PI : The double value that is closer than any other to pi, the ratio of the circumference of a circle to its diameter.

{@index}

자바 9부터는 자바독이 생성한 HTML 문서에 검색(색인) 기능이 추가되었다.

클래스, 메서드, 필드 같은 API 요소의 색인은 자동으로 만들어지며, 원한다면 {@index} 태그를 사용해 API에서 중요한 용어를 추가로 색인화할 수 있다.

// 자바독 문서에 색인 추가하기 - 자바 9부터 지원
/**
 * This method complies with the {@index IEEE 754} standard.
 */

제네릭 타입, 제네릭 메서드

제네릭 타입이나 제네릭 메서드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.

/**
 * 키와 값을 매핑하는 객체, 맵은 키를 중복해서 가질 수 없다
 * 즉, 키 하나가 가리킬 수 있는 값은 최대 1개다.
 * 
 * @param <K> 이 맵이 관리하는 키의 타입
 * @param <V> 매핑된 값의 타입
 */
public interface Map<K, V> { ... }

열거 타입

열거 타입을 문서화할 때는 상수들에도 주석을 달아야 한다.

열거 타입 자체와 그 열거 타입의 public 메서드도 물론이다.

// 열거 상수 문서화
/**
 * An instrument section of a symphony orchestra.
 */
public enum OrchestraSection {
    /** Woodwinds, such as flute, clarinet, and oboe. */
    WOODWIND,

    /** Brass instruments, such as french horn and trumpet. */
    BRASS,

    /** Percussion instruments, such as timpani and cymbals. */
    PERCUSSION,

    /** Stringed instruments, such as violin and cello. */
    STRING;
}
// 한글 버전 
/**
 * 심포니 오케스트라의 악기 세션.
 */
public enum OrchestraSection {
    /** 플루트, 클라리넷, 오보 같은 목관악기. */
    WOODWIND,

    /** 프렌치 호른, 트럼펫 같은 금관악기. */
    BRASS,

    /** 탐파니, 심벌즈 같은 타악기. */
    PERCUSSION,

    /** 바이올린, 첼로 같은 현악기. */
    STRING;
}

애너테이션 타입

애너테이션 타입을 문서화할 때는 애너테이션 타입 자체와 멤버들에도 모두 주석을 달아야 한다.

필드 설명은 명사구로 하고, 애너테이션 타입의 요약 설명은 프로그램 요소에 이 애너테이션을 단다는 것이 어떤 의미인지를 설명한느 동사구로 한다.

// 애너테이션 타입 문서화
/**
 * Indicates that the annotated method is a test method that
 * must throw the designated exception to pass.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    /**
     * The exception that the annotated test method must throw
     * in order to pass. (The test is permitted to throw any
     * subtype of the type described by this class object.)
     */
    Class<? extends Throwable> value();
}
// 한글 버전
/**
 * 이 애너테이션이 달린 메서드는 명시한 예외를 던져야만 성공하는
 * 테스트 메서드임을 나타낸다.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    /**
     * 이 애너테이션을 단 테스트 메서드가 성공하려면 던져야 하는 예외.
     * (이 클래스의 하위 타입 예외는 모두 허용된다.)
     */
    Class<? extends Throwable> value();
}

패키지

패키지를 설명하는 문서화 주석은 package-info.java 파일에 작성한다.

이 파일은 패키지 선언을 반드시 포함해야 하고, 패키지 선언 관련 애너테이션을 추가로 포함할 수 있다.

자바 9부터 지원하는 모듈 시스템도 이와 비슷하다. 모듈 시스템을 사용한다면 모듈 관련 설명은 module-info.java 파일에 작성하면 된다.


스레드 안전성 & 직렬화 가능성

API 문서화에서 자주 누락되는 설명 2가지가 스레드 안전성직렬화 가능성 이다.

클래스 혹은 정적 메서드가 스레드 안전하든 그렇지 않든, 스레드 안전 수준을 반드시 API 설명에 포함해야 한다.

또한 직렬화할 수 있는 클래스라면 직렬화 형태도 API 설명에 기술해야 한다.


메서드 주석 상속 & {@inheritDoc}

자바독은 메서드 주석을 '상속'시킬 수 있다.

자바독은 문서화 주석이 없는 API 요소를 발견하면, 가장 가까운 문서화 주석을 찾아준다. 이때 상위 '클래스'보다 그 클래스가 구현한 '인터페이스'를 먼저 찾는다.
상세한 검색 알고리즘은 자바독 참조 지침(The Javadoc Reference Guide)을 참고하자.

또한 {@inheritDoc} 태그를 사용해 상위 타입의 문서화 주석 일부를 상속할 수 있다.
클래스는 자신이 구현한 인터페이스의 문서화 주석을 재사용할 수 있다는 뜻이다.
이 기능을 활용하면 거의 똑같은 문서화 주석 여러 개를 유지보수하는 부담을 줄일 수 있지만, 사용하기 까다롭고 제약도 조금 있다.
자세한 내용은 오라클 공식 문서를 참고하자.


자바독 문서 검사

1. 자바독 자체 검사

자바독은 프로그래머가 자바독 문서를 올바르게 작성했는 확인하는 기능을 제공한다.
자바 7에서는 명령줄에서 -Xdoclint 스위치를 켜주어야 활성화되고, 자바 8부터는 기본으로 작동한다.

2. 체크스타일 IDE 플러그인

또한 체크스타일(checkstyle) 같은 IDE 플러그인을 사용하면 더 완벽하게 검사된다.

3. HTML 유효성 검사기

자바독이 생성한 HTML 파일을 HTML 유효성 검사기 로 돌리면 문서화 주석의 오류를 한층 더 줄일 수 있다.
HTML 유효성 검사기는 잘못 사용한 HTML 태그를 찾아주는데, 로컬에 내려받아 사용하는 설치형 검사기도 있고 웹에서 바로 사용할 수 있는 W3C 마크업 검사 서비스도 있다.
자바 9,10의 자바독은 기본적으로 HTML 4.01 문서를 생성하지만, 명령줄에서 -html5 스위치를 켜면 HTML 5 버전으로 만들어준다.



📌 핵심 정리

  • 문서화 주석은 여러분 API를 문서화하는 가장 훌륭하고 효과적인 방법이다.
  • 공개 API라면 빠짐없이 설명을 달아야 한다.
  • 표준 규약을 일관되게 지키자.
  • 문서화 주석에 임의의 HTML 태그를 사용할 수 있음을 기억하라.
    단, HTML 메타문자는 특별하게 취급해야 한다.
profile
백엔드 개발자

0개의 댓글