Spring Rest Docs 적용

최준호·2022년 1월 9일
0

eats

목록 보기
5/13

📕 api 문서화

api를 작업하면 문서화 작업이 필요한데 이를 한번에 해결해주는 라이브러리이다.

sweggar와 rest docs를 많이 사용하던데 rest docs를 선택한 이유는 저번에 sweggar를 작업해보니 코드상에 계속해서 sweggar 코드가 붙는다. 이걸 보기 싫었는데 rest docs는 코드상에 추가되진 않는다고 한다. 하지만 추가하기가 어렵다고 하니 일단 도전해보자.

그리고 우아한에서 쓴다고 한다.

우아한형제들 기술블로그

🛒 gradle 추가

plugins {
	id 'org.springframework.boot' version '2.6.2'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'java'
	id "org.asciidoctor.jvm.convert" version "3.3.2"	//(1) asciidoctor 추가
}

group = 'com.juno'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'	//(2) mockMvc에서 restdocs를 사용할 수 있도록 추가
}

ext {
	snippetsDir = file('build/generated-snippets')	//(3) 빌드시 snippets 파일들이 저장될 저장소
}

test {
	useJUnitPlatform()
	outputs.dir snippetsDir	//(4) test 실행 시 파일을 (3)에서 설정한 저장소에 출력하도록 설정
}

asciidoctor {	//(5) asccidoctor 설정
	dependsOn test
	inputs.dir snippetsDir
}

asciidoctor.doFirst {	//(6) asciidoctor가 실행될 때 docs 하위 파일 삭제
	delete file('src/main/resources/static/docs')
}

bootJar {	//(7) bootJar 시 asciidoctor 종속되고 build하위 스니펫츠 파일을 classes 하위로 복사
	dependsOn asciidoctor
	copy {
		from "${asciidoctor.outputDir}"
		into 'BOOT-INF/classes/static/docs'
	}
}

task copyDocument(type: Copy) {		//(8) from의 파일을 into로 복사
	dependsOn asciidoctor
	from file("build/docs/asciidoc")
	into file("src/main/resources/static/docs")
}

build {		//(9) build 시 copyDocument 실행
	dependsOn copyDocument
}

gradle은 다음과 같이 세팅해주었다.

spring rest docs 공식 문서
참고글
gradle 세팅 참고글

💼 test code 추가

rest docs는 무조건 test code를 작성해야 문서에 추가가 된다.

먼저 테스트를 할 url을 만들자

@PostMapping("/v1/rest_docs")
public ResponseEntity restDocs(@RequestBody RestDocsDto rdd){

    CommonV1.CommonV1Builder<Object> builder = CommonV1.builder();
    builder.result("success");
    builder.code("200");
    builder.data(rdd);
    builder.msg("성공");

    return ResponseEntity.status(HttpStatus.OK).body(builder.build());
}

@Data
public static class RestDocsDto {
    String id;
    String pw;
}

위 코드와 같이 테스트를 위한 dto와 controller를 만들었다.

import org.json.JSONObject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import javax.transaction.Transactional;

import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs(uriHost = "api", uriPort = 8081)
@Transactional
class TestControllerTest {
    @Autowired
    private MockMvc mock;

    @Test
    @DisplayName("테스트")
    void test() throws Exception{
        //given
        JSONObject json = new JSONObject();
        json.put("id", "testId1");
        json.put("pw", "testPw2");

        //when
        ResultActions act = mock.perform(MockMvcRequestBuilders.post("/v1/rest_docs").contentType(MediaType.APPLICATION_JSON).content(json.toString()));

        //then
        act.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("200"));

        //docs
        act.andDo(document("test-api",
                preprocessRequest(prettyPrint()),   //request json 형식으로 이쁘게 출력
                preprocessResponse(prettyPrint()),  //response json 형식으로 이쁘게 출력
                requestFields(  //request parameter
                        fieldWithPath("id").type(JsonFieldType.STRING).description("아이디"),
                        fieldWithPath("pw").type(JsonFieldType.STRING).description("비밀번호")
                ),
                responseFields( //response parameter
                        fieldWithPath("result").type(JsonFieldType.STRING).description("결과"),
                        fieldWithPath("code").type(JsonFieldType.STRING).description("결과 코드"),
                        fieldWithPath("msg").type(JsonFieldType.STRING).description("결과 메세지"),
                        fieldWithPath("data.id").type(JsonFieldType.STRING).description("아이디"),
                        fieldWithPath("data.pw").type(JsonFieldType.STRING).description("비밀번호")
                )
        ));
    }
}

test code 작성시 rest docs의 메서드들이 자동으로 import되지 않는 현상이 있는데 우선 메서드명을 적으면 alt + enter를 사용하여 import가 가능하다. 그것도 안되면 직접 import 파일 경로를 직접 쳐서 넣어도 된다. intellij에서 바로 인식하지 않아서 한참을 헤맸다.

그 후에 테스트 코드를 위와 같이 작성하고

gradle의 경우 src/docs/asciidoc/{파일명}.adoc 파일을 작성해주어야한다.

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

= EATS API
:toc: left
:toclevels: 4
:toc-title: eats api

== API TEST

=== REQUEST

include::{snippets}/test-api/http-request.adoc[]

=== REQEUST FIELD

include::{snippets}/test-api/request-fields.adoc[]

=== RESPONSE

include::{snippets}/test-api/http-response.adoc[]

=== RESPONSE FIELD

include::{snippets}/test-api/response-fields.adoc[]

다음과 같이 작성하였는데 adoc의 파일을 작성하는 방법은 직접 찾아보는게 제일 좋은 것 같다. 내가 작성한 방법만 간단하게 설명하면

:toc 목차
= 리스트
incldue:: 파일 추가

용도로 사용했다.

모두 작성하여 build를 실행하면 html파일이 떨어지고 해당 파일을 열어보면 다음과 같은 결과를 확인할 수 있다. build 후 생성된 파일을 controller에서 url로 연결해주면 페이지에 url로 접근이 가능하게 된다.

👍 rest docs를 적용하며 느낀점

  1. api를 만들면서 문서 작성의 자동화에 대해 많은 개발자들이 고민하고 있다는 것을 느꼈다.
  2. sweggar가 적용하기엔 정말 편했지만 rest docs의 강제적인 test code 작성과 서비스 로직상에 추가되는 코드가 없다는 것은 가장 큰 장점인것 같다.
  3. md 말고 adoc이라는 문서 작성 방법을 배울 수 있어서 좋았다.
profile
해당 주소로 이전하였습니다. 감사합니다. https://ililil9482.tistory.com

0개의 댓글