Spring rest docs ์ค์
๐ค ์ ์ฉ ์ด์
ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ ๋ฐฑ์๋ ๊ฐ๋ฐ์๊ฐ ์์ฑํ api ๋ฌธ์๋ฅผ ๋ณด๊ณ api ๋ฅผ ๋งคํํฉ๋๋ค. ๋ฌธ์๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋ ธ๋๋ ฅ์ด ๋ค์ด๊ฐ๋ ๊ฒ์ด๊ณ , ์ฌ๋์ด ์์ฑํ๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ๋ ์ฌํญ์ ์ ๋ฐ์ดํธ๋ฅผ ํ์ง ์๋ ๊ฒฝ์ฐ๋ ์กด์ฌํฉ๋๋ค. spring-rest-docs ๋ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๊ณ , ๋น๋ ์ api ๋ฌธ์๊ฐ ์๋์ผ๋ก ์์ฑ๋ฉ๋๋ค.
ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ๊ฒ์ฆ๋ api ๋ฌธ์๋ฅผ ์๋์ผ๋ก ์์ฑํ ์ ์๋ค!
vs Swagger
Swagger ๋ ๋ฌธ์ํ๋ฅผ ์ํด ๋ง์ด ์ฌ์ฉ๋๋ค๊ณ ํ์ต๋๋ค. ๋ณด๋ค UI ๊ฐ ๊น๋ํด๋ณด์ด๋ ์ฅ์ ์ด ์์์ต๋๋ค. spring-rest-docs ์ ๋ฌ๋ฆฌ ํ ์คํธ ์ฝ๋๊ฐ ์๋ฌด๊ฐ ์๋๋ฏ๋ก ๋น ๋ฅธ ์๊ฐ ๋ด์ ๋ฌธ์๋ฅผ ์์ฑํ ๋ ์ฉ์ดํ ๊ฑฐ๋ผ ์๊ฐ์ด ๋ญ๋๋ค. ๋จ์ ์ผ๋ก๋ ์ปจํธ๋กค๋ฌ ์ฝ๋ ์ฃผ์์ ๋ฌธ์๋ฅผ ์ํ ์ฝ๋๋ฅผ ์์ฑํด์ผ ๋๋ค๋ ์ ์ ๋๋ค. ๊ฐ๋ ์ฑ์ด ์ค์ํ๋ค๊ณ ํ๋จ๋์ด spring-rest-docs ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
โ๏ธ ์ ์ฉ ํ๊ธฐ
1. build.gradle
plugins {
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
ext {
snippetsDir = file('build/generated-snippets') // -- 1 --
}
dependencies {
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
test {
outputs.dir snippetsDir
}
asciidoctor {
dependsOn test
inputs.dir snippetsDir
}
asciidoctor.doFirst {
delete file('src/main/resources/static/docs') // -- 2 --
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs") // -- 3 --
}
build {
dependsOn copyDocument
}
- build ์ ์๋์์ฑ๋ snippet ์ ์ ์ฅ ๊ฒฝ๋ก (ex. request-body)
- asciidoctor ๊ฐ ์คํ๋๋ฉด ์ฐ์ ์ ์ผ๋ก ์ ์ฅ๋ ๋ฌธ์๋ฅผ ์ญ์ ํ๋ค. (์ด๊ธฐํ)
- asciidoctor ์คํ ์ ์์ฑ๋๋ index.html ์ ์ ์ ๊ฒฝ๋ก๋ก ์ด๋
2. MockMvc & restdocs ์ค์
@SpringBootTest
@ExtendWith(RestDocumentationExtension.class)
public class SpringContainerTest {
protected MockMvc mockMvc;
@BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentationContextProvider) throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentationContextProvider))
.build();
}
}
๊ธฐ์กด ์ปจํธ๋กค๋ฌ ํ ์คํธ์์ ์ถ๊ฐ๋ ๋ถ๋ถ์ apply(documentationConfiguration(restDocumentationContextProvider)) ์ ๋๋ค.
RestDocumentationContextProvider: RestDocumentationContext ์ ๋ํ ์ก์ธ์ค ์ ๊ทผ์ ์ ๊ณตํ๋ ์ธํฐํ์ด์ค RestDocumentationContext: RESTful API์ ๋ฌธ์ํ๊ฐ ์ํ๋๋ ์ปจํ ์คํธ ์ธํฐํ์ด์ค
3. ์ปจํธ๋กค๋ฌ ํ ์คํธ ์์ฑ
public class GroupControllerTest extends SpringContainerTest {
@Test
@Transactional
@DisplayName("๊ทธ๋ฃน ๋ฆฌ์คํธ ์กฐํ")
void getGroupList() throws Exception {
mockMvc.perform(get("/api/groups")
.contentType(MediaType.APPLICATION_JSON)
)
.andExpect(status().isOk())
.andDo(document("get-groups",
PayloadDocumentation.responseFields(
PayloadDocumentation.fieldWithPath("success").description("์ฑ๊ณต ์ฌ๋ถ"),
PayloadDocumentation.fieldWithPath("result.[].groupId").description("๊ทธ๋ฃน ์์ด๋"),
PayloadDocumentation.fieldWithPath("result.[].groupName").description("๊ทธ๋ฃน ์ด๋ฆ"),
PayloadDocumentation.fieldWithPath("result.[].totalUsers").description("๊ทธ๋ฃน ์ธ์์")
)));
}
}
์์๋ฐ์ MockMvc ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ์ต๋๋ค. ์์ฒญ ํ๋ผ๋ฏธํฐ ๋ฐ ๋ฐ์ดํฐ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์๋ต ํ๋๊ฐ๊ณผ ์ค๋ช ๊ฐ์ ์ถ๊ฐํ์ต๋๋ค.
4. ๋ฌธ์ ์ค๋ํซ ์์ฑ ํ์ธ
์์ฑํ ํ ์คํธ๋ฅผ ์คํํฉ๋๋ค. ํ ์คํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋๋ฌ๋ค๋ฉด, build ํด๋๊ฐ ์๊ธธ ๊ฒ์ ๋๋ค. ์์ฒญ ํ๋ ๊ฐ์ ์ ๋ ฅํ์ง ์์์ผ๋ฏ๋ก request-fileds ๋ฅผ ์ ์ธํ ๋๋จธ์ง ์ค๋ํซ์ด ์์ฑ๋์ต๋๋ค.
my@notebook build % tree
.
โโโ generated-snippets
โโโ get-groups
โโโ curl-request.adoc
โโโ http-request.adoc
โโโ http-response.adoc
โโโ httpie-request.adoc
โโโ request-body.adoc
โโโ response-body.adoc
โโโ response-fields.adoc
3 directories, 7 files
5. index.adoc ํ์ผ ์์ฑ
src/docs/asciidoc/index.adoc ํ์ผ์ ์์ฑํฉ๋๋ค. ๋ฌธ์์ ๋ฃ๊ณ ์ถ์ ์ค๋ํซ์ ์์ฑํฉ๋๋ค. asciidoc ๊ฐ adoc ํ์ผ์ ์คํ์ผ๋ง ํด์ html ํ์ผ๋ก ๋ณํ์์ผ์ค๋๋ค.
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
= API Docs
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 1
:toc-title: ๊ทธ๋ฃน
== ๊ทธ๋ฃน ๋ฆฌ์คํธ ์กฐํ
=== REQUEST
include::{snippets}/get-groups/http-request.adoc[]
=== REQEUST FIELD
// include::{snippets}/get-groups/request-fields.adoc[]F
=== RESPONSE
include::{snippets}/get-groups/http-response.adoc[]
=== RESPONSE FIELD
include::{snippets}/get-groups/response-fields.adoc[]
6. build ๋ฐ ๋ฌธ์ ํ์ธ
๋ฌธ์ ์ ์ฅ๊ฒฝ๋ก์ธ src/main/resources/static/docs ์ index.html ์ด ์กด์ฌํ๋์ง ํ์ธํฉ๋๋ค. http://localhost:8080/docs/index.html ๋ก ์ด๋ํ๋ฉด..!
๋ง์ฝ ์ค๋ํซ์ ์ ๋ณด๊ฐ ์ ๋์ค์ง ์๋๋ค๋ฉด index.adoc ํ์ผ์์ ์์ฑํ ์ค๋ํซ ๊ฒฝ๋ก๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธํด์ผ ํฉ๋๋ค.
๐ ๋ง์น๋ฉฐ..
์ด๊ธฐ ์ค์ ๋ง ํ๋ฉด ์ดํ ๋ฌธ์ํ๋ฅผ ํ๋ ๊ฒ์ (ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ ์ ์ ) ์ด๋ ต์ง ์์ ๋ณด์ ๋๋ค. ํ์ ํ๋ ๊ฐ๋ฐ์์๊ฒ ๊ฒ์ฆ๋ api๋ฅผ ์ ๊ณตํ๋ค๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ ๊ฐ์น๊ฐ ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ๊ธฐ์กด์ ์ฌ์ฉํ๋ ๋ ธ์ ์ ๋นํด์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๋ ๋จ์ ์ ์์์ต๋๋ค. ๋ค๋ฅธ ์ฌ์ฉ์๋ค์ swagger ui ๋ฅผ ์ฐ๋ํด์ ์ด๋ฅผ ๋ณด์ํ๋ค๊ณ ํด์ ์ดํ์ ์ ์ฉํ๋ ค ํฉ๋๋ค.
์ฐธ๊ณ ์ฌ์ดํธ
https://velog.io/@bagt/API-%EB%AC%B8%EC%84%9C%ED%99%94%EC%99%80-Spring-Rest-Docs