들어가며
API 문서화는 원활한 협업을 위해 꼭 필요한 작업 중 하나로, Swagger 혹은 Spring REST Docs 등으로 진행할 수 있다.
이 중 Swagger의 경우, 운영 코드에 어노테이션을 붙여야 하므로 코드가 지저분해질 수 있다는 아쉬운 점이 존재한다.
반면 Spring REST Docs는 운영 코드에 영향을 주지 않고, 테스트 코드를 기반으로 생성되기 때문에 운영 코드를 그대로 보존할 수 있으면서 API 문서화를 진행할 수 있다. 또한 테스트를 기반으로 생성되므로, 테스트 코드 작성을 강제화할 수 있다는 장점도 있다.
이번 포스트를 통해 이러한 Spring REST Docs를 어떻게 적용하는지에 대해 알아보도록 하자.
Spring REST Docs 문서화 과정
Spring REST Docs의 API 문서화는 다음과 같은 과정으로 이루어진다.
- 테스트 코드 작성 및 실행을 통해 Snippet 생성
- adoc 파일 생성 및 Snippet 결합
- HTML API 문서 생성
이제 위 과정을 순차적으로 진행해보도록 하자.
Snippet 생성을 위한 gradle 설정
테스트 코드를 작성하기에 앞서 Snippet을 생성하기 위한 gradle 설정을 먼저 진행해주도록 하자.
우선 이번 포스트에서는 MockMvc를 기반으로 REST Docs를 작성해볼 것이다.
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
MockMvc 이외에도 WebTestClient나 Rest Assured를 기반으로 작성할 수도 있다.
자세한 건 Spring REST Docs 문서를 참고하도록 하자.
다음으로 테스트 코드 실행 시 생성되는 Snippet이 저장될 디렉토리를 지정해주어야 한다.
ext {
snippetsDir = file('build/generated-snippets')
}
dependencies {
...
}
tasks.named('test') {
useJUnitPlatform()
outputs.dir snippetsDir
}
gradle 작성법이 약간 생소할 수 있는데, ext는 gradle에서 사용할 전역 변수를 지정하는 것이라고 보면 된다.
여기서는 snippetsDir이라는 변수에 Snippet이 저장될 디렉토리를 담아주었다.
그리고 test 테스크에서 생성될 Snippet에 대한 output 디렉토리를 snippetsDir로 설정해주었다.
테스트 코드 작성 및 실행을 통한 Snippet 생성
이제 앞서 의존성을 가져온 spring-restdoc-mockmvc를 이용하여 테스트 코드를 작성해보도록 하자.
@Test
void createPostTest() throws Exception {
// when
ResultActions perform = mockMvc.perform(post("/api/post")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("title", "test title")
.param("contents", "test contents"));
// then
MvcResult result = perform.andReturn();
perform.andExpect(status().isCreated());
assertThat(result.getResponse().getHeader(HttpHeaders.LOCATION))
.isEqualTo("/api/post/" + postId);
// restdocs
perform.andDo(document("createPost",
requestParameters(
parameterWithName("title").description("제목"),
parameterWithName("contents").description("내용")
),
responseHeaders(
headerWithName(HttpHeaders.LOCATION).description("게시물 접근 주소")
))
);
}
위 코드에서 restdocs 부분이 Snippet 생성을 위해 필요한 부분이다.
ResultActions 객체의 andDo() 메서드를 이용하여 Snippet을 생성하면 되는데, 위 예제는 createPost를 Identifier로 하고 요청 파라미터와 응답 헤더에 대한 Snippet을 작성한 예이다.
Snippet을 작성하는 방법은 Spring REST Docs 문서를 참고하면 수월하게 작성할 수 있다.
이제 이렇게 작성한 테스트 코드를 실행해보면, 다음과 같이 이전에 설정한 Snippet 저장 디렉토리에 Snippet들이 생성된 것을 확인할 수 있다.

adoc 파일 생성 및 Snippet 결합
앞서 생성한 Snippet을 사용하기 위해서는 템플릿 파일인 adoc 파일을 생성해주어야 하는데, 해당 파일의 경로는 src/docs/asciidoc 아래에 두면 된다.

그리고 이렇게 생성한 adoc 파일과 Snippet을 결합하여 API 문서를 작성해주도록 하자.

위 코드와 같이 앞서 생성한 Snippet의 경로를 가져와서 취향에 맞게 API 문서를 작성해주면 된다.
HTML API 문서 생성
마지막으로 앞서 작성한 adoc 파일을 기반으로 HTML API 문서를 생성해볼 차례이다. 방법은 asciidoctor를 이용하여 adoc 파일을 HTML로 변환하는 것이다.
이를 위해 먼저 build.gradle 파일에 asciidoctor 플러그인을 추가해주어야 한다.
plugins {
...
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}
그 후, 다음과 같이 asciidoctor 테스크를 설정하여 HTML 변환 작업을 수행하도록 해야한다.
이때 asciidoctor 테스크는 test 테스크 이후에 실행되어야 하므로 dependsOn test 설정을 해주었고, test 테스크 실행 후 생성된 Snippet을 사용해야 하기 때문에 snippetDir을 입력으로 주었다.
asciidoctor {
dependsOn test
inputs.dir snippetsDir
}
여기까지 설정한 뒤, asciidoctor를 실행하면 build/docs/asciidoc 경로에 이전에 작성한 api.adoc 파일이 HTML 파일로 변환된 것을 확인할 수 있다.

마지막으로 생성된 API 문서를 접근하기 쉬운 위치로 복사해주는 테스크를 작성해주도록 하자.
task copyDocs(type: Copy) {
dependsOn asciidoctor
from file('build/docs/asciidoc')
into file('src/main/resources/static/docs')
}
API 문서 접근
최종 build.gradle 파일은 다음과 같다.
plugins {
...
id 'org.asciidoctor.jvm.convert' version '3.3.2'
}
...
ext {
snippetsDir = file('build/generated-snippets')
}
dependencies {
...
}
tasks.named('test') {
useJUnitPlatform()
outputs.dir snippetsDir
}
asciidoctor {
dependsOn test
inputs.dir snippetsDir
}
task copyDocs(type: Copy) {
dependsOn asciidoctor
from file('build/docs/asciidoc')
into file('src/main/resources/static/docs')
}
bootJar {
dependsOn copyDocs
}
이제 ./gradlew bootJar를 수행하여 API 문서가 생성되고, 원하는 위치로 복사되는지 확인해보도록 하자.
잘 수행되었다면 스프링을 구동한 후, docs/api.html로 접근 시 API 문서를 확인할 수 있을 것이다.


Reference
'Backend > Spring' 카테고리의 다른 글
Redis를 활용한 캐싱 적용기 (0) | 2022.05.20 |
---|---|
테스트 격리하기 (0) | 2022.05.04 |
트랜잭션 전파 알아보기 (0) | 2022.04.26 |
AOP와 빈 후처리기를 이용한 부가 기능 분리 (0) | 2022.03.26 |
다이내믹 프록시를 활용한 JPA QueryCounter 구현기 (0) | 2022.03.20 |