postman
으로 API 설계를 했는데, API의 변경이 발생할 때마다 동기화를 손수 해주는 게 굉장히 힘들었다
문서를 항상 최신화할 순 없을까
테스트 코드 기반으로 Restful API 문서를 돕는 도구
테스트 코드가 일치하지 않으면 테스트 빌드가 실패하기 때문에 테스트 코드로 검증된 문서가 보장된다
Asciidoctor
를 이용하여 HTML등 다양한 포맷으로 문서를 자동으로 출력
OAS(OpenAPI Specification)
를 활용하여 Swagger
를 쉽게 만들수 있다
plugins {
// AsciiDoc 파일을 컨버팅 후, Build 폴더에 복사하기 위한 플러그인
id "org.asciidoctor.convert" version "1.5.9.2"
}
repositories {
mavenCentral()
}
dependencies {
// Ascii Docs
asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
ext {
// Snippet 의 생성 위치를 지정
snippetsDir = file('build/generated-snippets')
}
test {
// test시 JUnit을 사용
useJUnitPlatform()
outputs.dir snippetsDir
}
asciidoctor {
// Snippets 디렉토리를 Input 디렉토리로 설정
inputs.dir snippetsDir
// 문서 생성 전 테스트가 실행되도록 test 에 종속 설정
dependsOn test
}
bootJar {
// 빌드 전 문서 생성 확인
dependsOn asciidoctor
// 생성된 문서를 static/docs 에 복사
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
FriendController.java
@RestController
public class FriendController {
@GetMapping("/friends")
public ResponseEntity<List<FriendDto>> friendList(
@RequestParam Long userNo
){
List<FriendDto> res = new ArrayList<>();
for (int i = 1; i < 5; i++){
res.add(
FriendDto
.builder()
.id((long) i)
.name("name" + i)
.isAlumni(i%2==0)
.build()
);
}
return new ResponseEntity<>(res, HttpStatus.OK);
}
}
FriendControllerTest.java
@WebMvcTest
@AutoConfigureRestDocs
class FriendControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper; // (0)
@Test
public void friendListTest() throws Exception {
ResultActions resultActions = mockMvc.perform(
get("/friends") // (1)
.param("userNo","10") // (2)
.contentType(MediaType.APPLICATION_JSON));
resultActions
.andExpect(status().isOk()) // (3)
.andDo(print())
.andDo(document("friends-list", // (4)
preprocessRequest(prettyPrint()), // (5)
preprocessResponse(prettyPrint()),
requestParameters( // (6)
parameterWithName("userNo").description("친구 찾을 사람 ID")
),
responseFields( // (7)
fieldWithPath("[].id").type(NUMBER).description("태그 아이디"),
fieldWithPath("[].name").type(STRING).description("태그 제목"),
fieldWithPath("[].isAlumni").type(BOOLEAN).description("매니저 유무"))
));
}
}
RequestBody
요청을 처리하고 싶을때 추가RestDocumentationRequestBuilders
사용PathVariable
이 있다면 차례대로 가변배열처럼 넣어준다RequestBody
이 있다면 .content(objectMapper.writeValueAsString(dto))
형태로 넣어준다.andExpect
로 해당 테스트의 결과를 예상하고 비교할 수 있다.MockMvcRestDocumentationWrapper
사용PathVariable
이 있다면 이름을 pathParameters
로 사용테스트 코드를 동작하면 build/generated-snippets
폴더 하위로 friends-list
가 생기는데, 내가 docs에 넣고싶은 파일들을 넣어준다
Docs 폴더를 만들어서 (이름).adoc
파일을 만든다
= Rest Docs
:doctype: book
:icons:font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
=== 친구 리스트 반환
=== REQUEST
include::{snippets}/friends-list/http-request.adoc[]
include::{snippets}/friends-list/request-parameters.adoc[]
=== RESPONSE
include::{snippets}/friends-list/http-response.adoc[]
include::{snippets}/friends-list/response-fields.adoc[]
완성
build.gradle에 추가
plugins {
id 'com.epages.restdocs-api-spec' version '0.16.0'
}
dependencies {
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.16.2'
}
openapi3 {
server = 'http://localhost:8080'
title = 'Board API'
description = 'Board API description'
version = '0.1.0'
format = 'json'
outputDirectory = "./"
outputFileNamePrefix = 'swagger-board-api-spec'
}
RestDocumentationRequestBuilders
, MockMvcRestDocumentationWrapper
를 사용했다면 문제없이 사용 가능하다
터미널에서 gradle openapi3