🌈 [Section3] 12. [ Spring MVC ] API λ¬Έμ„œν™”

ν˜„μ£ΌΒ·2022λ…„ 11μ›” 14일
1

bootcamp

λͺ©λ‘ 보기
52/71

πŸ“• 였늘 배운 λ‚΄μš©!

  • API λ¬Έμ„œν™”
  • Swagger vs Spring Rest Docs
  • Spring Rest Docs
  • Asciidoc

✏️ API λ¬Έμ„œν™” (Documentation)

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ REST API λ°±μ—”λ“œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— μš”μ²­μ„ μ „μ†‘ν•˜κΈ° μœ„ν•΄μ„œ μ•Œμ•„μ•Ό λ˜λŠ” μš”μ²­ 정보λ₯Ό λ¬Έμ„œλ‘œ 잘 μ •λ¦¬ν•˜λŠ” 것
    ( ν΄λΌμ΄μ–ΈνŠΈ μͺ½μ—μ„œ 이 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ‚¬μš©μ„ μœ„ν•΄ μš”μ²­ URL / request body / query parameter 등이 ν•„μš” )

  • κ°œλ°œμžκ°€ 직접 수기둜 μž‘μ„±ν•  μˆ˜λ„ 있고, μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λΉŒλ“œλ₯Ό 톡해 API λ¬Έμ„œλ₯Ό μžλ™μœΌλ‘œ 생성할 μˆ˜λ„ 있음
    ( But, 수기둜 μž‘μ„±ν•˜λŠ” 것은 μ•„μ£Ό λΉ„νš¨μœ¨μ  ➜ API λ¬Έμ„œ μžλ™ν™” μ‚¬μš©)

βœ”οΈ REST API

βœ” API λ¬Έμ„œ μƒμ„±μ˜ μžλ™ν™”κ°€ ν•„μš”ν•œ 이유

➜ 수기둜 μž‘μ„±ν–ˆμ„ λ•Œ μ‹œκ°„λ„ 많이 λ“€κ³  μ—λŸ¬ λ°œμƒ κ°€λŠ₯성이 λ†’μ•„ λΉ„νš¨μœ¨μ μ΄κΈ° λ•Œλ¬Έμ— μžλ™ν™” μ‚¬μš©


✏️ Swagger vs Spring Rest Docs

βœ” Swagger의 API λ¬Έμ„œν™” 방식

( SwaggerλΌλŠ” API λ¬Έμ„œ μžλ™ν™” μ˜€ν”ˆ μ†ŒμŠ€ μ‚¬μš©ν•œ 방식 )

  • μ• ν„°λ„€μ΄μ…˜ 기반의 API λ¬Έμ„œν™” 방식
    ( μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œμ— λ¬Έμ„œν™”λ₯Ό μœ„ν•œ λ§Žμ€ μ• λ„ˆν…Œμ΄μ…˜λ“€μ΄ 포함됨 )

  • 가독성 및 μœ μ§€ λ³΄μˆ˜μ„±μ΄ 떨어짐

  • API λ¬Έμ„œμ™€ API μ½”λ“œ κ°„μ˜ 정보 뢈일치 문제 λ°œμƒ κ°€λŠ₯
    ( μ• λ„ˆν…Œμ΄μ…˜ 내에 API μŠ€νŽ™ 정보λ₯Ό λ¬Έμžμ—΄λ‘œ μž…λ ₯ν•˜λŠ” κ²½μš°κ°€ 많기 λ•Œλ¬Έ )

  • API 툴둜써의 κΈ°λŠ₯ ν™œμš© κ°€λŠ₯
    ( API λ¬Έμ„œ λ‚΄μ—μ„œ Execute λ²„νŠΌμ„ 눌러 Controller에 μš”μ²­ κ°€λŠ₯ )

[μ°Έκ³ ] https://swagger.io/docs/specification/about/

βœ” Spring Rest Docs의 API λ¬Έμ„œν™” 방식

  • ν…ŒμŠ€νŠΈ μ½”λ“œ 기반의 API λ¬Έμ„œν™” 방식

  • μ• ν”Œλ¦¬μΌ€μ΄μ…˜ μ½”λ“œμ— λ¬Έμ„œν™”λ₯Ό μœ„ν•œ 정보듀이 ν¬ν•¨λ˜μ§€ μ•ŠμŒ

  • ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ 싀행이 κΌ­ passedμ—¬μ•Ό API λ¬Έμ„œκ°€ 생성됨
    ➜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— μ •μ˜λ˜μ–΄ μžˆλŠ” API μŠ€νŽ™ 정보와 API λ¬Έμ„œ μ •λ³΄μ˜ 뢈일치둜 인해 λ°œμƒν•˜λŠ” 문제λ₯Ό 방지 κ°€λŠ₯

  • ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό λ°˜λ“œμ‹œ μž‘μ„±ν•΄μ•Όν•¨

  • API 툴둜써의 κΈ°λŠ₯은 μ œκ³΅ν•˜μ§€ μ•ŠμŒ


✏️ Spring Rest Docs

  • 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

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 클래슀 지정

  • (2) @MockBean(JpaMetamodelMappingContext.class)

    • 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에 λŒ€ν•œ μžλ™ ꡬ성을 μœ„ν•œ μ• λ„ˆν…Œμ΄μ…˜
  • (10) API λ¬Έμ„œλ₯Ό μžλ™ μƒμ„±ν•˜κΈ° μœ„ν•œ ν•΄λ‹Ή Controller ν•Έλ“€λŸ¬ λ©”μ„œλ“œμ˜ API μŠ€νŽ™ 정보λ₯Ό document(…)에 μΆ”κ°€

❗ μœ„ κΈ°λ³Έ ꡬ쑰λ₯Ό 따라 API μŠ€νŽ™ 정보λ₯Ό μž‘μ„±ν•˜λ©΄λ¨ !
( μžμ„Έν•œ λ‚΄μš©μ€ μ‹€μŠ΅ 파일 μ°Έκ³  )

βœ”οΈ @SpringBootTest vs @WebMvcTest
β €

  • @SpringBootTest μ• λ„ˆν…Œμ΄μ…˜
    • @AutoConfigureMockMvc와 ν•¨κ»˜ μ‚¬μš©
    • ν”„λ‘œμ νŠΈμ—μ„œ μ‚¬μš©ν•˜λŠ” 전체 Bean을 ApplicationContext에 λ“±λ‘ν•˜μ—¬ μ‚¬μš©
      ➜ ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ κ΅¬μ„±ν•˜λŠ” 것은 편리
      ➜ But, μ‹€ν–‰ 속도가 μƒλŒ€μ μœΌλ‘œ 느림
      β €
      πŸ‘‰ λ°μ΄ν„°λ² μ΄μŠ€κΉŒμ§€ μš”μ²­ ν”„λ‘œμ„ΈμŠ€κ°€ μ΄μ–΄μ§€λŠ” 톡합 ν…ŒμŠ€νŠΈμ— 주둜 μ‚¬μš©
      β €
  • @WebMvcTest μ• λ„ˆν…Œμ΄μ…˜
    • Controller ν…ŒμŠ€νŠΈμ— ν•„μš”ν•œ Bean만 ApplicationContext에 λ“±λ‘ν•˜μ—¬ μ‚¬μš©
      ➜ μ‹€ν–‰ 속도 μƒλŒ€μ μœΌλ‘œ 빠름
      But, Controllerμ—μ„œ μ˜μ‘΄ν•˜κ³  μžˆλŠ” 객체가 μžˆλ‹€λ©΄ ν•΄λ‹Ή 객체에 λŒ€ν•΄μ„œ Mock 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜μ‘΄μ„±μ„ 일일이 μ œκ±°ν•΄ 주어야함
      β €
      πŸ‘‰ Controllerλ₯Ό μœ„ν•œ 슬라이슀 ν…ŒμŠ€νŠΈμ— 주둜 μ‚¬μš©

✏️ 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둜 지정 ➜ ==== κΉŒμ§€μ˜ 제λͺ©λ§Œ λͺ©μ°¨μ— ν‘œμ‹œλ¨ )
  • (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::

    • 이미지 μΆ”κ°€
      Ex. image::https://spring.io/images/spring-logo-9146a4d3298760c2e7e49595184e1975.svg[spring]

[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 λ¬Έμ„œλ₯Ό 직접 λ³Ό 수 μžˆμ–΄μ„œ 더 μž¬λ―ΈμžˆλŠ” μ±•ν„°μ˜€λ‹€ !

0개의 λŒ“κΈ€