๐ ์ค๋ ๋ฐฐ์ด ๋ด์ฉ!
- API ๋ฌธ์ํ
- Swagger vs Spring Rest Docs
- Spring Rest Docs
- Asciidoc
โ๏ธ API ๋ฌธ์ํ (Documentation)
- ํด๋ผ์ด์ธํธ๊ฐ 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
โ API ๋ฌธ์ ์์ฑ์ ์๋ํ๊ฐ ํ์ํ ์ด์
โ ์๊ธฐ๋ก ์์ฑํ์ ๋ ์๊ฐ๋ ๋ง์ด ๋ค๊ณ ์๋ฌ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด ๋์ ๋นํจ์จ์ ์ด๊ธฐ ๋๋ฌธ์ ์๋ํ ์ฌ์ฉ
โ๏ธ Swagger vs Spring Rest Docs
โ Swagger์ API ๋ฌธ์ํ ๋ฐฉ์
( Swagger๋ผ๋ API ๋ฌธ์ ์๋ํ ์คํ ์์ค ์ฌ์ฉํ ๋ฐฉ์ )
- ์ ํฐ๋ค์ด์
๊ธฐ๋ฐ์ API ๋ฌธ์ํ ๋ฐฉ์
( ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์ ๋ฌธ์ํ๋ฅผ ์ํ ๋ง์ ์ ๋ํ ์ด์ ๋ค์ด ํฌํจ๋จ ) - ๊ฐ๋
์ฑ ๋ฐ ์ ์ง ๋ณด์์ฑ์ด ๋จ์ด์ง
- API ๋ฌธ์์ API ์ฝ๋ ๊ฐ์ ์ ๋ณด ๋ถ์ผ์น ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
( ์ ๋ํ ์ด์ ๋ด์ API ์คํ ์ ๋ณด๋ฅผ ๋ฌธ์์ด๋ก ์ ๋ ฅํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ธฐ ๋๋ฌธ ) - API ํด๋ก์จ์ ๊ธฐ๋ฅ ํ์ฉ ๊ฐ๋ฅ
( API ๋ฌธ์ ๋ด์์ Execute ๋ฒํผ์ ๋๋ฌ Controller์ ์์ฒญ ๊ฐ๋ฅ )
โ Spring Rest Docs์ API ๋ฌธ์ํ ๋ฐฉ์
- ํ
์คํธ ์ฝ๋ ๊ธฐ๋ฐ์ API ๋ฌธ์ํ ๋ฐฉ์
- ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์ ๋ฌธ์ํ๋ฅผ ์ํ ์ ๋ณด๋ค์ด ํฌํจ๋์ง ์์
- ํ
์คํธ ์ผ์ด์ค์ ์คํ์ด ๊ผญ
passed
์ฌ์ผ API ๋ฌธ์๊ฐ ์์ฑ๋จ
โ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์๋์ด ์๋ API ์คํ ์ ๋ณด์ API ๋ฌธ์ ์ ๋ณด์ ๋ถ์ผ์น๋ก ์ธํด ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ๋ฐฉ์ง ๊ฐ๋ฅ - ํ
์คํธ ์ผ์ด์ค๋ฅผ ๋ฐ๋์ ์์ฑํด์ผํจ
- API ํด๋ก์จ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง ์์
โ๏ธ Spring Rest Docs
- REST API ๋ฌธ์๋ฅผ ์๋์ผ๋ก ์์ฑํด ์ฃผ๋ Spring ํ์ ํ๋ก์ ํธ
- Controller์ ์ฌ๋ผ์ด์ค ํ ์คํธ๋ฅผ ํตํด ํ ์คํธ๊ฐ ํต๊ณผ ๋์ด์ผ์ง๋ง API ๋ฌธ์๊ฐ ์ ์์ ์ผ๋ก ๋ง๋ค์ด ์ง
โ Spring Rest Docs์ API ๋ฌธ์ ์์ฑ ํ๋ฆ
1. ํ ์คํธ ์ฝ๋ ์์ฑ
- ์ฌ๋ผ์ด์ค ํ ์คํธ ์ฝ๋ ์์ฑ
- API ์คํ ์ ๋ณด ์ฝ๋ ์์ฑ
2. ํ ์คํธ ํ์คํฌ (test task) ์คํ
- ์์ฑ๋ ์ฌ๋ผ์ด์ค ํ
์คํธ ์ฝ๋๋ฅผ ์คํ
โ ์ผ๋ฐ์ ์ผ๋ก Gradle์ ๋น๋ ํ์คํฌ(task)์ค ํ๋์ธ test task๋ฅผ ์คํ ์์ผ์ API ๋ฌธ์ ์ค๋ํ(snippet)์ ์ผ๊ด ์์ฑ - ํ
์คํธ ๊ฒฐ๊ณผ๊ฐ
passed
๋ฉด ๋ค์ ์์ /failed
๋ฉด ํ ์คํธ ์ผ์ด์ค ์์ ํ ๋ค์ ์คํ
3. API ๋ฌธ์ ์ค๋ํ(.adoc ํ์ผ) ์์ฑ
- ํ
์คํธ ๊ฒฐ๊ณผ๊ฐ
passed
๋ฉด ํ ์คํธ ์ฝ๋์ API ์คํ ์ ๋ณด ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก API ๋ฌธ์ ์ค๋ํ์ด.adoc
ํ์ฅ์๋ฅผ ๊ฐ์ง ํ์ผ๋ก ์์ฑ๋จ
โ๏ธ ์ค๋ํ (snippet)
- ๋ฌธ์์ ์ผ๋ถ ์กฐ๊ฐ์ ์๋ฏธ
- ํ ์คํธ ์ผ์ด์ค ํ๋ ๋น ํ๋์ ์ค๋ํ์ด ์์ฑ
- ์ฌ๋ฌ๊ฐ์ ์ค๋ํ์ ๋ชจ์์ ํ๋์ API ๋ฌธ์ ์์ฑ ๊ฐ๋ฅ
4. API ๋ฌธ์ ์์ฑ
- ์์ฑ๋ API ๋ฌธ์ ์ค๋ํ์ ๋ชจ์ ํ๋์ API ๋ฌธ์๋ก ์์ฑ
5. API ๋ฌธ์๋ฅผ HTML๋ก ๋ณํ
- ์์ฑ๋ API ๋ฌธ์๋ฅผ HTML ํ์ผ๋ก ๋ณํ
- HTML๋ก ๋ณํ๋ ๋ฌธ์๋ HTML ํ์ผ ์์ฒด๋ฅผ ๊ณต์ ํ ์๋ ์๊ณ , URL์ ํตํด ํด๋น HTML์ ์ ์ํด์ ํ์ธํ ์ ์์
โ Spring Rest Docs ์ค์
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 ๋ฌธ์ ์ค๋ํ์ ์์ฑํ ์ ์๋ค!
โ 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 ํด๋์ค ์ง์
- Controller ํ
์คํธ๋ฅผ ์ํ ์ ์ฉ ์ ๋ํ
์ด์
- (2)
@MockBean(JpaMetamodelMappingContext.class)
- JPA์์ ์ฌ์ฉํ๋ Bean ๋ค์ Mock ๊ฐ์ฒด๋ก ์ฃผ์
ํด์ฃผ๋ ์ค์
- JPA์์ ์ฌ์ฉํ๋ Bean ๋ค์ Mock ๊ฐ์ฒด๋ก ์ฃผ์
ํด์ฃผ๋ ์ค์
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
- Spring Rest Docs์ ๋ํ ์๋ ๊ตฌ์ฑ์ ์ํ ์ ๋ํ
์ด์
- Spring Rest Docs์ ๋ํ ์๋ ๊ตฌ์ฑ์ ์ํ ์ ๋ํ
์ด์
- (10) API ๋ฌธ์๋ฅผ ์๋ ์์ฑํ๊ธฐ ์ํ ํด๋น Controller ํธ๋ค๋ฌ ๋ฉ์๋์ API ์คํ ์ ๋ณด๋ฅผ document(…)์ ์ถ๊ฐ
.andDo(…)
๋ฉ์๋
โ API ๋ฌธ์๋ฅผ ์์ฑ ํ๊ธฐ ์ํด Spring Rest Docs์์ ์ง์ํ๋ ๋ฉ์๋document(…)
๋ฉ์๋
โ ์ผ๋ฐ์ ์ธ ๋์์ ์ ์ํ๊ณ ์ ํ ๋ ์ฌ์ฉ
(andExpect()
์ฒ๋ผ ์ด๋ค ๊ฒ์ฆ ์์ ์ ํ๋ ๊ฒ X )
โ API ์คํ ์ ๋ณด๋ฅผ ์ ๋ฌ ๋ฐ์์ ์ค์ง์ ์ธ ๋ฌธ์ํ ์์ ์ ์ํํ๋ RestDocumentationResultHandler ํด๋์ค์์ ๊ฐ์ฅ ํต์ฌ ๊ธฐ๋ฅ์ ํ๋ ๋ฉ์๋
[document() ๋ฉ์๋ ์์ ์ธ ์ ์๋ ํ๋ผ๋ฏธํฐ๋ค ์ฐธ๊ณ ]
[jsonFieldType ์ฐธ๊ณ ]
โ ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ผ API ์คํ ์ ๋ณด๋ฅผ ์์ฑํ๋ฉด๋จ !
( ์์ธํ ๋ด์ฉ์ ์ค์ต ํ์ผ ์ฐธ๊ณ )
โ๏ธ @SpringBootTest
vs @WebMvcTest
@SpringBootTest
์ ๋ํ ์ด์ @AutoConfigureMockMvc
์ ํจ๊ป ์ฌ์ฉ- ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ ์ ์ฒด Bean์ ApplicationContext์ ๋ฑ๋กํ์ฌ ์ฌ์ฉ
โ ํ ์คํธ ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ๊ฒ์ ํธ๋ฆฌ
โ But, ์คํ ์๋๊ฐ ์๋์ ์ผ๋ก ๋๋ฆผ
โ
๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊น์ง ์์ฒญ ํ๋ก์ธ์ค๊ฐ ์ด์ด์ง๋ ํตํฉ ํ ์คํธ์ ์ฃผ๋ก ์ฌ์ฉ
โ
@WebMvcTest
์ ๋ํ ์ด์ - Controller ํ
์คํธ์ ํ์ํ Bean๋ง ApplicationContext์ ๋ฑ๋กํ์ฌ ์ฌ์ฉ
โ ์คํ ์๋ ์๋์ ์ผ๋ก ๋น ๋ฆ
But, Controller์์ ์์กดํ๊ณ ์๋ ๊ฐ์ฒด๊ฐ ์๋ค๋ฉด ํด๋น ๊ฐ์ฒด์ ๋ํด์ Mock ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์์กด์ฑ์ ์ผ์ผ์ด ์ ๊ฑฐํด ์ฃผ์ด์ผํจ
โ
๐ Controller๋ฅผ ์ํ ์ฌ๋ผ์ด์ค ํ ์คํธ์ ์ฃผ๋ก ์ฌ์ฉ
- Controller ํ
์คํธ์ ํ์ํ Bean๋ง ApplicationContext์ ๋ฑ๋กํ์ฌ ์ฌ์ฉ
โ๏ธ Asciidoc
- Spring Rest Docs๋ฅผ ํตํด ์์ฑ๋๋ ํ
์คํธ ๊ธฐ๋ฐ ๋ฌธ์ ํฌ๋งท
โ Spring Rest Docs๋ฅผ ํตํด ๋ง๋ค์ด์ง๋ ๋ฌธ์ ์ค๋ํ๊ณผ ์ด ๋ฌธ์ ์ค๋ํ์ ์ฌ์ฉํ๋ ํ ํ๋ฆฟ ๋ฌธ์๋ Asciidoc ํฌ๋งท์ ๋ฌธ์๋ก ์ด๋ฃจ์ด์ ธ ์์ - ๊ธฐ์ ๋ฌธ์ ์์ฑ์ ์ํด ์ค๊ณ๋ ๊ฐ๋ฒผ์ด ๋งํฌ์
์ธ์ด
- ์ด๋ฅผ ์ด์ฉํด ์ข ๋ ์ธ๋ จ๋๊ณ ๊ฐ๋ ์ฑ ์ข์ ๋ฌธ์ ๋ง๋ค๊ธฐ ๊ฐ๋ฅ !
โ 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:
- ๋ชฉ์ฐจ์ ํ์ํ ์ ๋ชฉ์ level ์ง์
( ์์ ์์์์๋ 4๋ก ์ง์ โ====
๊น์ง์ ์ ๋ชฉ๋ง ๋ชฉ์ฐจ์ ํ์๋จ )
- ๋ชฉ์ฐจ์ ํ์ํ ์ ๋ชฉ์ level ์ง์
- (5)
:toc-title:
- ๋ชฉ์ฐจ์ ์ ๋ชฉ ์ง์
- ๋ชฉ์ฐจ์ ์ ๋ชฉ ์ง์
- (6)
:source-highlighter:
- ๋ฌธ์์ ํ์๋๋ ์์ค ์ฝ๋ ํ์ผ๋ผ์ดํฐ๋ฅผ ์ง์
( ์์ ์์์์๋prettify
์ง์ )
- ๋ฌธ์์ ํ์๋๋ ์์ค ์ฝ๋ ํ์ผ๋ผ์ดํฐ๋ฅผ ์ง์
- (9) API ๋ฌธ์ ์ค๋ํ์ ์ด์ฉํ๋ ๋ถ๋ถ
- (9-1)
.
- ํ๋์ ์ค๋ํ ์น์
์ ๋ชฉ์ ํํํ๊ธฐ ์ํด ์ฌ์ฉ
( ์์ ์์์์๋curl-request
๋ฅผ ์น์ ์ ๋ชฉ์ผ๋ก ํจ )
- ํ๋์ ์ค๋ํ ์น์
์ ๋ชฉ์ ํํํ๊ธฐ ์ํด ์ฌ์ฉ
- (9-2) ํ
ํ๋ฆฟ ๋ฌธ์์์ ์ค๋ํ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
โinclude::{snippets}/์ค๋ํ ๋ฌธ์๊ฐ ์์นํ ๋๋ ํ ๋ฆฌ/์ค๋ํ ๋ฌธ์ํ์ผ๋ช .adoc[]
include
โ Asciidoctor์์ ์ฌ์ฉํ๋ ๋งคํฌ๋ก(macro) ์ค ํ๋
โ ์ค๋ํ์ ํ ํ๋ฆฟ ๋ฌธ์์ ํฌํจํ ๋ ์ฌ์ฉ::
โ ๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ํ๊ธฐ๋ฒ
- (9-1)
โ๏ธ ๋งคํฌ๋ก (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::
- ์ด๋ฏธ์ง ์ถ๊ฐ
Ex.image::https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg[spring]
- ์ด๋ฏธ์ง ์ถ๊ฐ
[Asciidoctor ์ฌ์ฉ๋ฒ ์ฐธ๊ณ ]
- https://docs.asciidoctor.org/asciidoc/latest/syntax-quick-reference/#ex-normal
- https://docs.asciidoctor.org/asciidoc/latest/toc/
- https://docs.spring.io/spring-restdocs/docs/current/reference/html5/#working-with-asciidoctor
โ Asciidoctor
- AsciiDoc ํฌ๋งท์ ๋ฌธ์๋ฅผ ํ์ฑํด์ HTML 5, ๋งค๋ด์ผ ํ์ด์ง, PDF ๋ฐ EPUB 3 ๋ฑ์ ๋ฌธ์๋ฅผ ์์ฑํ๋ ํด
- Spring Rest Docs์์๋ Asciidoc ํฌ๋งท์ ๋ฌธ์๋ฅผ HTML ํ์ผ๋ก ๋ณํํ๊ธฐ ์ํด ๋ด๋ถ์ ์ผ๋ก Asciidoctor๋ฅผ ์ฌ์ฉํ๊ณ ์์
๐ ์ค์ต
- projects - be-template-api-documentation
- git - be-homework-api-documentation
์์ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ ๋ฐ๋ผ 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 ๋ฌธ์๋ฅผ ์ง์ ๋ณผ ์ ์์ด์ ๋ ์ฌ๋ฏธ์๋ ์ฑํฐ์๋ค !
( ๋ณธ ๊ฒ์๋ฌผ์ 2022/11/15์ ์์ฑํ ๊ธ์ ์ฎ๊ธด ๊ธ์ ๋๋ค. ์๋ฌธ์ ์๊ธฐ์ ์์! )
'โข CodeStates BootCamp > Section 3' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๐ [Section3] ์ด์ ๊ฐ๋ ํ๊ณ ๐ฅ (0) | 2023.04.11 |
---|---|
๐ [Section3] 13. [ Spring MVC ] ์ ํ๋ฆฌ์ผ์ด์ ๋น๋/์คํ/๋ฐฐํฌ (0) | 2023.04.11 |
๐ [Section3] 11. [ Spring MVC ] ํ ์คํ 2 (1) | 2023.04.11 |
๐ [Section3] 10. [ Spring MVC ] ํ ์คํ 1 (0) | 2023.04.11 |
๐ [Section3] 9. [ Spring MVC ] ํธ๋์ญ์ (0) | 2023.04.11 |