DevLog ๐ถ
[Spring] Rest-docs ์ฐ๋ํ๊ธฐ (version 3.3.2) ๋ณธ๋ฌธ
๐ฑ ๋ค์ด๊ฐ๊ธฐ ์
์ฌ์ค rest-docs๋ ๋ฏธ์ ์๊ตฌ์ฌํญ์ ์๋์์ง๋ง, ๊ฐ์ธ์ ์ธ ์์ฌ์ผ๋ก ํ ๋ฒ ์ง์ ๊ตฌ์ถํด๋ณด๊ณ ์ถ๋ค๋ ์๊ฐ์ด ๋ค์์ด์ ๋ฏธ์ ํ๋ ๊น์ ํจ๊ป ์งํํ๊ฒ ๋์๋ค. (์ด์ฐจํผ ์ถํ ๋ฏธ์ ์งํํ๋ฉด์ ํ๋ก ํธ ํฌ๋ฃจ์ ํ์ ํ๊ฒ ๋๋ฉด ์ธํ ํด์ผ ํ๋๊น...)
RestDocs๋ฅผ ์ฐ๋ํ ๋ ์ธ์ ํ ์คํธ ๋ ๋ฒจ์์ ์งํํ ์ง (RestAssured) ์๋๋ฉด ์ปจํธ๋กค๋ฌ์ ๋จ์ ํ ์คํธ์์ ์งํํ ์ง ๊ณ ๋ฏผํ์๋๋ฐ, RestAssured๋ฅผ ํตํด ์ฌ์ฉํ๊ฒ ๋๋ฉด BDD ์คํ์ผ์ด๋ผ ๋ ์ง๊ด์ ์ผ ๊ฒ ๊ฐ๊ธด ํ์ง๋ง ์๋ฌด๋๋ ์๋๊ฐ ๋๋ฆฌ๋ค ๋ณด๋๊น ๊ทธ๋ฅ ์ปจํธ๋กค๋ฌ ๋ ์ด์ด์์ ์งํํ๋ค. (์ค์ ๋ก๋ ์ปจํธ๋กค๋ฌ ๋ ๋ฒจ์์ ๋ง์ด ์งํํ๋ ๊ฒ ๊ฐ๋ค.)
โ๏ธ Rest-docs๊ฐ ์ด๋ค ๊ฑฐ์ง?
Spring Rest Docs๋ Restful ์๋น์ค์ ๋ํด ์ ํํ๊ณ ์ฝ๊ธฐ ์ฌ์ด ๋ฌธ์๋ฅผ ์์ฑํ ๋ ๋์์ ์ฃผ๋ ๋ฌธ์ ์๋ํ ๋๊ตฌ์ด๋ค.
ํํ ๋ฐฑ์๋, ํ๋ก ํธ์๋๊ฐ ํ์ ํ ๋ ํ๋ก ํธ์๋๋ API endPoint๋ฅผ ์๊ณ ์์ด์ผ ํด๋น API๋ก ์์ฒญ์ ๋ณด๋ด์ response๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ค. ๋ง์ฝ ๋ฐฑ์๋ ๊ฐ๋ฐ์๊ฐ API ๋ฌธ์๋ฅผ ์์ ํ๋ค๋ฉด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ํํ ์ด๋ป๊ฒ ์๋ ค์ผ ํ ๊น?
์ง์ ๋ฉ์ ์ ๋ก ๐โ๏ธ 'oo๋, xxx endpoint์ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ์๊ตฌ์ฌํญ์ด ๋ณ๊ฒฝ๋์์ด์' ๋ผ๊ณ ๋งํ ์๋ ์๊ฒ ์ง๋ง, ์ด๋ ๋งค์ฐ ๋ฒ๊ฑฐ๋กญ๋ค.
๋ง์ฝ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ๋น์ฅ ์์ ํด์ผ ํ๋ hotfix ์๊ตฌ์ฌํญ์์๋ ๋ฉ์ ์ ๋ฅผ ์ฝ์ง ๋ชปํ๋ค๋ฉด? ํน์, ์ํ๋ ์๊ฐ์ ์๋ก ์ํตํ ์ ์๋ค๋ฉด? ํ๋์ ์ ํํ๋ ๋ฌธ์๋ฅผ ํตํด์ ๊ด๋ฆฌํ๋ ๊ฒ ํจ์ฌ ์ฌ์ธ ๊ฒ์ด๋ค.
Swagger๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์๊ณ , Rest docs๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์์ ํ ๋ฐ ๊ฐ๊ฐ์ ์ฅ๋จ์ ์ด ๋๋ ทํ๋ค.
Swagger์ ๊ฒฝ์ฐ ํ ์คํธ ์ฝ๋ ์์ฑ์ ๋ถ๋ด์ด ์์ง๋ง, ํ๋ก๋์ ์ฝ๋ ์์ฒด์ ๋ฌธ์์ ๋ํ ์ค์ ๋ด์ฉ์ด ์ด๋ ธํ ์ด์ ์ผ๋ก ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ ์ด ์์ฒด๊ฐ ๋ถ๋ด์ด ๋ ์ ์๋ค. Swagger์์ Rest docs๋ก ์ ํํด์ผ ํ๋ค๋ฉด ํ๋ก๋์ ์ฝ๋ ์์ฒด๋ฅผ ์ ๋ถ ๋ฐ๊ฟ์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์ง๋ง, Rest-docs๋ณด๋ค๋ ๋น๊ต์ ์ ์ฉํ๊ธฐ๊ฐ ์ฌ์ฐ๋ฉฐ, ๋ฌธ์ ์์ฒด๋ ์ฐ๋ฆฌ๊ฐ ์ง์ ๊ตฌ์ถํ ํ์์์ด ์์์ ๋ง๋ค์ด์ฃผ๊ณ , ์ง์ ํ ์คํธ๋ฅผ ํด๋ณผ ์ ์๋ ํ๋ฉด์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ์ ์ฅ์์๋ ์กฐ๊ธ ๋ ํธํ ์๋ ์๋ค.
๋ฐ๋ฉด์ Rest-docs์ ๊ฒฝ์ฐ ํ ์คํธ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ธฐ ๋๋ฌธ์ ํ๋ก๋์ ์ฝ๋์๋ ์ํฅ์ ๋ผ์น์ง ์๋๋ค.
์ด ๋ง์ ๊ณง, ํ ์คํธ ์ฝ๋๊ฐ ์ ๋ง ๊ผผ๊ผผํ๊ฒ ์ง์ฌ ์์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ์ฑ๊ณตํ์ ๋, ์์ธ๊ฐ ๋ฐ์ํ์ ๋ ๋ฑ๋ฑ... response ๊ฐ์ด ๋ณํํ์ฌ ๋ด๋ ค๊ฐ๋ ๊ฒฝ์ฐ์๋ ์ฌ๋งํ๋ฉด ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค. (๊ทธ๋ ๊ธฐ ๋๋ฌธ์ RestAssured๋ณด๋ค ๋ ๋ฌด๊ฑฐ์ด MockMvc๋ฅผ ํ์ฉํ๋ ๊ฒ ๋ ๋ซ๋ค๊ณ ํ๋จํ ์ ๋ ์๋ค.)
๋ํ, ์ค์จ๊ฑฐ์ ๋นํด ๊ตฌ์ถ์ด ์ด๋ ค์ด ํธ์ด๋ค. ๋ฌธ์ ๊ตฌ์ถ ์์ฒด๋ ๋น๋๋ adoc ํ์ผ์ ์ง์ ์กฐ๋ฆฝํด์ผ ํ๊ณ , ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ํ ์คํธํด๋ณผ ์๋ ์๋ค. ํ์ง๋ง, ์ง์ ๊ตฌ์ถํ๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๋ด๋ถ์ ์ด๋ค ๋ด์ฉ์ด ๋ค์ด๊ฐ์ง ์กฐ๊ธ ๋ ์ธ๋ถํํ์ฌ ๊ฐ๋ฐ์๊ฐ ์์ฑํ ์ ์๋ค.
๊ฐ์ธ์ ์ผ๋ก ๊ท๋ชจ๊ฐ ์๋ค๋ฉด swagger๋ฅผ ์ฌ์ฉํด๋ณผ๋ง ํ๋ค๊ณ ์๊ฐํ์ง๋ง, ํ๋ก๋์ ์ฝ๋ ์์ฒด๋ฅผ ์์ ํ๋ ๊ฒ ์ฐ์ฐํ๊ธฐ ๋๋ฌธ์ rest-docs๋ฅผ ์กฐ๊ธ ๋ ์ ํธํ๋ ํธ์ด๋ค. (๋ฌผ๋ก ๊ทธ๋๋ถํฐ ํ ์คํธ ์ง์ฅ์ด์ง๋ง... ใ _ใ ) ํ ๋ฒ ์ ์ฉํด๋๋ฉด ๊ทธ๋ค๋ถํฐ ์์ฉ์ ์ฝ๊ธฐ ๋๋ฌธ์ ์ฒ์ ๋ฌ๋์ปค๋ธ๋ง ์กฐ๊ธ ๊ฒฌ๋๋ฉด ๋๋ค!
โ๏ธ SetUp - build.gradle
๐ฑ ๊ทธ์ ์, Rest Docs์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก 'Asciidoctor'๋ผ๋ ์น๊ตฌ๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์ด ์น๊ตฌ๋ ์ผ๋ฐ ํ ์คํธ๋ฅผ ์ฒ๋ฆฌํ๋ฉด์ HTML ํํ๋ก ๋ง๋ค์ด์ฃผ๋ ์น๊ตฌ๋ค. ์ด ์น๊ตฌ์ ๊ฐ๋ ์์ฒด๊ฐ ์ค์ํ ๊ฑด ์๋์ง๋ง, HTML๋ก ๋ฌธ์๋ฅผ ์์ฑํ๊ธฐ ์ํ ์ผ์ข ์ ์ ์ฒ๋ฆฌ ๋๊ตฌ๋ผ๊ณ ์๊ฐํ์.
๐ฑ ๋ํ, ๊ฐ๊ฐ์ ํ ์คํธ ์ฝ๋๊ฐ ์ฑ๊ณตํ๋ฉด 'snippet'์ด๋ผ๋ ์น๊ตฌ๊ฐ ์์ฑ๋๋๋ฐ, ์ด๋ฌํ ์ค๋ํซ๋ค์ ์กฐ๋ฆฝํ์ฌ ๋ฌธ์๋ฅผ ์์ฑํ๋ค๊ณ ์๊ฐํ์.
๋จผ์ , build.gradle์ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ์ถ๊ฐํด์ผ ํ๋ค.
plugins {
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
configurations {
asciidoctorExt
}
ext {
snippetsDir = file('build/generated-snippets')
}
test {
outputs.dir snippetsDir
}
asciidoctor {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
dependsOn test
sources {
include("**/index.adoc")
}
baseDirFollowsSourceFile()
}
asciidoctor.doFirst {
delete file('src/main/resources/templates/docs')
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/templates/docs")
}
build {
dependsOn copyDocument
}
dependencies {
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
bootJar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
์๊ฐ๋ณด๋ค ์ค์ ํด์ค ๊ฒ์ด ๊ต์ฅํ ๋ง๊ธฐ ๋๋ฌธ์ ์กฐ๊ธ์ฉ ๋๋ ์ ๋ณด๋๋ก ํ์.
plugins {
id "org.asciidoctor.jvm.convert" version "3.3.2" (1)
}
configurations {
asciidoctorExt (2)
}
ext {
snippetsDir = file('build/generated-snippets') (3)
}
tasks.named('test') {
outputs.dir snippetsDir (4)
}
(1): Asciidoctor ํ๋ฌ๊ทธ์ธ์ ์ ์ฉํ๋ค.
(2): asciidoctorExt๋ฅผ ํ๋ก์ ํธ์ configuration์ ์ถ๊ฐํ๋ค.
asciidoctorExt๋ Asciidoctor๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์๋ฅผ ์์ฑํ๊ณ ํฌ๋งทํ๋ ๋ฐ ์ฌ์ฉํ๋ ํ๋ฌ๊ทธ์ธ์ธ๋ฐ, ์ด๋ฅผ ์ด์ฉํ์ฌ HTML ํ์์ผ๋ก ์ถ๋ ฅํ ์ ์๋๋ก ๋ง๋ ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค.
(3): ์์ฑ๋ ์ค๋ํซ๋ค์ ์ด๋ ์์น์ ์ ์ฅํ ๊ฒ์ธ์ง ์ง์ ํ๋ ๊ฒ์ด๋ค. (๋ํดํธ๋ก build ํด๋ ๋ฐ์ ์์ฑํ๋ ํธ)
(4): ์ ์ฅํ ์ค๋ํซ ๊ฒฝ๋ก๊ฐ ํ ์คํธ ๊ฒฐ๊ณผ๋ก ์์ฑ๋๋ ๋๋ ํฐ๋ฆฌ์ ์ถ๊ฐ๋๋๋ก ์ค์ ํ๋ ๊ฒ์ด๋ค.
ํ ์คํธ๊ฐ ์คํ๋ ๋ ์ค๋ํซ์ด ์์ฑ๋๋ฉด, ํด๋น ๋๋ ํฐ๋ฆฌ์ ์ ์ฅ๋๋ ๊ฒ์ด๋ค.
asciidoctor {
inputs.dir snippetsDir (5)
configurations 'asciidoctorExt' (6)
dependsOn test (7)
sources {
include("**/index.adoc") (8)
}
baseDirFollowsSourceFile() (9)
}
asciidoctor.doFirst {
delete file('src/main/resources/templates/docs') (10)
}
(5) asciidoctor๊ฐ ํ ์ผ์ ์ ์ํ๋ ๋ถ๋ถ์ด๋ค.
์ด์ ์ ์ค์ ํ ์ค๋ํซ ์ ์ฅ ๊ฒฝ๋ก๋ฅผ ์ ๋ ฅ ๋๋ ํฐ๋ฆฌ๋ก ๋ง๋ค์ด์, asciidoctor๊ฐ ๋ฌธ์๋ฅผ ์์ฑํ ๋ ์ด๋๋ฅผ ์ฐธ์กฐํ ์ง ์ง์ ํ๋ ๊ฒ์ด๋ค.
(6) ext์์ ๊ตฌ์ฑํ asciidoctorExt๋ฅผ ๊ตฌ์ฑ ํ์ผ๋ก ์ฌ์ฉํ๋๋ก ๋ง๋๋ ๊ฒ์ด๋ค.
(asciidoctor์ configuration์ ์ค์ ํ ๊ฑด๋ฐ, ์ด๋ค ์ค์ ์ ๋ณด๋ฅผ ์ฐธ์กฐํ ๊ฒ์ธ์ง ํ์ธํ๋ ๋๋?)
(7) ๋ฌธ์๊ฐ ์์ฑ๋๊ธฐ ์ ์ ํ ์คํธ๊ฐ ์คํ๋ ์ ์๋๋ก, test์ ์ข ์์ ์ด๋๋ก ๋ง๋ ๋ค. (test ์คํ -> ๋ฌธ์ ์์ฑ ๋๋!)
(8) rest-docs ๋ฌธ์์ ๊ฒฝ์ฐ, .adoc ํ์ผ์ ๋ฐํ์ผ๋ก ์์ฑํ๊ฒ ๋๋๋ฐ ์ด๋ source๋ฅผ ์ง์ ํ๊ฒ ๋๋ฉด ํน์ adoc๋ง HTML๋ก ๋ง๋ ๋ค.
๋ง์ฝ ๋ชจ๋ adoc์ ๋ํด ๊ฐ๊ฐ html์ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด ํด๋น ์ต์ ์ ์ ๊ฑฐํด๋ ๋๋ค.
(9) ํน์ .adoc์ ๋ค๋ฅธ adoc์ include ํ๊ณ ์ถ์ ๋ ๊ฒฝ๋ก๋ฅผ baseDir๋ก ๋ง์ถฐ์ฃผ๋ ์ญํ ์ด๋ค.
๋ ๊ฐ์ ๊ฒฝ์ฐ index.adoc์ผ๋ก ์ธ๋ถ adoc์ ๋ก๋ํ๋๋ก ๋ง๋ค์๊ธฐ ๋๋ฌธ์ ์ด ์ต์ ์ ํ์ฑํํด์ฃผ์๋ค.
(10) static.docs ํ์ผ์ ์ด๊ธฐํํ๊ณ ๋ค์ ๋ง๋ค๋๋ก ์ค์ ํ๋ ๊ฒ์ด๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก๋ static ํ์์ ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์๋ฐ, ๋ฏธ์ ์์ ํ์๋ฆฌํ๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ templates ํด๋ ํ์๋ก ์ค์ ํ์๋ค.
task copyDocument(type: Copy) { (11)
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/templates/docs")
}
build {
dependsOn copyDocument (12)
}
dependencies {
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' (13)
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' (14)
}
bootJar {
dependsOn asciidoctor (15)
from ("${asciidoctor.outputDir}/html5") {
into 'templates/docs'
}
}
(11) asciidoctor ์์ ํ ์ดํ (dependsOn), build/docs/asciidoc์ ๋ณด๋ฉด ์๋จ์์ ์ค์ ํด์ค source ์ต์ ์ ๋ฐ๋ผ html์ด ์๊ธฐ๊ฒ ๋๋ค. ๋ ๊ฐ์ ๊ฒฝ์ฐ ํ๋ก๋์ ์ฝ๋์์ html์ ์ ๊ทผํ ์ ์๋๋ก ํ๊ธฐ ์ํด์ resources/templates/docs์ ๋ณต์ฌ๋ฅผ ์งํํ์๋ค.
(12) ๋น๋ ์์ ์ด copyDocument ์ดํ์ ์งํ๋๋๋ก ์ค์ ํ๋ค.
(13) asciidoctorExt configuration์ ์ฌ์ฉํ์ฌ restdocs-asciidoctor ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํ ์์กด์ฑ์ ์ค์ ํ๋ค.
์ด๋ ๊ฒ ํ๋ฉด .adoc ํ์ผ์์ ๋น๋, ์์ฑ๋ ์ค๋ํซ์ ๊ฐ๋ฆฌํค๋๋ก ์ค๋ํซ ์์ฑ์ด ์๋์ผ๋ก ๊ตฌ์ฑ๋๋ค.
(14) MockMVC๋ฅผ ํ์ฉํ์ฌ rest-docs๋ฅผ ๊ตฌ์ฑํ ๋ ์ฌ์ฉํ๋ค. (Rest-Assured๋ฅผ ์ฌ์ฉํ๋ฉด spring-restdocs-restassured ์ฌ์ฉ)
(15) ์์ฑ๋ ๋ฌธ์๋ฅผ jar ํ์ผ์ ํจํค์งํด์ ์ ์ ์ปจํ ์ธ ๋ก ์ ๊ณตํ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
asciidoctor์ ์ข ์์ ์ด๋๋ก ๋ง๋ค์ด์, jar ํ์ผ์ ๋น๋ํ๊ธฐ ์ ์ ๋ฌธ์๊ฐ ์์ฑ๋์๋์ง ์ฒดํฌํ๋ค.
์ด๋ ๊ฒ ๋๋ฉด jar ํ์ผ์ด ๋น๋๋๊ธฐ ์ ์ ๋ฌธ์๊ฐ ์์ฑ๋๊ณ , ์์ฑ๋ ๋ฌธ์๋ jar ํ์ผ์ ํฌํจ๋๋ค.
asciidoctor ์งํ ์ดํ ๋ฌธ์๋ฅผ templates/docs๋ก ์ด๋์ํจ๋ค.
๐ฑ ๊ทธ๋์ ๊ฒฐ๊ณผ์ ์ผ๋ก ./gradlew clean build๋ฅผ ์งํํ๊ฒ ๋๋ฉด test -> asciidoctor -> copyDocument -> build ์์ผ๋ก ์ธํ ์ด ์งํ๋๋ค.
โ๏ธ ์ฝ๋์ ์ ์ฉํ๊ธฐ (Junit5 ๊ธฐ์ค)
์ฐ์ , ํ ์คํธํ ๋์ ํด๋์ค์ ๋ค์๊ณผ ๊ฐ์ ์ ์ ์์ ์ ์งํํด์ค๋ค.
controller๊ฐ ์ฌ๋ฌ ๊ฐ๋ผ๋ฉด ๊ณตํต์ ์ผ๋ก ์ฌ์ฉํ ๋ถ๋ถ์ ๋นผ๋ ๊ฒ์ด ์ข๊ธฐ ๋๋ฌธ์, ๋๋ ๋ณ๋์ helper ํด๋์ค๋ฅผ ์์ฑํด๋์๋ค.
@ExtendWith(RestDocumentationExtension.class) (1)
public class RestDocsHelper {
private MockMvc mockMvc;
private RestDocumentationResultHandler documentationResultHandler;
@BeforeEach
void setUp(final WebApplicationContext webApplicationContext,
final RestDocumentationContextProvider restDocumentationContextProvider) {
this.documentationResultHandler = MockMvcRestDocumentation.document(
"{class-name}/{method-name}", (2)
Preprocessors.preprocessRequest(Preprocessors.prettyPrint()), (3)
Preprocessors.preprocessResponse(Preprocessors.prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) (4)
.addFilter(new CharacterEncodingFilter("UTF-8", true)) (5)
.alwaysDo(MockMvcResultHandlers.print()) (6)
.alwaysDo(documentationResultHandler) (7)
.apply(documentationConfiguration(restDocumentationContextProvider)) (8)
.build();
}
}
(1): Junit5์์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, Spring Rest Docs์ RestDocumentationExtension ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ๋ฅผ ํ์ฅ์ํค๋ ๊ฒ์ด๋ค. ์ด๋ ต๊ฒ ์๊ฐํ ํ์์์ด, API ๋ฌธ์๋ฅผ ์์ฑํ๊ธฐ ์ํด ํ๊ฒฝ์ ์ค์ ํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
(2): ๋ฌธ์์ ์ธ๋ถ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ๋ ๋ถ๋ถ์ด๋ค. build/generated-snippets ํจํค์ง ๋ฐ์, class-name ํจํค์ง, ๊ทธ๋ฆฌ๊ณ method-name ํจํค์ง ํ๋จ์ ๊ฐ๊ฐ์ ์ค๋ํซ์ด ์์ฑ๋๋๋ก ๋ง๋๋ ์ค์ ์ด๋ค.
- ์ฌ์ค ์ด ๋ถ๋ถ์ ๋์ค์ ๋ฐฐํฌ๋ ๋๋ฅผ ๋๋นํ์ฌ ํ๋กํ ์ฝ ์ ๋ณด๋ host ์ ๋ณด๋ ์ง์ ํด์ค ์ ์๋ค. (Preprocessors.modifyUris ํ์ฉ)
(3) request, response์ ๋ํด์ ์ ์ฒ๋ฆฌ ์์ ์ ํด์ฃผ๋ ๊ฒ์ธ๋ฐ ์์ฒญ, ์๋ต์ ๋ํด ์ฝ๊ธฐ ์ฝ๋๋ก ๋ง๋ค์ด์ค๋ค.
(4): ๊ฐ๊ฐ์ ํ ์คํธ ๋ฉ์๋๊ฐ ์คํ๋๊ธฐ ์ ์ ๊ณตํต์ ์ผ๋ก ์คํํ ์์ ์ ์ ์ํ๋ค.
mockMvc์ ๋ํด์ ์ค์ ํ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด๋ฅผ ํตํด Spring REST docs์์ ์์ฑ๋ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ์ฌ API๋ฅผ ํ ์คํธํ ์ ์๋๋ก ๋ง๋ค์ด์ฃผ๋ ์ ์ด ์์ ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
(5) ๋ฌธ์ ์์ฑ ์ ํ๊ธ์ด ๊นจ์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ์ํด ์ธ์ฝ๋ฉ ํ์์ ์ง์ ํด์ฃผ๋ ๋ถ๋ถ์ด๋ค.
(6) ์ฒ๋ฆฌํ ๋ด์ฉ์ ๋ํด ํญ์ ๋ก๊น ์์ ์ ์ํํ๋๋ก print๋ฅผ ํด์ฃผ๋ ๋ถ๋ถ์ด๋ค.
(7) ์์์ ์ค์ ํ ์ ๋ณด๋ค์ ํญ์ ์ ์ฉํ๋๋ก ๋ง๋๋ ๊ฒ์ด๋ค.
์๊ฐ๋ณด๋ค ๊ธ์ด ๊ธธ์ด์ง ๊ฒ ๊ฐ์์, ์ค์ ๋ก ์ฝ๋์ ์ด๋ค ์์ผ๋ก ์ ์ฉ๋๋์ง๋ ๋ค์ ํฌ์คํ ์์ ์์ฑํ ์์ ์ด๋ค ๐ซ