DEV ℧ Developer Diary

[EffectiveJava] item56 - 공개된 API 요소에는 항상 문서화 주석을 작성하라

API를 쓸모 있게 하려면 잘 작성된 문서도 곁들어야 한다. 전통적으로 API 문서는 사람이 직접 작성하므로 코드가 변경되면 매번 함께 수정해줘야 하는데, 자바에서는 자바독(Javadoc)이라는 유틸리티가 이 귀찮은 작업을 도와준다.

자바독(Javadoc) : 소스코드 파일에서 문서화 주석(doc comment; 자바독 주석)이라는 특수한 형태로 기술된 설명을 추려 API 문서로 변환해준다.

주석을 작성하는 방법

문서화 주석을 작성하는 규칙은 문서화 주석 작성법(How to Write Doc Comments) (오라클로 대체) 웹페이지에 기술 되어있다.

자바 4 이후로는 갱신되지 않은 페이지지만, 자바 버전이 올라가며 추가된 중요한 자바독 태그로는 자바 5의 @literal@code, 자바 8의 @implSpec, 자바 9의 @index를 꼽을 수 있다.

올바른 문서화 작성법

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

문서화 주석이 없다면 자바독도 그저 공개 API 요소들이 ‘선언’만 나열해주는게 전부다.

규약에 대한 작성

  • 메서드용 문서화 주석에는 해당 메서드와 클라이언트 사이의 규약을 명료하게 기술해야 한다. 상속용으로 설계된 클래스의 메서드가 아니라면 메서드가 어떻게(how) 동작하는 지가 아니라 무엇(what)을 하는지 기술해야 한다.
  • 클래스가 메서드를 호출하기 위한 전제조건(precondition)과 성공적으로 수행된 후에 만족해야 하는 사후조건(postcondition)도 모두 나열해야 한다.
  • 전제조건과 사후조건 뿐만 아니라 부작용도 문서화 해야한다.
  • 메서드의 계약(contact)을 완벽히 기술하려면 모든 매개변수에 @param 태그를, 반환 타입이 void가 아니라면 @return 태그를, 발생할 가능성이 있는 모든 예외에 @throws 태그를 달아야 한다.

@param, @return, @throws 태그

관례상 @param 태그와 @return 태그의 설명은 해당 매개변수가 뜻하는 값이나 반환값을 설명하는 명사구를 쓴다. 또한 관례상 @param, @return, @throws 태그의 설명에는 마침표를 붙이지 않는다.

다음은 이상의 규칙을 모두 반영한 문서화 주석의 예다. List<E>의 get 메서드를 예시로 가져왔다.

/**
 * 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.</p>
 *
 * @param index index of the element to return
 * @return the element at the specified position in this list
 * @throws IndexOutOfBoundsException if the index is out of range
 *         ({@code index < 0 || index >= size()})
 */
E get(int index);

자바독 유틸리티는 문서화 주석을 HTML로 변환하므로 문서화 주석 안의 HTML 요소들이 최종 HTML 문서에 반영된다. 위 예시는 문서화 주석에 HTML 태그(<p>와 <i>)를 사용했다.

영문 문서화 주석에서 쓴 “this list” 라는 내용을 보자 인스턴스 메서드의 문서화 주석에 쓰인 “this”는 호출된 메서드가 자리하는 객체를 가리킨다.

{@code}

이 태그의 효과는 두가지다.

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

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

@implSpec

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

이 패턴은 @implSpec 태그로 문서화 한다. @implSpec 주석은 해당 메서드와 하위 클래스 사이의 계약을 설명하여, 하위 클래스들이 그 메서드를 상속하거나 super 키워드를 이용해 호출할 때 그메서드가 어떻게 동작하는지 인지하게 한다.

아래의 예시는 List<E>의 isEmpty 메서드를 가져왔다.

/**
 * Returns {@code true} if this list contains no elements.
 *
 * @implSpec
 * This implementation returns {@code this.size() == 0}.
 *
 * @return {@code true} if this list contains no elements
 */
boolean isEmpty();

자바 11까지도 자바독 명령줄에서 -tag “implSpec:a:Implementation Requirements:” 스위치를 켜주지 않으면 @implSpec 태그를 무시해버린다.

{@literal}

API 설명에 <, >, & 등의 HTML 메타문자를 포함시키려면 {@literal} 태그로 감싸줘야 한다.

이 태그는 HTML 마크업이나 자바독 태그를 무시하게 해준다. {@code} 태그와 비슷하지만 코드 폰트로 렌더링 하지는 않는다.

* A geometric series converges if {@literal |r| < 1}.

요약설명 (summary description)

각 문서화 주석의 첫 번째 문장은 해당 요소의 요약 설명으로 간주된다. 이번 아이템 첫 예시 코드의 “Returns the element at the specified position in this list.”라는 문장이 여기에 속한다.

요약 설명은 반드시 대상의 기능을 고유하게 기술해야 한다. 한 클래스(혹은 인터페이스) 안에서 요약 설명이 똑같은 멤버(혹은 생성자)가 둘 이상이면 안 된다.

요약 설명에는 마침표(.)에 주의해야 한다. 예를 들어 문서화 주석의 첫 문장이 “A suspect, such as Colonel Mstard or Mrs. Peacock.” 라면 첫 번째 마침표가 나오는 “A suspect, such as Colonel Mstard or Mrs.” 까지만 요약 성명이 된다.

요약 설명이 끝나는 판단 기준은 {<마침표> <공백> <다음 문장 시작>} 패턴의 <마침표> 까지이다.

가장 좋은 해결 책은 의도치 않은 마침표를 포함한 텍스트를 {@literal}로 감싸주는 것이다.

/**
 * A suspect, such as Colonel Mstard or {@literal Mrs. Peacock}.
 */
public class Suspect { ... }

{@summary}

자바 10 부터는 {@summary}라는 요약 설명 전용 태그가 추가되어 다음처럼 한결 깔끔하게 처리할 수 있다.

/**
 * {@summary A suspect, such as Colonel Mstard or Mrs. Peacock.}
 */
public class Suspect { ... }

요약설명의 작성법

메서드와 생성자의 요약 설명은 해당 메서드와 생성자의 동작을 설명하는 (주어가 없는) 동사구여야 한다.

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

예시와 같이 2인칭 문장이 아닌 3인칭 문장으로 써야한다.

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

  • 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 문서를 확인하는 일이 한결 수월해졌다.

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

* This method complies with the {@index IEEE 754} standard.

문서화할때 주의할 점

문서화 주석에서 제네릭, 열거 타입, 애너테이션은 특별히 주의해야 한다. 제네릭 타입이나 제네릭 메서드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.

/**
 * An object that maps keys to values.  A map cannot contain duplicate keys;
 * each key can map to at most one value.
 *
 * (나머지 설명은 생략)
 *
 * @param <K> the type of keys maintained by this map
 * @param <V> the type of mapped values
 */
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 tripani and cymbals */
    PERCUSSION,

    /** Stringed instruments, such as violin and cello */
    STRINGS
}

애너테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 한다. 애너테이션 타입 자체도 물론이다. 필드 설명은 명사구, 타입 요약 설명은 애너테이션을 달았을 때 어떤 의미인지를 설명하는 동사구로 한다.

/**
 * 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();
}

패키지, 모듈의 문서화 주석

패키지를 설명하는 문서화 주석은 package-info.java 파일에 작성한다. 이파일은 패키지 선언을 반드시 포함해야 한다. 모듈 시스템을 사용한다면 모듈 관련 설명은 module-info.java 파일에 작성하면 된다.

스레드 안정성, 직렬화에 대한 문서화

API 문서화에서 스레드 안정성과 직렬화 가능성에 대한 설명이 자주 누락된다. 클래스 혹은 정적 메서드가 스레드 안전하든 그렇지 않든, 스레드 안전 수준을 반드시 API 설명에 포함해야 한다.

문서화의 상속

자바독은 메서드 주석을 ‘상속’시킬 수 있다. 문서화 주석이 없는 API 요소를 발견하면 자바독이 가장 가까운 문서화 주석을 찾아준다. 상위 ‘클래스’보다 그 클래스가 구현한 ‘인터페이스’를 먼저 찾는다.

{@inheritDoc} 태그를 사용해 상위 타입의 문서화 주석 일부를 상속 할 수 있다.

자바독 문서의 검사

자바독은 프로그래머가 자바독 문서를 올바르게 작성했는지 확인하는 기능을 제공하며, 이번 아이템에서 소개한 권장사항 중 상당수를 검사해준다.

자바7에서는 명령줄에서 -Xdoclint 스위치를 켜주면 이 기능이 활성화 되고, 자바 8부터는 기본으로 작동한다.

자바 9와 10의 자바독은 기본적으로 HTML 4.01 문서를 생성하지만, 명령줄에서 -html5 스위치를 켜면 HTML 5 버전으로 만들어준다.