Spring/Spring Boot

[Spring] API 문서 자동화 2 - Sping REST Docs

민돌v 2021. 12. 31. 17:55
728x90
✨ api 문서화 정리 글
  1. Swagger
  2. Spring Rest Docs
  3. RestDocs + Swagger-UI 같이사용하기

 

아래의 버전인 상황에서 적용을해본 예제입니다 참고해주세요 :)

  • spring boot 3.0
  • jdk 17
  • gradle 7.5.1

1. Spring REST Docs란

Spring 에서 API 문서를 자동화하는 또 다른 방법은 Spring Rest Docs 입니다.

 

2. Swagger VS Spring Rest Docs

자바 문서 자동화에는 주로 Swagger 와 Spring Rest Docs 가 사용됩니다.

 

  Spring Rest Docs Swagger
장점 제품코드에 영향이 없다 API를 테스트 해 볼수 있는 화면을 제공한다.
  테스트가 성공해야 문서가 작성된다. 적용하기 쉽다.
단점 적용하기 어렵다 제품코드에 어노테이션을 추가해야한다.
    제품코드와 동기화가 안될 수 도있다.

 

Swagger는 API 동작을 테스트하는 용도에 더 특화되어있다고 합니다.
반면에 Spring Rest Docs 은 깔끔 명료한 문서를 만들 수도 있고, 코드에 추가할것도 없고, 테스트 수행 후 deploy (배포)가 된다는 장점도 있습니다.

 

 

3. Spring Rest Docs 사용법

Spring Project version 에 따른 적용방법 가이드

 

1) 의존성 추가

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.0.0'
	id 'io.spring.dependency-management' version '1.1.0'

	// gradle 7.0 이상부터는 jvm 사용
	id "org.asciidoctor.jvm.convert" version "3.3.2"
	//id "org.asciidoctor.convert" version "1.5.9.2"
}

group = 'com.tht'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
	asciidoctorExtensions // dependencies 에서 적용한 것 추가
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
    implementation 'com.google.code.gson:gson:2.8.8'

	//SpringRest Docs
    
    // restdocs-mockmvc의 testCompile 구성 -> mockMvc를 사용해서 snippets 조각들을 뽑아낼 수 있게 된다.
    // MockMvc 대신 WebTestClient을 사용하려면 spring-restdocs-webtestclient 추가
    // MockMvc 대신 REST Assured를 사용하려면 spring-restdocs-restassured 를 추가
    testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'	 //(5)
    
    // build/generated-snippets 에 생긴 .adoc 조각들을 프로젝트 내의 .adoc 파일에서 읽어들일 수 있도록 연동해줍니다.
    // 이 덕분에 .adoc 파일에서 operation 같은 매크로를 사용하여 스니펫 조각들을 연동할 수 있는 것입니다.
    // 그리고 최종적으로 .adoc 파일을 HTML로 만들어 export 해줍니다.
    asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.4.RELEASE'
}

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

test {
    // 위에서 작성한 snippetsDir 디렉토리를 test의 output으로 구성하는 설정 -> 스니펫 조각들이 build/generated-snippets로 출력
    outputs.dir snippetsDir
    useJUnitPlatform()
    
    //filter {
    //    includeTestsMatching "com.sparta.springcore.controller.*"
    //}
}

asciidoctor {
	dependsOn test
	configurations 'asciidoctorExtensions' // 위에서 작성한 configuration 적용
	inputs.dir snippetsDir
}

//build시 문서 실시간 동기화
bootJar {
	dependsOn asciidoctor 
	from ("${asciidoctor.outputDir}") {
		into 'static/docs'
	}
}

// static/docs 폴더 비우기
asciidoctor.doFirst {
	delete file('src/main/resources/static/docs/')
}


task copyDocument(type: Copy) {
	dependsOn asciidoctor

	from file("build/docs/asciidoc/")
	into file("src/main/resources/static/docs")
}

build {
	dependsOn copyDocument
}

// ✨참고사항 //
// 공식 문서에서는 위의 ascidoctor.doFirst부터 아래 내용은 없고 이와 같은 내용만 있습니다.
// 이렇게 하면 jar로 만들어 질때 옮겨지는 것으로 IDE로 돌릴 때는 build 폴더에서만 확인이 가능합니다.
// 위 방법을 사용하면 IDE에서도 static으로 옮겨진 것을 확인할 수 있습니다.
// 위에 방법을 사용하든 아래 방법을 사용하든 편한 선택지를 사용하시면 됩니다.
bootJar {
    dependsOn asciidoctor	 //(3)
    from ("${asciidoctor.outputDir}/html5") {	 //(4)
        into "/static/docs"
    }
}

Spring Rest Docs는 Asciidoctor 툴을 사용한다. plain text로 작성한 문서를 특별한 문법에 맞춰(mark down과 유사) page 문서로 만들어준다.

(1) AsciiDoc 파일을 컨버팅하고 Build 폴더에 복사하기 위한 플러그인입니다.
(2) gradle build 시 test -> asciidoctor 순으로 수행됩니다.
(3) gradle build 시 asciidoctor -> bootJar 순으로 수행됩니다.
(4) gradle build 시 ./build/asciidoc/html5/ 에 html 파일이 생깁니다.
이것을 jar 안에 /static/docs/ 폴더에 복사가 됩니다.
(5) mockmvc 를 restdocs 에 사용할수 있게 하는 라이브러리입니다.

  • 위와 같이 세팅하게 되면 ./gradlew build 진행시 test -> asciidoctor -> copyDocument -> build 순서로 진행되게 됩니다.
    정확한 설정은 공식문서를 참고하시는 것을 권장합니다.

 

👏🏻 만약 build/asciidoc/html5/ html파일을 src/main/resources/static/doc 복사해주고 싶으시다면 아래 설정을 추가해 주시면 됩니다.

task copyDocument(type: Copy) {
    dependsOn asciidoctor

    from file("build/asciidoc/html5/")
    into file("src/main/resources/static/docs")
}

build {
    dependsOn copyDocument
}

 

2) Spring  Test  properties

  • Spring Rest Docs는 실행하기전에 테스트를 먼저 돌리기 때문에, 테스트 코드를 작성해줘야만합니다. (무족권)
  • 테스트할 때 사용할 properties를 만들어주자

 

application-test.properties

//properties
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:springcoredb

jwt.secret=javainuse



//yaml
spring:
    h2:
        console:
            enabled: true
    datasource:
        url: jdbc:h2:mem:testdb
        driver-class-name: org.h2.Driver
        username: sa
        password:

테스트용이기 때문에 인메모리 디비인 h2를 사용한다.

dependencies {
   //h2
   implementation 'com.h2database:h2'
}

스프링 시큐리티를 사용하고 있다면 RestDocs url을 풀어준다.

                .antMatchers("/docs/**").permitAll()

 


 

3) Test Controller 만들기

1. 간단한 컨트롤러 만들기

@RestController
public class RestDocsController {

    @GetMapping("rest-docs")
    public ResponseEntity<String> getRestDocsHello() {
        return ResponseEntity.ok("Let's Start Spring Rest Docs");
    }
}

 

2. 테스트코드 작성하기

1️⃣ 1번 방법

@WebMvcTest(RestDocsController.class)
class RestDocsControllerTest {

    private MockMvc mockMvc;

    @BeforeEach
    public void setUp(WebApplicationContext webApplicationContext,
        RestDocumentationContextProvider restDocumentation) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
            .apply(documentationConfiguration(restDocumentation))
            .build();
    }
  • webAppContextSetup()만 쓴다면 여러분들이 아시는 Mock을 사용하기 위한 일반적인 MockMvc의 setting이지만 
  • apply(documentationConfiguration(restDocumentation))를 추가함으로써 문서화를 할 수 있는거라고 합니다.

 

2️⃣ 2번 방법

@AutoConfigureMockMvc // -> webAppContextSetup(webApplicationContext)
@AutoConfigureRestDocs // -> apply(documentationConfiguration(restDocumentation))
@WebMvcTest(RestDocsController.class)
class RestDocsControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName("Spring Rest Docs Test")
    void restDocsTest() throws Exception {

        String text = "parameter test";

        ResultActions resultActions = mockMvc.perform(
            //RestDocs를 사용하기 위해서는 RestDocumentationRequestBuilders 의 get을 사용해야함
            RestDocumentationRequestBuilders.get("/rest-docs/{text}", text)
                .contentType(MediaType.APPLICATION_JSON)
                .accept(MediaType.APPLICATION_JSON)
        ).andDo( // rest docs 문서 작성 시작
            MockMvcRestDocumentation.document("guide-rest-docs", // 문서 조각 디렉토리 명
                RequestDocumentation.pathParameters( //path 파라미터 정보 입력
                    RequestDocumentation.parameterWithName("text").description("파라미터 1")
                )
            )
        );

        resultActions.andExpect(MockMvcResultMatchers.status().isOk());
    }
}
  • 이렇게 해주시면 위의 복잡한 설정을 어노테이션이 대신 해줄 수 있습니다.
  • 자세히 알고 싶으신 분들은 @AutoConfigureMockMvc와 @AutoConfigureRestDocs를 참고하시기 바랍니다.
  • 평소에 Controller test 의 request 는 MockMvcRequestBuilders 를 사용했는데 Spring Rest Docs 를 사용해주기 위해서는 RestDocumetationRequestBuilders 를 사용해야 되더군요

 

2️⃣ parmeter 값을 세팅해주는 방법 (출처)

@Test
public void member_get() throws Exception {
	// 조회 API -> 대상의 데이터가 있어야 합니다.
	mockMvc.perform(
    	get("/api/members/{id}", 1L)
			.contentType(MediaType.APPLICATION_JSON)
        )
        .andExpect(status().isOk())
        .andDo( // rest docs 문서 작성 시작
		document("member-get", // 문서 조각 디렉토리 명
			PayloadDocumentation.responseFields(
                    PayloadDocumentation.fieldWithPath("id").description("게시글 ID"),
                    PayloadDocumentation.fieldWithPath("title").description("게시글 제목"),
                    PayloadDocumentation.fieldWithPath("content").description("게시글 내용")
                )
			)
		)
	);
}

 

 

이렇게하고 build(test)를 돌리면 gradle 에서 설정한경로( build/generated-snippets 폴더) 에 테스트에서 작성한 docs가 조각들이 생성되어 있습니다.

기본적으로 다음과 같은 조각들이 default로 생성됙, 이 조각들로 문서를 작성합니다.

  • curl-request.adoc
  • http-request.adoc
  • httpie-request.adoc
  • http-response.adoc
  • request body
  • response body

 


 

4) Rest Docs 문서 작성하기

위에 만들어진 파일들은 test 가 돌아가면서 생성된 adoc 문서 조각 파일이지 아직 문서가 아닙니다.

차차 문서로 만들어봅시당

 

1. index.adoc 파일 만들기

  • .adocs 파일을 이용해서, 흩어져잇는 문서 조각들을 문서화 시킬 수 있습니다.
  • 그 전에, 폴더를 만들어봅시다.
    1. main/resources/static/docs 디렉토리를 만들어줍니다. 앞서 gradle 설정에 의해 이곳으로 html 파일이 복사되어 이곳으로 옮겨집니다.
    2. 그리고 src/docs/asciidoc 디렉토리를 만들고 안에 index.adoc 파일을 만들어줍니다. 
  • Index.adoc 파일을 만드니, 인텔리제이에서 플러그인을 추천해줍니다. 까니까 readme 처럼 옆에 미리보기가 지원되더군요
  •  

adocs 폴더 구조

 

2. index.adoc 파일 작성하기

  • adocs 문서파일을 작성해봅시당
  • adocs 파일을 작성하고, 다시 빌드를 해주면 main/resources/static/docs 폴더에 index.html 파일이 만들어지게 됩니다.
= REST Docs 문서 만들기 (글의 제목)
backtony.github.io(부제)
:doctype: book
:icons: font
:source-highlighter: highlightjs // 문서에 표기되는 코드들의 하이라이팅을 highlightjs를 사용
:toc: left // toc (Table Of Contents)를 문서의 좌측에 두기
:toclevels: 2
:sectlinks:

[[Rest-Docs-test-API]]
== Rest 테스트
operation::guide-rest-docs[snippets='http-request,path-parameters,http-response']



[[기타-API]]
== 기타 API
  • :source-highlighter: highlightjs
    • 문서에 표기되는 코드들의 하이라이팅을 highlightjs를 사용합니다.
  • :toc: left
    • toc (Table Of Contents)를 문서의 좌측에 둡니다.
  • =, ==, ===
    • Markdown의 #, <h1>, <h2>, <h3> 와 같은 역할을 합니다.
  • [[텍스트]]
    • 해당 텍스트에 링크를 겁니다.
  • operation::디렉토리명[snippets=’원하는 조각들’]
    • 문서로 사용할 조각들을 명시해줍니다.
    • 🔥 각 상황에 따라 문서화할 조각들이 달라지므로 자세히 봐주세요
  • include::{snippets}/member-get/XXX.adoc[]
    • opertaion의 경우 한번에 원하는 조각들을 넣을 수 있었는데 include는 특정 adoc을 지정하여 넣을 수 있습니다.

추가적으로 다른 Asciidoc 사용법은 [링크]를 참고하시면 도움이 될 것 같습니다.

 

 

➡️ 이렇게 .adoc 파일을 작성하게 되면, build 시 gradle 설정에따라 .adoc 문서를 .build 안에 html 파일로 만들어줍니다. (이 글 처럼 copy docs를 설정하였다면 "resources/static/docs/" 안에 파일이 생성되어있습니다

 

 


*참고

반응형