Spring Rest Docs 적용하기
Spring Rest Docs는 우아한 형제들 기술블로그를 보고 적용해보고 싶은 마음이 들었다.
적용하기 위해서 아래 글들을 참고했다.
0. 전체 흐름
- Member CRUD 코드 작성
- test 코드 작성
build.gradle
내용 추가- snippet 파일 생성
- adoc 파일생성 및 snippet 파일 import
- html 파일로 변환
- 최종화면
※ 전체 코드는 git 참고
1. Member CRUD 코드 작성
spring rest docs 을 적용하기 위한 코드가 필요하다. 간단하게 member에 대한 CRUD 를 작성하였다.
참고
테스트 환경에서는 H2를 데이터베이스로 사용한다.
(개발 및 배포 환경에서는 docker compose를 통해 mysql을 사용한다.)
2. test 코드 작성
UserControllerTest.java
@ActiveProfiles("test")
@AutoConfigureRestDocs // Spring REST Docs 자동 설정 어노테이션 (테스트 수행 시, 자동으로 sinppets 파일을 생성)
@AutoConfigureMockMvc
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class UserControllerTest {
/*
* 1. member 조회
* 2. member 추가
* 3. member 수정
* 4. member 삭제
* 5. member 전체 조회
* */
@Autowired
private MockMvc mockMvc;
@Autowired
private MemberService memberService;
@BeforeAll
void init() {
CreateRequest createRequest = new CreateRequest();
createRequest.setName("john");
createRequest.setYear(2);
createRequest.setGrade("junior");
memberService.createMember(createRequest);
}
@Order(1)
@Test
void getMemberTest() throws Exception {
mockMvc.perform(get("/member/{name}", "john"))
.andDo(print())
.andDo(document("getMember"))
.andExpect(jsonPath("$.name", is("john")));
}
@Order(2)
@Test
void createMemberTest() throws Exception {
mockMvc.perform(post("/member")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.content("{\n" +
" \"name\":\"jessi\",\n" +
" \"grade\":\"junior\",\n" +
" \"year\":2\n" +
"}"))
.andDo(print())
.andDo(document("createMember"))
.andExpect(status().isOk());
}
@Order(3)
@Test
void updateMemberTest() throws Exception {
UpdateRequest request = new UpdateRequest();
request.setGrade("senior");
request.setYear(7);
mockMvc.perform(put("/member/{name}", "john")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.content(new ObjectMapper().writeValueAsString(request)))
.andDo(print())
.andDo(document("updateMember"))
.andExpect(jsonPath("$.year", is(7)))
.andExpect(jsonPath("$.grade", is("senior")));
}
@Order(5)
@Test
void deleteMemberTest() throws Exception {
mockMvc.perform(delete("/member/{name}", "john"))
.andDo(print())
.andDo(document("deleteMember"))
.andExpect(jsonPath("$.name", is("john")))
.andExpect(status().isOk());
}
@Order(4)
@Test
void getAllMemberTest() throws Exception {
mockMvc.perform(get("/members"))
.andDo(print())
.andDo(document("getAllMember"))
.andExpect(status().isOk());
}
}
3. build.gradle
내용 추가
✅ 부분을 잘 확인하자!
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.8'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'org.asciidoctor.jvm.convert' version '3.3.2' // ✅ asciidoctor plugin 추가
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext{
snippetDir = file('build/generated-snippets') // ✅ snippets 파일을 저장할 디렉토리 생성
}
test {
useJUnitPlatform()
outputs.dir snippetDir // ✅ 생성된 파일을 미리 생성한 디렉토리(build/generated-snippets)에 저장
}
asciidoctor{ // asciidoctor 설정 task
inputs.dir snippetDir // ✅ snippets 디렉토리를 입력으로 함
dependsOn test // ✅ test task를 의존하도록 하여, 문서 생성 전에 test를 수행하도록 함
}
bootJar{ // springboot를 이용한 jar 파일 생성 시 필요한 설정 task
dependsOn asciidoctor // ✅ asciidoctor 를 의존하도록 하여, bootJar 생성 전에 asciidoctor task를 수행하도록 함
// (jar 파일 생성 시, 문서 생성을 보장 함)
from("src/docs/asciidoc"){ // ✅ 문서 생성 시, Jar 파일 내 static/docs 에도 복사되도록 함 // 🌟 경로 조심!
into 'BOOT-INF/classes/static/docs'
}
// ✅ index 페이지를 노출하기 위해 설정
copy {
from asciidoctor.outputDir
into "src/main/resources/static/docs"
}
}
dependencies {
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.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// spring rest docs 을 이용하기 위한 라이브러리
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // ✅
// mockmvc
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // ✅
// H2
implementation 'com.h2database:h2:1.4.199' // ✅ 테스트를 위한 데이터베이스로 사용
}
4. snippet 파일 생성
테스트 코드 빌드 후, 테스트가 통과되었다면 snippet 파일이 자동으로 생성될 것이다.
테스트 코드 안에서 실행 버튼을 클릭하고 확인해보자.
5. adoc 파일생성 및 snippet 파일 import
Rest API 문서의 메인 파일을 생성한다.
src/docs/asciidoc
디렉토리 생성 후, index.adoc
생성
src/docs/asciidoc/index.adoc
= REST Docs
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:
ifndef::snippets[]
:snippets: ./build/generated-snippets
endif::[]
[[Member_API]]
== Member API
[[Member_생성]]
=== Member 생성
include::{snippets}/createMember/http-request.adoc[]
include::{snippets}/createMember/http-response.adoc[]
[[Member_조회]]
=== Member 조회
include::{snippets}/getMember/http-request.adoc[]
include::{snippets}/getMember/http-response.adoc[]
[[Member_수정]]
=== Member 수정
include::{snippets}/updateMember/http-request.adoc[]
include::{snippets}/updateMember/http-response.adoc[]
[[Member_삭제]]
=== Member 삭제
include::{snippets}/deleteMember/http-request.adoc[]
include::{snippets}/deleteMember/http-response.adoc[]
[[Member_전체_조회]]
=== Member 전체 조회
include::{snippets}/getAllMember/http-request.adoc[]
include::{snippets}/getAllMember/http-response.adoc[]
include::{snippets}
를 이용하여 생성한 snippets 파일을 추가하였다.
gradle bootJar 실행
$ ./gradlew clean
$ ./gradlew build jar
jar 파일 내부에 생성된 index.html 확인
bootJar 실행 후 자동으로 생성된다.
# jar 파일 내부 리스트 보기
$ jar -tf ./build/libs/jenkins-test-0.0.1-SNAPSHOT.jar
# jar 파일 내부 index.adoc 검색
$ jar -tf ./build/libs/jenkins-test-0.0.1-SNAPSHOT.jar | grep index.adoc
6. html 파일로 변환
bootJar 파일에 index.html 문서를 추가하기 위한 build.gradle 설정을 한다.
(아래 부분인데, 이전에 미리 작성 해두었으니 넘어가자!)
plugins {
...
id 'org.asciidoctor.jvm.convert' version '3.3.2' //asciidoctor plugin 추가
}
...
asciidoctor{ //asciidoctor 설정 task
inputs.dir snippetDir //snippets 디렉토리를 입력으로 함
dependsOn test // test task를 의존하도록 하여, 문서 생성 전에 test를 수행하도록 함
}
bootJar{ //springboot를 이용한 jar 파일 생성 시 필요한 설정 task
dependsOn asciidoctor //asciidoctor 를 의존하도록 하여, bootJar 생성 전에 asciidoctor task를 수행하도록 함
// (jar 파일 생성 시, 문서 생성을 보장 함)
from("src/docs/asciidoc"){ //문서 생성 시, Jar 파일 내 static/docs 에도 복사되도록 함
into 'BOOT-INF/classes/static/docs'
}
}
...
7. 최종 화면
bootJar 실행후, localhost:8080/docs/index.html
에 들어갔을 때 화면이다.
모든 설정이 잘 되었는지 확인
모든 설정이 잘 되었다면 아래 파일들이 자동으로 생성되어 있어야 한다.
💛 개인 공부 기록용 블로그입니다. 👻