레넌의 개발 일기
REST Docs 적용기 본문
Spring REST Docs
Spring REST Docs는 테스트 코드 기반으로 RESTful 문서생성을 돕는 도구로 기본적으로 Asciidoctor를 사용하여 HTML를 생성한다. Spring MVC 테스트 프레임워크로 생성된 snippet을 사용해서 snippet이 올바르지 않으면 생성된 테스트가 실패하여 정확성을 보장해준다.
왜 REST Docs인가?
인수테스트가 있음에도 컨트롤러 테스트를 작성해야하는 불편함이 있지만, 프로덕션 코드에 의존적이지 않다는 점에서 REST Docs가 더 좋다고 생각한다. 또한, Swagger는 API 동작을 테스트하는 데에 더 특화되어 있다고한다. API 문서화를 통해 스펙을 정의하는 것이 목적이라면, 깔끔하고 명료하게 문서화할 수 있는 REST Docs가 더 좋다고 생각한다.
build.gradle
plugins {
id 'org.asciidoctor.jvm.convert' version "3.3.2"
}
configurations {
asciidoctorExtensions
}
dependencies {
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
}
// snippets 파일이 저장될 경로 설정
ext {
snippetsDir = file('build/generated-snippets')
}
// test 실행 시 snippets 저장할 경로로 snippetsDir 설정
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
// API 문서 생성
asciidoctor {
configurations 'asciidoctorExtensions'
inputs.dir snippetsDir
dependsOn test
}
bootJar {
dependsOn asciidoctor
from("${asciidoctor.outputDir}/asciidoc") {
into 'static/docs'
}
}
task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc/")
into file("src/main/resources/static/docs")
}
build {
dependsOn copyDocument
}
- ext : 전역 변수 세팅
- asciidoctor : testDocument를 의존하여 테스트를 실행하고, snippetsDir에서 snippets을 참조하여
문서를 생성한다. - bootJar : jar 빌드 시 asciidoctor를 참조하여 문서를 생성하고, snippetsDir에 있는 html파일을
static/docs로 복사한다. - task : gradle을 통해 실행되는 단위
- copyDocument : from 디렉토리에 있는 API 문서 파일을 into로 복사한다.
복사하는 이유는 api 요청으로 문서 접근을 위함. 테스트 시에는 이걸 쓰고, 배포 시에는 bootjar를 씀.
ArticleControllerTest
@DisplayName("질문 게시판 문서화")
@AutoConfigureRestDocs
@WebMvcTest(ArticleController.class)
@Import(RestDocsConfig.class)
class ArticleControllerTest {
@MockBean
private ArticleService articleService;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private MockMvc mockMvc;
@Test
void 질문_게시물_생성_API_문서화() throws Exception {
ArticleIdResponse response = new ArticleIdResponse(1L);
ArticleRequest request = new ArticleRequest("title", "content", "question");
given(articleService.save(any(), any())).willReturn(response);
ResultActions results = mockMvc.perform(post("/api/articles")
.header(HttpHeaders.AUTHORIZATION, "Bearer token")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.characterEncoding("UTF-8"));
results.andExpect(status().isCreated())
.andDo(print())
.andDo(document("article-create",
requestHeaders(
headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer + 토큰")
),
requestFields(
fieldWithPath("title").type(JsonFieldType.STRING).description("제목"),
fieldWithPath("content").type(JsonFieldType.STRING).description("내용"),
fieldWithPath("category").type(JsonFieldType.STRING).description("카테고리")
),
responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("식별자")
)
));
}
}
ASCIIDOC 적용
Maven을 쓰는지 Gradle을 쓰는지에 따라, 소스파일의 위치와 생성 위치가 달라진다.
article.adoc
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
= Article
---
= 기본 API
== 게시물 생성
`POST /api/articles`
=== Request
include::{snippets}/article-create/http-request.adoc[]
==== Header
include::{snippets}/article-create/request-headers.adoc[]
==== Body
include::{snippets}/article-create/request-fields.adoc[]
=== Response
include::{snippets}/article-create/http-response.adoc[]
include::{snippets}/article-create/response-fields.adoc[]
결과
참고
https://techblog.woowahan.com/2597/
https://velog.io/@monkeydugi/Spring-Rest-Docs-%EC%B1%84%ED%83%9D-%EC%9D%B4%EC%9C%A0
'Spring' 카테고리의 다른 글
Logback을 통한 Logging (0) | 2022.08.29 |
---|