Spring REST Docs ์ ์šฉ (Gradle 7)

Spring REST Docs ์ ์šฉ (Gradle 7)

2021, Sep 28    

1. Spring REST Docs

์•ˆ๋…•ํ•˜์„ธ์š”, ์ผ€๋นˆ์ž…๋‹ˆ๋‹ค. Pick-Git ์„œ๋น„์Šค ๊ฐœ๋ฐœ ์ดˆ๊ธฐ์—๋Š” ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์›๋“ค๊ณผ API ์ŠคํŽ™ ํ˜‘์˜ ํ›„ Notion์— ์ž‘์„ฑํ•ด์„œ ๊ณต์œ ํ–ˆ์—ˆ๋Š”๋ฐ์š”. ํ•ด๋‹น ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๋ฌธ์ œ์ ์ด ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜๊ธฐ๋กœ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ ์ธ์  ์‹ค์ˆ˜๋กœ ์ธํ•ด ์˜ค๊ธฐ์žฌ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ API ์ŠคํŽ™ ๋ณ€๊ฒฝ์ด ๋งค์šฐ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜๋Š”๋ฐ, ์ด๋ฅผ ๋ฏธ๊ธฐ์žฌํ•˜๋Š” ์‹ค์ˆ˜๋กœ ์ธํ•ด ์ผ์ •์— ์ฐจ์งˆ์ด ์ข…์ข… ์ƒ๊ธฐ๊ณค ํ–ˆ์Šต๋‹ˆ๋‹ค.

ํŒ€์› ๋ชจ๋‘ ์กฐ๊ธˆ ๋” ๊ทœ๊ฒฉํ™”๋œ API ๋ฌธ์„œํ™”์˜ ๋„์ž…์˜ ํ•„์š”์„ฑ์„ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ํŒ€์ด ์„ ํƒํ•œ ๋„๊ตฌ๋Š” Spring Rest Docs์ž…๋‹ˆ๋‹ค. Spring REST Docs๋Š” RESTful ์„œ๋น„์Šค์˜ ๋ฌธ์„œํ™”๋ฅผ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ์ธ๋ฐ์š”. ๋ฌธ์„œ ์ž‘์„ฑ ๋„๊ตฌ๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ Asciidoctor๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์ตœ์ข…์ ์œผ๋กœ HTML์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด Markdown์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ „๋ฐ˜์ ์ธ ํ๋ฆ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Spring MVC Test, Spring WebFlux WebTestClient, RestAssured ๋“ฑ์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ํ•ด๋‹น ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด Snippet์ด ์ž๋™ ์ƒ์„ฑ๋œ๋‹ค.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•ด๋‘” ํ…œํ”Œ๋ฆฟ ๋ฌธ์„œ์™€ Snippet์„ ๊ฒฐํ•ฉํ•ด ์ตœ์ข…์ ์ธ API ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค.

1.1. Swagger ๋Œ€๋น„ ์žฅ์ 

๋น„์Šทํ•œ ๋Œ€์•ˆ์œผ๋กœ Swagger๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋˜ํ•œ ์‰ฝ๊ฒŒ API๋ฅผ ๋ฌธ์„œํ™”ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”. ํŠนํžˆ Swagger๋Š” API ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•˜๋Š”๋ฐ ํŠนํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Swagger๋Š” ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์— ์• ๋„ˆํ…Œ์ด์…˜์„ ๋ถ€์ฐฉํ•˜๋Š” ๋“ฑ ์ฝ”๋“œ ์นจํˆฌ๋ผ๋Š” ๋‹จ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด Spring REST Docs๋Š” ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์— ๋ณ„๋‹ค๋ฅธ ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„์šธ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ์„ฑ๋˜๋ฉฐ, Snippet์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์„ ๊ฒฝ์šฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ๊ฐ•์ œํ•˜๋ฉฐ, ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฒ€์ฆ๋˜๋ฉด ์ž‘์„ฑ๋˜๋Š” ๋ฌธ์„œ ๋˜ํ•œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.


2. ์„ค์ •

log

Some problems were found with the configuration of task ':asciidoctor' (type 'AsciidoctorTask').
  - In plugin 'org.asciidoctor.convert' type 'org.asciidoctor.gradle.AsciidoctorTask' method 'asGemPath()' should not be annotated with: @Optional, @InputDirectory.

์„ค์ •์„ ์œ„ํ•ด ๊ณต์‹ ๋ฌธ์„œ์— ๊ธฐ์ˆ ๋œ ๋ฐฉ๋ฒ•์„ ์ฐธ๊ณ ํ–ˆ์œผ๋‚˜ ์œ„ ์—๋Ÿฌ์— ๊ณ„์† ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค.

build.gradle

plugins {
    id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

Gradle 7 ๋ถ€ํ„ฐ๋Š” org.asciidoctor.convert๊ฐ€ ์•„๋‹Œ asciidoctor.jvm.convert๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

build.gradle

dependencies {
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}

REST Docs๋ฅผ RestAssured ํ˜น์€ WebTestClient๋กœ ์ž‘์„ฑํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด spring-restdocs-webtestclient ํ˜น์€ spring-restdocs-restassured๋กœ ๊ต์ฒดํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

build.gradle

ext {
    snippetsDir = file('build/generated-snippets')
}

test {
    outputs.dir snippetsDir
    useJUnitPlatform()
}

asciidoctor {
    inputs.dir snippetsDir
    dependsOn test
}

Gradle ๋ฌธ๋ฒ•์— ์ต์ˆ™ํ•˜์ง€ ์•Š์ง€๋งŒ ์•Œ์•„๋‘๋ฉด ์ข‹์€ ๊ฐœ๋…๋“ค์„ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

  • dependsOn : ํŠน์ • Task๊ฐ€ ์˜์กดํ•˜๋Š” Task๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
    • ์ฆ‰, asciidoctor Task๋Š” ์˜์กดํ•˜๋Š” test Task ์ดํ›„์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.
  • finalizedBy : ํŠน์ • Task์˜ ํ›„ํ–‰ Task๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
    • asciidoctor Task์— finalizedBy copy๋ผ๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ์œผ๋ฉด, asciidoctor Task ์ดํ›„ copy Task๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

build.gradle

ext {
    snippetsDir = file('build/generated-snippets')
}

์ƒ์„ฑ๋œ ์Šค๋‹ˆํŽซ์˜ ์ €์žฅ ์œ„์น˜๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.

build.gradle

test {
    outputs.dir snippetsDir
    useJUnitPlatform()
}

ํ…Œ์ŠคํŠธ Task์˜ ์•„์›ƒํ’‹ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์Šค๋‹ˆํŽซ ์ €์žฅ ์œ„์น˜๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

build.gradle

asciidoctor {
    inputs.dir snippetsDir
    dependsOn test
}

์Šค๋‹ˆํŽซ ์ €์žฅ ์œ„์น˜๋ฅผ ์ธํ’‹์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. asciidoctor Task๋Š” ํ…Œ์ŠคํŠธ Task ์ˆ˜ํ–‰ ์ดํ›„์— ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฌธ์„œ๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์ „์— ํ…Œ์ŠคํŠธ๊ฐ€ ์ˆ˜ํ–‰๋˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๊ผญ ์˜์กด์„ฑ์„ ์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ•˜์ง€ ์•Š๊ณ  Jenkins Pipeline์—์„œ Task๋ฅผ ์ ๋‹นํžˆ ์ˆœ์„œ์— ๋งž์ถฐ ์ˆ˜ํ–‰ํ•ด๋„ ๋ฉ๋‹ˆ๋‹ค.


3. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

PostControllerTest.java

// when, then
ResultActions resultActions = mockMvc.perform(get("/api/posts/{id}", "1")
    .accept(MediaType.APPLICATION_JSON_VALUE))
    .andExpect(status().isOk())
    .andExpect(content().string(expected));

verify(postService, times(1)).readById(1L);

// restDocs
resultActions.andDo(document("post-read-one",
    preprocessRequest(prettyPrint()),
    preprocessRequest(prettyPrint()),
    responseFields(fieldWithPath("id").description("๊ฒŒ์‹œ๋ฌผ id"),
        fieldWithPath("title").description("์ œ๋ชฉ"),
        fieldWithPath("content").description("๋‚ด์šฉ"),
        fieldWithPath("author").description("๊ธ€์“ด์ด"),
        fieldWithPath("viewCounts").description("์กฐํšŒ์ˆ˜"),
        fieldWithPath("createdDate").description("์ž‘์„ฑ์ผ"),
        fieldWithPath("modifiedDate").description("์ˆ˜์ •์ผ"),
        fieldWithPath("imageUrls").description("์‚ฌ์ง„ ๋งํฌ")))
);

MockMvcRestDocumentation

public static RestDocumentationResultHandler document(String identifier,
			OperationRequestPreprocessor requestPreprocessor, OperationResponsePreprocessor responsePreprocessor,
			Snippet... snippets) {
         //...
      }

andDo()๋ฉ”์„œ๋“œ์— document()๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์Šค๋‹ˆํŽซ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋Š” ์Šค๋‹ˆํŽซ์˜ Identifier์ž…๋‹ˆ๋‹ค.

preprocessRequest(prettyPrint())๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ์š”์ฒญ๊ณผ ์‘๋‹ต์˜ ๋‚ด์šฉ์„ ํ˜•์‹ํ™”ํ•จ์œผ๋กœ์จ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค. document() ๋ฉ”์„œ๋“œ๋Š” ์˜ค๋ฒ„๋กœ๋”ฉ๋˜์–ด ์žˆ์–ด์„œ prettyPrint()๋ฅผ ์š”์ฒญ, ์‘๋‹ต, ์š”์ฒญ ๋ฐ ์‘๋‹ต ๋ชจ๋‘ ๋“ฑ ์ด 3๊ฐ€์ง€์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Snippet์˜ ๊ฒฝ์šฐ ์š”์ฒญ ํ—ค๋”, ์‘๋‹ต ํ—ค๋”, ์‘๋‹ต ๋ณธ๋ฌธ ๋ฐ์ดํ„ฐ ํ•„๋“œ ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ์š”. ์‚ฌ์šฉ ์˜ˆ์ œ ๋ฐ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด์„œ ํ•™์Šตํ•˜๋ฉด ์ดํ•ด๊ฐ€ ๋น ๋ฅผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PostControllerTest.java

// restDocs
resultActions.andDo(document("post-read-multiple",
    getDocumentRequest(),
    getDocumentResponse(),
    responseFields(fieldWithPath("simplePostResponses[].id").description("๊ฒŒ์‹œ๋ฌผ id"),
        fieldWithPath("simplePostResponses[].title").description("์ œ๋ชฉ"),
        fieldWithPath("simplePostResponses[].content").description("๋‚ด์šฉ"),
        fieldWithPath("simplePostResponses[].author").description("๊ธ€์“ด์ด"),
        fieldWithPath("simplePostResponses[].viewCounts").description("์กฐํšŒ์ˆ˜"),
        fieldWithPath("simplePostResponses[].createdDate").description("์ž‘์„ฑ์ผ"),
        fieldWithPath("simplePostResponses[].modifiedDate").description("์ˆ˜์ •์ผ"),
        fieldWithPath("startPage").description("์‹œ์ž‘ ํŽ˜์ด์ง€"),
        fieldWithPath("endPage").description("๋ ํŽ˜์ด์ง€"),
        fieldWithPath("prev").description("์ด์ „ ํŽ˜์ด์ง€ ์—ฌ๋ถ€"),
        fieldWithPath("next").description("๋‹ค์Œ ํŽ˜์ด์ง€ ์—ฌ๋ถ€")))
);

List๊ฐ€ ํฌํ•จ๋œ ์‘๋‹ต์€ []๋ฅผ ํ†ตํ•ด ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์‘๋‹ต์ด ๋‹จ์ผ ๋ฆฌ์ŠคํŠธ๋งŒ์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์˜ˆ์ œ์™€ ๋‹ค๋ฅด๊ฒŒ [].id, [].title๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PostControllerTest.java

resultActions.andDo(document("post-write-login",
    getDocumentRequest(),
    getDocumentResponse(),
    requestHeaders(headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer token")),
    requestPartBody("images"),
    responseHeaders(headerWithName(HttpHeaders.LOCATION).description("๊ฒŒ์‹œ๋ฌผ ์ฃผ์†Œ")))
);

ํ—ค๋” ๋˜ํ•œ ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2021-09-01 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 2 48 32

Gradle ์˜ต์…˜์—์„œ Test๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์Šค๋‹ˆํŽซ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ ์Šค๋‹ˆํŽซ๊ณผ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•ด๋‘” ํ…œํ”Œ๋ฆฟ ๋ฌธ์„œ๋ฅผ ๊ฒฐํ•ฉํ•ด ์ตœ์ข… API HTML ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ๋ฏธ๋ฆฌ ์ž‘์„ฑํ•ด๋‘˜ ํŒŒ์ผ์€ Gradle ๊ธฐ์ค€ src/docs/asciidoc์— ์œ„์น˜์‹œํ‚ต๋‹ˆ๋‹ค. ์ด ๋•Œ ํŒŒ์ผ ํ™•์žฅ์ž๋Š” .adoc์ž…๋‹ˆ๋‹ค.

api.adoc

ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]

== User
=== ํšŒ์›ํƒˆํ‡ด (๋น„๋กœ๊ทธ์ธ)
==== Request
include::{snippets}/withdraw-not-login/http-request.adoc[]
==== Response
include::{snippets}/withdraw-not-login/http-response.adoc[]

=== ํšŒ์›ํƒˆํ‡ด (๋กœ๊ทธ์ธ)
=== ๊ฒŒ์‹œ๋ฌผ ๋‹จ๊ฑด ์กฐํšŒ
==== Request
include::{snippets}/post-read-one/http-request.adoc[]
==== Response
include::{snippets}/post-read-one/http-response.adoc[]
==== Fields
include::{snippets}/post-read-one/response-fields.adoc[]

์ •์  ๋ธ”๋กœ๊ทธ๋ฅผ ๊พธ๋ฏธ๋“ฏ์ด API ๋ฌธ์„œ ๋˜ํ•œ ์กฐ๊ธˆ๋งŒ ๋” ๋…ธ๋ ฅ์„ ๋“ค์ด๋ฉด ๋งค์šฐ ์•„๋ฆ„๋‹ต๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฐ๊ฐ์˜ ์Šค๋‹ˆํŽซ identifier ํด๋” ํ•˜์œ„์—๋Š” ์ฒจ๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ adoc๋“ค์ด ์กด์žฌํ•˜๋Š”๋ฐ, ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ž˜ ์„ ํƒํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

build.gradle

bootJar {
    dependsOn asciidoctor
    copy {
        from "${asciidoctor.outputDir}"
        into 'BOOT-INF/classes/static/docs'
    }
}

ํ•ด๋‹น ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ณ , ์ด๋ฒˆ์—๋Š” Gradle ์˜ต์…˜์—์„œ bootJar๋ฅผ ์‹คํ–‰ํ•ด๋ด…์‹œ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2021-09-01 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 2 59 33

bootJar๋ฅผ ์‹คํ–‰ํ•˜๋ฉด src/docs/asciidoc์— ์œ„์น˜์‹œํ‚จ ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ adoc ํŒŒ์ผ๊ณผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ์ƒ์„ฑ๋œ ์Šค๋‹ˆํŽซ๋“ค์ด ์กฐํ•ฉ๋œ ์ตœ์ข… API ๋ฌธ์„œ๊ฐ€ build/docds/asciidoc ๋””๋ ‰ํ† ๋ฆฌ์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

build.gradle

bootJar {
    dependsOn asciidoctor
    copy {
        from "${asciidoctor.outputDir}"
        into 'BOOT-INF/classes/static/docs'
    }
    finalizedBy 'copyDocument'
}

task copyDocument(type: Copy) {
    dependsOn bootJar
    from file("build/docs/asciidoc")
    into file("src/main/resources/static/docs")
}

bootJar Task๋ฅผ ํ†ตํ•ด api.html์ด ์ƒ์„ฑ๋˜๋ฉด, ํ›„ํ–‰ Task๋กœ copyDocument๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํ•ฉ์‹œ๋‹ค. copyDocument๊ฐ€ ์ˆ˜ํ–‰๋˜๋ฉด classpath(resources)๋กœ API ๋ฌธ์„œ๊ฐ€ ๋ณต์‚ฌ๋ฉ๋‹ˆ๋‹ค.

image

image

bootJar๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ปดํŒŒ์ผ์„ ๊ฑฐ์น˜๊ณ  ํ…Œ์ŠคํŠธ Task๊ฐ€ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ Task๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ์˜์กด ๊ด€๊ณ„์— ๋”ฐ๋ผ asciidoctor, bootJar, copyDocument Task๊ฐ€ ๊ฐ๊ฐ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

image

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด ์ž˜ ์ ‘์†๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


3. ๊ธฐํƒ€

build.gradle

asciidoctor.doFirst {
    delete file('src/main/resources/static/docs')
}

๋งŒ์•ฝ ๋ฌธ์„œ์— ์ˆ˜์ •์ด ๋ฐœ์ƒํ–ˆ์Œ์—๋„ ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹ค์Œ ํƒœ์Šคํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ณต์‚ฌ ์ด์ „์— ๊ธฐ์กด์— ์žˆ๋˜ ํด๋ž˜์ŠคํŒจ์Šค์˜ API ๋ฌธ์„œ๋ฅผ ์ œ๊ฑฐํ•˜๋„๋ก ํ•ฉ์‹œ๋‹ค.



Reference