Spring Boot REST API 프로젝트에 API 문서 자동화 툴로 뭘 써야 하나...
Swagger vs Spring rest docs
- Swagger - 적용 난이도 쉬움, 이쁜 UI, API 테스트 기능, 코드 기반
- Spring rest docs - 적용 난이도 보통, 단순 UI, 테스트 기반
장단점이 분명하지만, 내부용으로 후딱 만들어 쓸 것이라면 Swagger. 외부에 제공해야 한다면 정갈해 보이는 Spring rest docs 이 아닐까 싶다.
문서화를 위한 gradle 구성
- Step 1. 코드 테스트 -> 에러가 발생하지 않으면 해당 api 내용(snippets: asciidoc 파일) 생성.
- Step 2. Test 로부터 생성된 snippets 을 import 하도록 api 문서 템플릿(asciidoc 파일) 을 생성하고 빌드하여 API 문서(html) 완성.
- Step 3. 생성된 html 파일을 외부에 노출하도록 static 디렉토리에 복제.
plugins {
id 'org.springframework.boot' version '2.5.6'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
// gradle 7 이상은 org.asciidoctor.convert 대신 org.asciidoctor.jvm.convert 사용
id 'org.asciidoctor.jvm.convert' version '3.3.2' // Step 1
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
bootJar { // Step 3
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}"
into "${sourceSets.main.output.resourcesDir}/static/docs"
}
}
asciidoctor { // Step 2
sourceDir 'src/main/asciidoc'
attributes \
'snippets': file('build/generated-snippets')
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // Step 1
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
이 build.gradle 파일은 가장 기본적인 세팅이므로 추후 익숙해지면 task 를 통합하거나 build 경로를 수정하여 커스터마이징 해서 편하게 쓰면 되겠다. 예를 들면...
asciidoctor {
dependsOn test
sourceDir 'src/main/asciidoc'
outputDir "${sourceSets.main.output.resourcesDir}/static/docs"
attributes \
'snippets': file('build/generated-snippets')
}
3개의 task (단위 Test - html 생성 - html 복사) 를 하나로 통합한 예...
아무튼 build.gradle 파일을 수정했으면 JUnit 와 MockMvc 를 사용하여 Test 를 작성한다. (model, controller 생략)
MockMvc, REST Assured 중 서버 구동없이 컨트롤러 단위 테스트가 쉬운 MockMvc 를 사용한다.
@WebMvcTest(TestController.class)
@AutoConfigureRestDocs(outputDir = "build/generated-snippets")
public class TestControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
mockMvc.perform(RestDocumentationRequestBuilders.get("/book/{id}",1).accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(document("book",
pathParameters(
parameterWithName("id").description("book unique id")
),
responseFields(
fieldWithPath("id").description("book unique id"),
fieldWithPath("title").description("title")
)
))
.andExpect(jsonPath("$.id", is(notNullValue())))
.andExpect(jsonPath("$.title", is(notNullValue())));
}
}
* 아래 에러 발생시:
urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?
> pathParameters 을 사용할 때는 MockMvcBuilders.get 대신 RestDocumentationRequestBuilders.get 사용
Test 코드에서 document(identifier, ...) 에 기반하여 snippets 의 내용을 구성하게 되며, andExpect 에서 에러가 발생하지 않으면 outputDir 에 snippets 들이 생성된다.
curl-request.adoc
http-request.adoc
http-response.adoc
...
정상적으로 생성됐다면 이 snippets 들을 불러올 템플릿 asciidoc 파일을 생성한다. (/src/main/asciidoc/)
= Document Title
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toc-title: API 명세서
:toclevels: 4
[[api]]
== Book Api
api 에 관련된 설명을 이곳에 적습니다..
include::{snippets}/book/curl-request.adoc[]
include::{snippets}/book/http-request.adoc[]
include::{snippets}/book/path-parameters.adoc[]
include::{snippets}/book/http-response.adoc[]
include::{snippets}/book/response-fields.adoc[]
* asciidoc 파일 작성시 참고
https://docs.asciidoctor.org/asciidoc/latest/
https://narusas.github.io/2018/03/21/Asciidoc-basic.html
Spring REST Docs는 기본적으로 Asciidoctor 를 사용하지만, Markdown 을 사용할 수도 있다.
간략하게 이런식으로 작성하고 bootJar 를 실행하면 /build/docs/asciidoc/book.html 파일이 생성된 것을 확인할 수 있다.
bootRun 을 실행하면 해당 파일이 /build/resources/main/static/docs 에 복사되며 http://localhost/docs/book.html 에서 확인 가능.
WRITTEN BY
- 손가락귀신
정신 못차리면, 벌 받는다.