아이템 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라. - ksw6169/effective-java GitHub Wiki
- 자바에서는 API 문서화를 위해 자바독(javadoc) 이라는 유틸리티를 제공한다.
- 자바독은 소스 파일에서 문서화 주석(doc comment; 자바독 주석) 이라는 특수한 형태로 기술된 설명을 추려 API 문서로 변환해준다.
- API를 올바로 문서화하려면 공개된 모든 요소(클래스, 인터페이스 등)에 문서화 주석을 달아야 한다.
- 직렬화할 수 있는 클래스는 직렬화 형태에 관해서도 적어야 한다.
- 기본 생성자에는 문서화 주석을 달 수 없으니 공개 클래스는 절대 기본 생성자를 사용하면 안된다.
- 유지보수까지 고려한다면 대다수의 공개되지 않은 요소에도 문서화 주석을 달아야 한다.
- 메소드용 문서화 주석에는 해당 메소드와 클라이언트 사이의 규약을 명료하게 기술해야 한다.
- 상속용으로 설계된 클래스의 메소드가 아니라면 그 메소드가 어떻게 동작하는지가 아니라 무엇을 하는지를 기술해야 한다.
- 메소드 문서화 주석에는 해당 메소드를 호출하기 위한 전제조건(precondition)을 모두 나열해야 한다.
- 또한 메소드가 성공적으로 수행된 후에 만족해야 하는 사후조건(postcondition)도 모두 나열해야 한다.
- 일반적으로 전제조건은
@throws
태그로 비검사 예외를 선언하여 암시적으로 기술한다. (비검사 예외 하나가 전제 조건 하나와 연결되는 것이다.) - 또한,
@param
태그를 이용해 그 조건에 영향받는 매개변수에 기술할 수도 있다.
- 전제조건과 사후조건뿐만 아니라 부작용도 문서화해야 한다.
- 부작용이란 사후조건으로 시스템의 상태에 어떠한 변화를 가져오는 것을 말한다.
- 예컨대 백그라운드 스레드를 시작시키는 메소드라면 그 사실을 문서에 밝혀야 한다.
- 메소드의 계약(contract)을 완벽하게 기술하려면 모든 매개변수에
@param
태그를 달아야 한다. - 관례상 매개변수가 뜻하는 값을 설명하는 명사구를 쓴다. (드물게는 산술 표현식도 쓰기도 한다.)
- 관례상 태그의 설명에는 맞춤표를 붙이지 않는다.
- 반환 타입이 void가 아니라면
@return
태그를 달아야 한다. - 관례상 반환값을 설명하는 명사구를 쓴다. (드물게는 산술 표현식도 쓰기도 한다.)
- 관례상 태그의 설명에는 맞춤표를 붙이지 않는다.
- 개발자끼리 상호 협의한 프로젝트 규약에서
@return
태그의 설명이 메소드 설명과 같다면@return
태그를 생략해도 좋다.
- 발생할 가능성이 있는 모든 예외에
@throws
태그를 달아야 한다. - 설명은 if로 시작해 해당 예외를 던지는 조건을 설명하는 절이 뒤따른다.
- 관례상 태그의 설명에는 맞춤표를 붙이지 않는다.
@param, @return, @throws의 설명에는 맞춤표를 붙이지 않는다?
설명을 한글로 작성할 경우 온전한 종결 어미로 끝나면 마침표를 써주는 게 일관돼 보인다.
/**
* 이 리스트에서 지정한 위치의 원소를 반환한다.
*
* <p>이 메소드는 상수 시간에 수행됨을 보장하지 <i>않는다</i>. 구현에 따라
* 원소의 위치에 비례해 시간이 걸릴 수도 있다.</p>
*
* @param index 반환할 원소의 인덱스; 0 이상이고 리스트 크기보다 작아야 한다.
* @return 이 리스트에서 지정한 위치의 원소
* @throws IndexOutOfBoundsException index가 범위를 벗어나면,
* 즉, ({@code index < 0 || index >= this.size()}) 이면 발생한다.
*/
E get(int index);
- 자바독 유틸리티는 문서화 주석을 HTML로 변환하므로 문서화 주석 안의 HTML 요소들이 최종 HTML 문서에 반영된다.
- 관례상 인스턴스 메소드의 문서화 주석에 사용된 this는 호출된 메소드가 자리하는 객체를 가리킨다.
- 태그로 감싼 내용을 코드용 폰트로 렌더링한다.
- 태그로 감싼 내용에 포함된 HTML 요소나 다른 자바독 태그를 무시한다.
- 문서화 주석에 여러 줄로 된 코드 예시를 넣으려면
{@code}
태그를 다시<pre>
태그로 감싸면 된다. (<pre>{@code ... 코드 ... }</pre>
)
- 클래스를 상속용으로 설계할 때는 자기사용 패턴에 대해서도 문서로 남겨 다른 프로그래머에게 그 메소드를 올바르게 재정의하는 방법을 알려줘야 한다.
- 이 때 자기 사용 패턴은 자바 8에 추가된
@implSpec
태그로 문서화한다. - 자바 11까지도 자바독 명령줄에서
-tag "implSpec:a:Implementation Requirements:"
를 사용하지 않으면@implSpec
태그를 무시해버린다.
/**
* 이 컬렉션이 비었다면 true를 반환한다.
*
* @implSpec
* 이 구현은 {@code this.size() == 0}의 결과를 반환한다.
*
* @return 이 컬렉션이 비었다면 true, 그렇지 않으면 false
*/
public boolean isEmpty() { ... }
- API 설명에
<, >, &
등의 HTML 메타문자를 포함시키려면{@literal}
태그로 감싸야 한다. - 이 태그는 HTML 마크업이나 자바독 태그를 무시하게 해준다.
-
{@code}
태그와 비슷하지만 코드 폰트로 렌더링하지 않는다.
* {@literal |r| < 1}이면 기하 수열이 수렴한다.
참고
문서화 주석은 코드에서건 변환된 API 문서에서건 읽기 쉬워야 한다는 게 일반 원칙이다. 양쪽을 만족하지 못하겠다면 API 문서에서의 가독성을 우선시하자.
- 각 문서화 주석의 첫 번째 문장은 해당 요소의 요약 설명으로 간주된다.
- 요약 설명은 반드시 대상의 기능을 고유하게 설명해야 한다.
- 즉, 한 클래스(혹은 인터페이스) 안에 요약 설명이 똑같은 멤버(혹은 생성자)가 둘 이상이면 안된다.
- 요약 설명은 첫 번째 마침표(.)가 나오는 설명까지만 요약 설명으로 간주하기 때문에 주의해야 한다.
- 만약 요약 설명에서 의도치 않은 마침표를 포함했다면
@{literal}
로 감싸주면 된다.
/**
* 요약 설명이 끝나는 판단 기준은 다음과 같다.
* 여기서 <공백>은 스페이스, 탭, 줄바꿈이며, <다음 문장 시작>은 '소문자가 아닌' 문자다.
*/
{<마침표> <공백> <다음 문장 시작>} 패턴의 <마침표>까지를 요약 설명으로 간주
위 기준에 따르면 다음 코드에서 요약 설명은 "머스타드 대령이나 Mrs." 까지가 요약 설명이 된다.
/**
* 머스타드 대령이나 Mrs. 피콕 같은 용의자.
*/
public class Suspect { ... }
이를 올바르게 수정하면 다음과 같다.
/**
* 머스타드 대령이나 {@literal Mrs. 피콕} 같은 용의자.
*/
public class Suspect { ... }
자바 10부터는 요약 설명 전용 태그가 추가되어 다음처럼 한결 깔끔하게 처리할 수 있다.
/**
* {@summary A suspect, such as Colonel Mustard or Mrs. Peacock.}
*/
public enum Suspect { ... }
메소드와 생성자의 요약 설명은 해당 메소드와 생성자의 동작을 설명하는 (주어가 없는) 동사구여야 한다.
- ArrayList(int initialCapacity) : 지정한 초기 용량을 갖는 빈 리스트를 생성한다.
- Collection.size() : 이 컬렉션 안의 원소 개수를 반환한다.
클래스, 인터페이스, 필드의 요약 설명은 자기 자신을 설명하는 명사절이어야 한다.
- Instant : 타임라인상의 특정 순간
- Math.PI : 원주율(pi)에 가장 가까운 double 값
- 자바 9부터는 자바독이 생성한 HTML 문서에 검색(색인) 기능이 추가되었다.
- API 문서 페이지 우측 상단에 있는 검색창에 키워드를 입력하면 해당 기능을 사용할 수 있다.
- 클래스, 메소드, 필드 같은 API 요소의 색인은 자동으로 만들어지지만 원한다면
{@index}
태그를 사용해 API에서 중요한 용어를 추가로 색인할 수 있다.
* 이 메소드는 {@index IEEE 754} 표준을 준수한다.
제네릭 타입이나 제네릭 메소드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.
/**
* 키와 값을 매핑하는 객체. 맵은 키를 중복해서 가질 수 없다.
* 즉, 키 하나가 가리킬 수 있는 값은 최대 1개다.
*
* (나머지 설명은 생략)
*
* @param <K> 이 맵이 관리하는 키의 타입
* @param <V> 매핑된 값의 타입
*/
public interface Map<K, V> { ... }
열거 타입을 문서화할 때는 상수들에도 주석을 달아야 한다.
/**
* 심포니 오케스트라의 악기 세션.
*/
public enum OrchestraSection {
/** 플루트, 클라리넷, 오보 같은 목관악기. */
WOODWIND,
/** 프렌치 호른, 트럼펫 같은 금관악기. */
BRASS,
/** 탐파니, 심벌즈 같은 타악기. */
PERCUSSION,
/** 바이올린, 첼로 같은 현악기. */
STRING;
}
- 어노테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 한다. (타입 자체도 포함)
- 필드 설명은 명사구로 한다.
- 요약 설명은 프로그램 요소에 이 어노테이션을 다는 것이 어떤 의미인지를 설명하는 동사구로 한다.
/**
* 이 어노테이션이 달린 메소드는 명시한 예외를 던져야만 성공하는
* 테스트 메소드임을 나타낸다.
*/
public @interface ExceptionTest {
/**
* (이 어노테이션을 단 테스트 메소드가 성공하려면 던져야 하는 예외.
* (이 클래스의 하위 타입 예외는 모두 허용된다.)
*/
Class<? extends Throwable> value();
}
- 패키지를 설명하는 문서화 주석은
package-info.java
파일에 작성한다. - 이 파일은 패키지 선언에 반드시 포함해야 하며, 패키지 선언 관련 어노테이션을 추가로 포함할 수도 있다.
- 자바 9의 모듈 시스템도 마찬가지로 모듈 관련 설명은
module-info.java
파일에 작성하면 된다.
IntelliJ에서 package-info.java, module-info.java 파일 생성 방법
IntelliJ 기준 패키지 선택 후 우클릭을 하면 해당 파일을 생성할 수 있다.
- 클래스 혹은 정적 메소드가 스레드 안전하든 그렇지 않든, 스레드 안전 수준을 반드시 API 설명에 포함해야 한다.
- 또한 직렬화할 수 있는 클래스라면 직렬화 형태도 API 설명에 기술해야 한다.
- 자바독은 메소드 주석을 '상속' 시킬 수 있다.
- 문서화 주석이 없는 API 요소를 발견하면 자바독이 가장 가까운 메소드 주석을 찾아준다.
- 이 때 상위 클래스보다 그 클래스가 구현한 인터페이스를 먼저 찾는다.
- 상위 타입의 문서화 주석 일부를 상속할 수 있다.
- 클래스는 자신이 구현한 인터페이스의 문서화 주석을 재사용할 수 있다는 뜻이다.
- 이 기능을 활용하면 똑같은 문서화 주석을 여러 개를 유지보수하는 부담을 줄일 수는 있지만, 사용하기 까다롭고 제약이 조금 있다.
여러 클래스가 상호작용하는 복잡한 API라면 문서화 주석 외에도 전체 아키텍처를 설명하는 별도의 설명이 필요할 때가 간혹 있다. 이 경우에는 설명 문서가 있다면 관련 클래스나 패키지의 문서화 주석에서 그 문서의 링크를 제공해주면 좋다.
- 자바독은 프로그래머가 자바독 문서를 올바르게 작성했는지 확인하는 기능을 제공한다.
- 자바 7에서는
-Xdoclint
옵션을 사용하면 이 기능이 활성화되며 자바 8부터는 기본으로 동작한다. - 체크스타일(checkstyle) 같은 IDE 플러그인을 사용하면 더 완벽하게 검사된다.
- 자바독이 생성한 HTML 파일을 HTML 유효성 검사기로 돌리면 오류를 한층 더 줄일 수 있다.
- 문서화 주석은 API를 문서화하는 가장 훌륭하고 효과적인 방법이다.
- 공개 API라면 빠짐없이 설명을 달아야 하며 표준 규약을 일관되게 지키자.
- 문서화 주석에 임의의 HTML 태그를 사용할 수 있음을 기억하라.
- 단, HTML 메타 문자는 특별하게 취급해야 한다.
- Effective Java 3/E