ํด๋ผ์ด์ธํธ๊ฐ REST API ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ฒญ์ ์ ์กํ๊ธฐ ์ํด์ ์์์ผ ๋๋ ์์ฒญ ์ ๋ณด๋ฅผ ๋ฌธ์๋ก ์ ์ ๋ฆฌํ๋ ๊ฒ
( ํด๋ผ์ด์ธํธ ์ชฝ์์ ์ด ์ ํ๋ฆฌ์ผ์ด์
์ฌ์ฉ์ ์ํด ์์ฒญ URL / request body / query parameter ๋ฑ์ด ํ์ )
๊ฐ๋ฐ์๊ฐ ์ง์ ์๊ธฐ๋ก ์์ฑํ ์๋ ์๊ณ , ์ ํ๋ฆฌ์ผ์ด์
๋น๋๋ฅผ ํตํด API ๋ฌธ์๋ฅผ ์๋์ผ๋ก ์์ฑํ ์๋ ์์
( But, ์๊ธฐ๋ก ์์ฑํ๋ ๊ฒ์ ์์ฃผ ๋นํจ์จ์ โ API ๋ฌธ์ ์๋ํ ์ฌ์ฉ)
โ๏ธ REST API
- HTTP ํ๋กํ ์ฝ์ ํตํด API๋ฅผ ์ค๊ณํ๊ธฐ ์ํ ์ํคํ ์ฒ ์คํ์ผ
- CRUD ๊ธฐ๋ฅ ๊ฐ์ง
[์ฐธ๊ณ ] https://appmaster.io/ko/blog/rest-apiran-mueosimyeo-dareun-yuhyeonggwa-eoddeohge-dareungayo
โ ์๊ธฐ๋ก ์์ฑํ์ ๋ ์๊ฐ๋ ๋ง์ด ๋ค๊ณ ์๋ฌ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋์ ๋นํจ์จ์ ์ด๊ธฐ ๋๋ฌธ์ ์๋ํ ์ฌ์ฉ
( Swagger๋ผ๋ API ๋ฌธ์ ์๋ํ ์คํ ์์ค ์ฌ์ฉํ ๋ฐฉ์ )
์ ํฐ๋ค์ด์
๊ธฐ๋ฐ์ API ๋ฌธ์ํ ๋ฐฉ์
( ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์ ๋ฌธ์ํ๋ฅผ ์ํ ๋ง์ ์ ๋ํ
์ด์
๋ค์ด ํฌํจ๋จ )
๊ฐ๋ ์ฑ ๋ฐ ์ ์ง ๋ณด์์ฑ์ด ๋จ์ด์ง
API ๋ฌธ์์ API ์ฝ๋ ๊ฐ์ ์ ๋ณด ๋ถ์ผ์น ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
( ์ ๋ํ
์ด์
๋ด์ API ์คํ ์ ๋ณด๋ฅผ ๋ฌธ์์ด๋ก ์
๋ ฅํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ธฐ ๋๋ฌธ )
API ํด๋ก์จ์ ๊ธฐ๋ฅ ํ์ฉ ๊ฐ๋ฅ
( API ๋ฌธ์ ๋ด์์ Execute ๋ฒํผ์ ๋๋ฌ Controller์ ์์ฒญ ๊ฐ๋ฅ )
[์ฐธ๊ณ ] https://swagger.io/docs/specification/about/
ํ ์คํธ ์ฝ๋ ๊ธฐ๋ฐ์ API ๋ฌธ์ํ ๋ฐฉ์
์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์ ๋ฌธ์ํ๋ฅผ ์ํ ์ ๋ณด๋ค์ด ํฌํจ๋์ง ์์
ํ
์คํธ ์ผ์ด์ค์ ์คํ์ด ๊ผญ passed
์ฌ์ผ API ๋ฌธ์๊ฐ ์์ฑ๋จ
โ ์ ํ๋ฆฌ์ผ์ด์
์ ์ ์๋์ด ์๋ API ์คํ ์ ๋ณด์ API ๋ฌธ์ ์ ๋ณด์ ๋ถ์ผ์น๋ก ์ธํด ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์ง ๊ฐ๋ฅ
ํ ์คํธ ์ผ์ด์ค๋ฅผ ๋ฐ๋์ ์์ฑํด์ผํจ
API ํด๋ก์จ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์์
REST API ๋ฌธ์๋ฅผ ์๋์ผ๋ก ์์ฑํด ์ฃผ๋ Spring ํ์ ํ๋ก์ ํธ
Controller์ ์ฌ๋ผ์ด์ค ํ ์คํธ๋ฅผ ํตํด ํ ์คํธ๊ฐ ํต๊ณผ ๋์ด์ผ์ง๋ง API ๋ฌธ์๊ฐ ์ ์์ ์ผ๋ก ๋ง๋ค์ด ์ง
[์ฐธ๊ณ ]
https://intellij-asciidoc-plugin.ahus1.de/docs/users-guide/features/advanced/spring-rest-docs.html
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#introduction
ํ ์คํธ ์ฝ๋ ์์ฑ
ํ ์คํธ ํ์คํฌ (test task) ์คํ
์์ฑ๋ ์ฌ๋ผ์ด์ค ํ
์คํธ ์ฝ๋๋ฅผ ์คํ
โ ์ผ๋ฐ์ ์ผ๋ก Gradle์ ๋น๋ ํ์คํฌ(task)์ค ํ๋์ธ test task๋ฅผ ์คํ ์์ผ์ API ๋ฌธ์ ์ค๋ํ(snippet)์ ์ผ๊ด ์์ฑ
ํ
์คํธ ๊ฒฐ๊ณผ๊ฐ passed
๋ฉด ๋ค์ ์์
/ failed
๋ฉด ํ
์คํธ ์ผ์ด์ค ์์ ํ ๋ค์ ์คํ
API ๋ฌธ์ ์ค๋ํ(.adoc ํ์ผ) ์์ฑ
passed
๋ฉด ํ
์คํธ ์ฝ๋์ API ์คํ ์ ๋ณด ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก API ๋ฌธ์ ์ค๋ํ์ด .adoc
ํ์ฅ์๋ฅผ ๊ฐ์ง ํ์ผ๋ก ์์ฑ๋จโ๏ธ ์ค๋ํ (snippet)
- ๋ฌธ์์ ์ผ๋ถ ์กฐ๊ฐ์ ์๋ฏธ
- ํ ์คํธ ์ผ์ด์ค ํ๋ ๋น ํ๋์ ์ค๋ํ์ด ์์ฑ
- ์ฌ๋ฌ๊ฐ์ ์ค๋ํ์ ๋ชจ์์ ํ๋์ API ๋ฌธ์ ์์ฑ ๊ฐ๋ฅ
API ๋ฌธ์ ์์ฑ
API ๋ฌธ์๋ฅผ HTML๋ก ๋ณํ
์์ฑ๋ API ๋ฌธ์๋ฅผ HTML ํ์ผ๋ก ๋ณํ
HTML๋ก ๋ณํ๋ ๋ฌธ์๋ HTML ํ์ผ ์์ฒด๋ฅผ ๊ณต์ ํ ์๋ ์๊ณ , URL์ ํตํด ํด๋น HTML์ ์ ์ํด์ ํ์ธํ ์ ์์
1. build.gradle
์ ์๋์๊ฐ์ด ์ค์ ํด์ฃผ์ด์ผ Spring Rest Docs๊ฐ API ๋ฌธ์ ์์ฑ ์์
์ ์ ์์ ์ผ๋ก ์ํ ๊ฐ๋ฅ
plugins {
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id "org.asciidoctor.jvm.convert" version "3.3.2"
// (1) .adoc ํ์ผ ํ์ฅ์๋ฅผ ๊ฐ์ง๋ AsciiDoc ๋ฌธ์๋ฅผ ์์ฑํด์ฃผ๋ Asciidoctor ์ฌ์ฉํ๊ธฐ ์ํ ํ๋ฌ๊ทธ์ธ ์ถ๊ฐ
id 'java'
}
group = 'com.codestates'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
// (2) ext ๋ณ์์ set() ๋ฉ์๋๋ฅผ ์ด์ฉํด์, API ๋ฌธ์ ์ค๋ํ์ด ์์ฑ๋ ๊ฒฝ๋ก ์ง์
ext {
set('snippetsDir', file("build/generated-snippets"))
}
// (3) AsciiDoctor์์ ์ฌ์ฉ๋๋ ์์กด ๊ทธ๋ฃน ์ง์
// (:asciidoctor task๊ฐ ์คํ๋๋ฉด ๋ด๋ถ์ ์ผ๋ก ์๋์์ ์ง์ ํ โasciidoctorExtensionsโ๋ผ๋ ๊ทธ๋ฃน์ ์ง์ )
configurations {
asciidoctorExtensions
}
dependencies {
// (4) spring-restdocs-core / spring-restdocs-mockmvc ์์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ถ๊ฐ
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
// (5) spring-restdocs-asciidoctor ์์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ
// ((3)์์ ์ง์ ํ asciidoctorExtensions ๊ทธ๋ฃน์ ์์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํฌํจ๋ฉ)
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.mapstruct:mapstruct:1.5.1.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final'
implementation 'org.springframework.boot:spring-boot-starter-mail'
implementation 'com.google.code.gson:gson'
}
// (6) test task ์คํ ์, API ๋ฌธ์ ์์ฑ ์ค๋ํ ๋๋ ํ ๋ฆฌ ๊ฒฝ๋ก ์ค์
tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}
// (7) :asciidoctor task ์คํ ์, Asciidoctor ๊ธฐ๋ฅ ์ฌ์ฉ์ ์ํด :asciidoctor task์ asciidoctorExtensions ์ค์
tasks.named('asciidoctor') {
configurations "asciidoctorExtensions"
inputs.dir snippetsDir
dependsOn test
}
// (8) :build task ์คํ ์ ์ ์คํ๋๋ task
// :copyDocument task๊ฐ ์ํ๋๋ฉด index.html ํ์ผ์ด "src/main/resources/static/docs" ์ copy๋จ
// copy๋ index.html ํ์ผ์ API ๋ฌธ์๋ฅผ ํ์ผ ํํ๋ก **์ธ๋ถ์ ์ ๊ณตํ๊ธฐ ์ํ ์ฉ๋**๋ก ์ฌ์ฉ
task copyDocument(type: Copy) {
dependsOn asciidoctor
// (8-1) :asciidoctor task๊ฐ ์คํ๋ ํ์ task๊ฐ ์คํ ๋๋๋ก ์์กด์ฑ ์ค์
from file("${asciidoctor.outputDir}")
// (8-2) "build/docs/asciidoc/" ๊ฒฝ๋ก์ ์์ฑ๋๋ index.html์ copy
into file("src/main/resources/static/docs")
// (8-3) "src/main/resources/static/docs" ๊ฒฝ๋ก๋ก index.html์ ์ถ๊ฐ
}
build {
dependsOn copyDocument
// (9) :build task๊ฐ ์คํ๋๊ธฐ ์ ์ :copyDocument task๊ฐ ๋จผ์ ์ํ ๋๋๋ก ํจ
}
// (10) ์ ํ๋ฆฌ์ผ์ด์
์คํ ํ์ผ์ด ์์ฑํ๋ :bootJar task ์ค์
// ( jar ํ์ผ์ ํฌํจํด์ ์น ๋ธ๋ผ์ฐ์ ์์ API ๋ฌธ์๋ฅผ ํ์ธํ๊ธฐ ์ํ ์ฉ๋ )
bootJar {
dependsOn copyDocument
// (10-1) :bootJar task ์คํ ์ ์ :copyDocument task๊ฐ ์คํ ๋๋๋ก ์์กด์ฑ ์ค์
from ("${asciidoctor.outputDir}") {
// (10-2) Asciidoctor ์คํ์ผ๋ก ์์ฑ๋๋ index.html ํ์ผ์ jar ํ์ผ ์์ ์ถ๊ฐ
into 'static/docs'
// (10-3) jar ํ์ผ์ index.html์ ์ถ๊ฐํด ์ค์ผ๋ก์จ ์น ๋ธ๋ผ์ฐ์ ์์ ์ ์(http://localhost:8080/docs/index.html) ํ, API ๋ฌธ์๋ฅผ ํ์ธ ๊ฐ๋ฅ
}
}
[์ฐธ๊ณ ] https://docs.gradle.org/current/dsl/org.gradle.api.plugins.ExtraPropertiesExtension.html
2. API ๋ฌธ์ ์ค๋ํ์ ์ฌ์ฉํ๊ธฐ ์ํ ํ
ํ๋ฆฟ(๋๋ source ํ์ผ) ์์ฑ
โ API ๋ฌธ์ ์ค๋ํ์ด ์์ฑ ๋์์ ๋ ์ด ์ค๋ํ์ ์ฌ์ฉํด์ ์ต์ข
API ๋ฌธ์๋ก ๋ง๋ค์ด ์ฃผ๋ ํ
ํ๋ฆฟ ๋ฌธ์(index.adoc)๋ฅผ ์์ฑํ๋ ๊ฒ
โ๏ธ Gradle ๊ธฐ๋ฐ ํ๋ก์ ํธ์์๋ src/docs/asciidoc/
๊ฒฝ๋ก์ ํด๋นํ๋ ๋๋ ํ ๋ฆฌ ์์ฑํ๊ธฐ
โ๏ธ src/docs/asciidoc/
๋๋ ํ ๋ฆฌ ๋ด์ ๋น์ด์๋ ํ
ํ๋ฆฟ ๋ฌธ์(index.adoc) ์์ฑํ๊ธฐ
( ๋์ค์ ๋ง๋ค์ด์ง ์ค๋ํ๋ค์ ํ๋ฒ์ ๋ชจ์ html๋ก ๋ณํํ ์ต์ข
API ๋ฌธ์๋ฅผ ์์ฑํ ์ฉ๋ !)
โ ์์ ๊ฐ์ด ๋๊ฐ์ง ์ค์ ์ ๋ง์น๋ฉด Controller๋ฅผ ํ ์คํธ ํ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์์ฑํ๊ณ , ํด๋น Controller์ ๋ํ API ์คํ ์ ๋ณด๋ฅผ ํ ์คํธ ์ผ์ด์ค์ ์ถ๊ฐํด ์ฃผ๋ฉด API ๋ฌธ์ ์ค๋ํ์ ์์ฑํ ์ ์๋ค!
@WebMvcTest(MemberController.class) // (1)
@MockBean(JpaMetamodelMappingContext.class) // (2)
@AutoConfigureRestDocs // (3)
public class MemberControllerRestDocsTest {
@Autowired
private MockMvc mockMvc; // (4)
@MockBean
// (5) ํ
์คํธ ๋์ Controller ํด๋์ค๊ฐ ์์กดํ๋ ๊ฐ์ฒด๋ฅผ Mock Bean ๊ฐ์ฒด๋ก ์ฃผ์
๋ฐ๊ธฐ
@Test
public void postMemberTest() throws Exception {
// given
// (6) ํ
์คํธ ๋ฐ์ดํฐ
// (7) Mock ๊ฐ์ฒด๋ฅผ ์ด์ฉํ Stubbing
// when
ResultActions actions =
mockMvc.perform(
// (8) request ์ ์ก
);
// then
actions
.andExpect( // (9) response์ ๋ํ ๊ธฐ๋ ๊ฐ ๊ฒ์ฆ
.andDo(document(
// (10) API ๋ฌธ์ ์คํ ์ ๋ณด ์ถ๊ฐ
));
}
}
( ์์์ ๋๋จธ์ง๋ Mockito๋ฅผ ์ฌ์ฉํ์ฌ Cotroller ์ฌ๋ผ์ด์ค ํ ์คํธ๋ฅผ ํ ์ฝ๋์ ๊ฐ์ )
( ํด๋์ค ๋ ๋ฒจ์ ์ ๋ํ
์ด์
๊ณผ then
๋ถ๋ถ์์ .andDo(document(...));
์ดํ๊ฐ API ๋ฌธ์์ ๋ํ ์ ๋ณด์ ! )
(1) @WebMvcTest(MemberController.class)
Controller ํ
์คํธ๋ฅผ ์ํ ์ ์ฉ ์ ๋ํ
์ด์
( @SpringBootTest
์ ๋ํ
์ด์
์ฌ์ฉ X )
๊ดํธ ์์๋ ํ ์คํธ ๋์ Controller ํด๋์ค ์ง์
(2) @MockBean(JpaMetamodelMappingContext.class)
Spring Boot ๊ธฐ๋ฐ ํ ์คํธ๋ ํญ์ ์ต์์ ํจํค์ง ๊ฒฝ๋ก์
~~~Application
ํด๋์ค๋ฅผ ์ฐพ์์ ์คํํ๋๋ฐ, ์ด ํด๋์ค์๋@EnableJpaAuditing
์ ๋ํ ์ด์ ์ด ์ถ๊ฐ๋์ด์์@EnableJpaAuditing // ์ฌ๊ธฐ @SpringBootApplication public class Section3Week3RestDocsApplication { public static void main(String[] args) { SpringApplication.run(Section3Week3RestDocsApplication.class, args); } }
โ ๊ทธ๋ฌ๋ฉด JPA์ ๊ด๋ จ๋ Bean๋ค์ ํ์๋ก ํ๊ธฐ ๋๋ฌธ์
@WebMvcTest
์ ๋ํ ์ด์ ์ ์ฌ์ฉํ์ฌ ํ ์คํธ๋ฅผ ์งํํ ๊ฒฝ์ฐ,
๊ดํธ์ JpaMetamodelMappingContext๋ฅผ Mock ๊ฐ์ฒด๋ก ์ฃผ์ ํด ์ฃผ์ด์ผ ํจ
(3) @AutoConfigureRestDocs
(10) API ๋ฌธ์๋ฅผ ์๋ ์์ฑํ๊ธฐ ์ํ ํด๋น Controller ํธ๋ค๋ฌ ๋ฉ์๋์ API ์คํ ์ ๋ณด๋ฅผ document(โฆ)์ ์ถ๊ฐ
.andDo(โฆ)
๋ฉ์๋
โ API ๋ฌธ์๋ฅผ ์์ฑ ํ๊ธฐ ์ํด Spring Rest Docs์์ ์ง์ํ๋ ๋ฉ์๋
document(โฆ)
๋ฉ์๋
โ ์ผ๋ฐ์ ์ธ ๋์์ ์ ์ํ๊ณ ์ ํ ๋ ์ฌ์ฉ
( andExpect()
์ฒ๋ผ ์ด๋ค ๊ฒ์ฆ ์์
์ ํ๋ ๊ฒ X )
โ API ์คํ ์ ๋ณด๋ฅผ ์ ๋ฌ ๋ฐ์์ ์ค์ง์ ์ธ ๋ฌธ์ํ ์์
์ ์ํํ๋ RestDocumentationResultHandler
ํด๋์ค์์ ๊ฐ์ฅ ํต์ฌ ๊ธฐ๋ฅ์ ํ๋ ๋ฉ์๋
[document() ๋ฉ์๋ ์์ ์ธ ์ ์๋ ํ๋ผ๋ฏธํฐ๋ค ์ฐธ๊ณ ]
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api
โ
[jsonFieldType ์ฐธ๊ณ ]
https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api-request-response-payloads-fields-json-field-types
โ ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ผ API ์คํ ์ ๋ณด๋ฅผ ์์ฑํ๋ฉด๋จ !
( ์์ธํ ๋ด์ฉ์ ์ค์ต ํ์ผ ์ฐธ๊ณ )
โ๏ธ
@SpringBootTest
vs@WebMvcTest
โ
@SpringBootTest
์ ๋ํ ์ด์
@AutoConfigureMockMvc
์ ํจ๊ป ์ฌ์ฉ- ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ ์ ์ฒด Bean์ ApplicationContext์ ๋ฑ๋กํ์ฌ ์ฌ์ฉ
โ ํ ์คํธ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ๊ฒ์ ํธ๋ฆฌ
โ But, ์คํ ์๋๊ฐ ์๋์ ์ผ๋ก ๋๋ฆผ
โ
๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊น์ง ์์ฒญ ํ๋ก์ธ์ค๊ฐ ์ด์ด์ง๋ ํตํฉ ํ ์คํธ์ ์ฃผ๋ก ์ฌ์ฉ
โ @WebMvcTest
์ ๋ํ ์ด์
- Controller ํ ์คํธ์ ํ์ํ Bean๋ง ApplicationContext์ ๋ฑ๋กํ์ฌ ์ฌ์ฉ
โ ์คํ ์๋ ์๋์ ์ผ๋ก ๋น ๋ฆ
But, Controller์์ ์์กดํ๊ณ ์๋ ๊ฐ์ฒด๊ฐ ์๋ค๋ฉด ํด๋น ๊ฐ์ฒด์ ๋ํด์ Mock ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์์กด์ฑ์ ์ผ์ผ์ด ์ ๊ฑฐํด ์ฃผ์ด์ผํจ
โ
๐ Controller๋ฅผ ์ํ ์ฌ๋ผ์ด์ค ํ ์คํธ์ ์ฃผ๋ก ์ฌ์ฉ
Spring Rest Docs๋ฅผ ํตํด ์์ฑ๋๋ ํ
์คํธ ๊ธฐ๋ฐ ๋ฌธ์ ํฌ๋งท
โ Spring Rest Docs๋ฅผ ํตํด ๋ง๋ค์ด์ง๋ ๋ฌธ์ ์ค๋ํ๊ณผ ์ด ๋ฌธ์ ์ค๋ํ์ ์ฌ์ฉํ๋ ํ
ํ๋ฆฟ ๋ฌธ์๋ Asciidoc ํฌ๋งท์ ๋ฌธ์๋ก ์ด๋ฃจ์ด์ ธ ์์
๊ธฐ์ ๋ฌธ์ ์์ฑ์ ์ํด ์ค๊ณ๋ ๊ฐ๋ฒผ์ด ๋งํฌ์ ์ธ์ด
์ด๋ฅผ ์ด์ฉํด ์ข ๋ ์ธ๋ จ๋๊ณ ๊ฐ๋ ์ฑ ์ข์ ๋ฌธ์ ๋ง๋ค๊ธฐ ๊ฐ๋ฅ !
Ex.
= ์ปคํผ ์ฃผ๋ฌธ ์ ํ๋ฆฌ์ผ์ด์ // (1) ๋ฌธ์์ ์ ๋ชฉ :sectnums: // (2) :toc: left // (3) :toclevels: 4 // (4) :toc-title: Table of Contents // (5) :source-highlighter: prettify // (6) โ Joo Hyun Ju <57wnguswn57@gmail.com> // (7) ๋ฌธ์๋ฅผ ์์ฑํ ์ด์ ์ ๋ณด โ v1.0.0, 2022.11.15 // (8) ์์ฑ ๋ ์ง โ // (9) API ๋ฌธ์ ์ค๋ํ์ ์ด์ฉํ๋ ๋ถ๋ถ *** // (10) == MemberController === ํ์ ๋ฑ๋ก .curl-request // (9-1) include::{snippets}/post-member/curl-request.adoc[] // (9-2) โ .http-request include::{snippets}/post-member/http-request.adoc[] โ .request-fields include::{snippets}/post-member/request-fields.adoc[] โ .http-response include::{snippets}/post-member/http-response.adoc[] โ .response-fields include::{snippets}/post-member/response-fields.adoc[]
(1) ๋ฌธ์์ ์ ๋ชฉ
=
๋ฅผ ์ถ๊ฐํ์ฌ ์ ๋ชฉ ์์ฑ=
์ ๊ฐ์๊ฐ ๋์ด๋ ์๋ก ๊ธ์๋ ์์์ง )(2) :sectnums:
(3) :toc:
left
๋ก ์ค์ )(4) :toclevels:
====
๊น์ง์ ์ ๋ชฉ๋ง ๋ชฉ์ฐจ์ ํ์๋จ )(5) :toc-title:
(6) :source-highlighter:
prettify
์ง์ )(9) API ๋ฌธ์ ์ค๋ํ์ ์ด์ฉํ๋ ๋ถ๋ถ
(9-1) .
curl-request
๋ฅผ ์น์
์ ๋ชฉ์ผ๋ก ํจ )(9-2) ํ
ํ๋ฆฟ ๋ฌธ์์์ ์ค๋ํ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
โ include::{snippets}/์ค๋ํ ๋ฌธ์๊ฐ ์์นํ ๋๋ ํ ๋ฆฌ/์ค๋ํ ๋ฌธ์ํ์ผ๋ช
.adoc[]
include
โ Asciidoctor์์ ์ฌ์ฉํ๋ ๋งคํฌ๋ก(macro) ์ค ํ๋
โ ์ค๋ํ์ ํ
ํ๋ฆฟ ๋ฌธ์์ ํฌํจํ ๋ ์ฌ์ฉ
::
โ ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ํ๊ธฐ๋ฒ
โ๏ธ ๋งคํฌ๋ก (macro)
โ ์ด๋ค ๋ฐ๋ณต๋๋ ์์ ์ ์๋ํํ๋ค๋ ์๋ฏธ
{snippets}
โ ํด๋น ์ค๋ํ์ด ์์ฑ๋๋ ๋ํดํธ ๊ฒฝ๋ก
โ build.gradle
ํ์ผ์ ์ค์ ํ snippetsDir
๋ณ์๋ฅผ ์ฐธ์กฐํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์
(10) ***
๋ฐ์ค ๋ฌธ๋จ
CAUTION:
/ NOTE:
/ TIP:
/ IMPORTANT:
/ WARNING:
๋ฑ
URL Scheme ์๋ ์ธ์
http
/ https
/ ftp
/ irc
/ mailto
/ hgd@gmail.com
๊ณผ ๊ฐ์ URL Scheme๋ Asciidoc ์์ ์๋์ผ๋ก ์ธ์ํ์ฌ ๋งํฌ ์ค์ ๋จimage::
image::https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg[spring]
[Asciidoctor ์ฌ์ฉ๋ฒ ์ฐธ๊ณ ]
AsciiDoc ํฌ๋งท์ ๋ฌธ์๋ฅผ ํ์ฑํด์ HTML 5, ๋งค๋ด์ผ ํ์ด์ง, PDF ๋ฐ EPUB 3 ๋ฑ์ ๋ฌธ์๋ฅผ ์์ฑํ๋ ํด
Spring Rest Docs์์๋ Asciidoc ํฌ๋งท์ ๋ฌธ์๋ฅผ HTML ํ์ผ๋ก ๋ณํํ๊ธฐ ์ํด ๋ด๋ถ์ ์ผ๋ก Asciidoctor๋ฅผ ์ฌ์ฉํ๊ณ ์์
์์ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ ๋ฐ๋ผ API ์คํ ์ ๋ณด๋ฅผ ์์ฑํ ํ ํ
์คํธ๊ฐ passed
๊ฐ ๋๋ฉด,
์๋์ ๊ฐ์ด build > generated-snippets > ์ค๋ํ๋ช
ํด๋์ ํด๋น ์ค๋ํ๋ค์ด ์์ฑ๋จ !
โ๏ธ snippet ์ข ๋ฅ
curl-request.adoc
โ ํธ์ถ์ ๋ํ curl ๋ช ๋ น์ ํฌํจ ํ๋ ๋ฌธ์httpie-request.adoc
โ ํธ์ถ์ ๋ํ http ๋ช ๋ น์ ํฌํจ ํ๋ ๋ฌธ์http-request.adoc
โ http ์์ฒญ ์ ๋ณด ๋ฌธ์http-response.adoc
โ http ์๋ต ์ ๋ณด ๋ฌธ์request-body.adoc
โ ์ ์ก๋ http ์์ฒญ ๋ณธ๋ฌธ ๋ฌธ์response-body.adoc
โ ๋ฐํ๋ http ์๋ต ๋ณธ๋ฌธ ๋ฌธ์request-parameters.adoc
โ ํธ์ถ์ parameter ์ ๋ํ ๋ฌธ์path-parameters.adoc
โ http ์์ฒญ์ url ์ ํฌํจ๋๋ path parameter ์ ๋ํ ๋ฌธ์request-fields.adoc
โ http ์์ฒญ object ์ ๋ํ ๋ฌธ์response-fields.adoc
โ http ์๋ต object ์ ๋ํ ๋ฌธ์
๋ชจ๋ ํ
์คํธ๋ฅผ ๋ง์น๊ณ ์ค๋ํ๋ค์ด ๋ชจ๋ ์์ฑ๋๋ฉด,
Spring Rest Docs ์ค์ ์์ ๋ง๋ค์ด ๋์๋ ํ
ํ๋ฆฟ ๋ฌธ์๋ฅผ ์์ฑํ src/docs/asciidoc/index.adoc
ํ์ผ์
์๋์ ๊ฐ์ด ์ค๋ํ๋ค์ ๋ชจ๋ ํฉ์ณ HTML๋ก ๋ณํ์ ์ํ ์ต์ข
API ๋ฌธ์๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ๋จ!
์ด์ ๋ค ์์ฑ์ ํ๋ค๋ฉด,
์ ์ฌ์ง์ฒ๋ผ Gradle์ :build
or :bootJar
task ๋ช
๋ น์ ์คํํ์ฌ index.adoc
ํ์ผ์ index.html
ํ์ผ๋ก ๋ณํํ๊ธฐ !
๊ทธ๋ฌ๋ฉด ์์ ๊ฐ์ด src/main/resources/static.docs/
๊ฒฝ๋ก์ index.html
์ด ์๊น !!
์ด๋ฅผ ์ธํฐ๋ท์์ ํ์ธํ๊ณ ์ถ๋ค๋ฉด,
intellij ์์ ์ ํ๋ฆฌ์ผ์ด์
์คํ ํ, http://localhost:8080/docs/index.html
์ด URL์ ์น ๋ธ๋ผ์ฐ์ ์ ์
๋ ฅํ๋ค๋ฉด
์๋์ ๊ฐ์ด API ๋ฌธ์๊ฐ htmlํ ๋ ๊ฒ์ ๋ณผ ์ ์์ ~
[์ฐธ๊ณ ] https://jogeum.net/16
์ค๋ ํ์ต์ ์ ์ ํ์ตํ๋ Controller ์ฌ๋ผ์ด์ค ํ ์คํธ์ ์ข ๋ ์ถ๊ฐ๋ง ํ๋ฉด ๋๋ ๋ถ๋ถ์ด์ด์ ์ฌ์ ๊ณ ์๋์ผ๋ก ๋ง๋ค์ด์ง API ๋ฌธ์๋ฅผ ์ง์ ๋ณผ ์ ์์ด์ ๋ ์ฌ๋ฏธ์๋ ์ฑํฐ์๋ค !