Spring REST Docs?
Asciidoctor를 사용해 RESTful 서비스에 대한 문서를 생성하도록 돕는다.
Swagger vs REST Docs
Swagger UI | Spring REST Docs |
실제 코드에 애노테이션을 추가하여 문서가 작성되는 방식 -> 가독성 측면에서 좋지 않을 수 있음 -> 하지만 적용하는 방법이 쉬움! |
테스트 코드로 문서를 작성하는 방식 (테스트가 성공해야만 작성됨) -> 테스트 코드를 반드시 작성해야 함.. -> 약간의 진입 장벽이 있음 |
API를 테스트 해볼 수 있음! | API 테스트 불가능 진짜 "문서" |
Swagger도 사용해봤지만, REST Docs가 더 좋다고 느꼈다.. 일단 문서 생긴게 더 마음에 들기도 하고...^^
REST Docs 적용하기!
우아한형제들 기술 블로그의 Spring Rest Docs 적용글을 참고하며 진행했습니다.
build.gradle
plugins
항목에 org.asciidoctor.convert
를 추가
plugins {
id 'org.springframework.boot' version '2.4.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'org.asciidoctor.convert' version '1.5.6' // asciidoc파일을 변환해주고, Build폴더에 복사해주는 플러그인
}
repositories
항목 위에 아래의 내용을 작성!
asciidoctor {
dependsOn test // gradle build 시 test -> asciidoctor 순으로 수행
}
bootJar {
dependsOn asciidoctor // gradle build 시 asciidoctor 순으로 수행
from("${asciidoctor.outputDir}/html5") { // gradle build 시 ./build/asciidoc/html5/ 에 html 파일생성
into 'static/docs' // html 파일이 jar 안의 /static/docs/ 폴더로 복사됨
}
}
dependencies
항목에 spring-restdocs-mockmvc
를 추가
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
테스트 작성 (JUnit5 기준)
보통은 @WebMvcTest
로 컨트롤러 레이어만 테스트하는 환경에서 REST Docs 작성을 하게된다.
- JUnit5의 경우
@AutoConfigureRestDocs
설정이 안 먹힌다.. @ExtendWith
에RestDocumentataionExtension.class
를 설정@WebMvcTest
에는 테스트할(문서를 만들고자 하는) 컨트롤러를 설정
RestDocumentationExtention
이 자동적으로 빌드 툴에 맞는 output 디렉터리를 설정한다.Gradle
의 경우 build/generated-snippets
경로에 스니펫들이 생성됨
@ExtendWith(RestDocumentationExtension.class) // JUnit5 필수
@WebMvcTest(ImageController.class)
public class ImageControllerRestDocsTest {
}
그리고 @BeforeEach
메서드로 MockMvc
설정을 해준다.
@Autowired
private MockMvc mockMvc;
@BeforeEach
void setup(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(springSecurity()) // springSecurity설정이 되어있지 않으면 생략
.apply(documentationConfiguration(restDocumentation)).build();
}
테스트 메서드 작성하기
- 테스트가 성공해야 스니펫이 생성된다.
@MockBean
으로 주입된 서비스에 대해 적절한 처리를 해준다.. ->given().willReturn()
- 요청, 응답에 대한 문서에 적힐 설명들을 작성
requestParameters
는 queryString으로 요청하는 값에 대해..responseFields
는 응답 값에 대해.. 형식에 맞게 적어주는 것이 중요 -> 공식문서 참고optional()
을 붙여주면 필수 값이 아니라는 뜻 붙이지 않으면 테스트 실패하니 주의
@Test
@WithMockUser(...) // springSecurity 설정 없으면 생략
void getImages() throws Exception {
List<HashMap<String, Object>> response = new ArrayList<>();
HashMap<String, Object> data = new HashMap<>();
data.put("name", "foo");
data.put("img_url", "https://~~~");
data.put("reg_date", "2021-06-03 10:00:00");
response.add(data);
given(imageService.findAllById(any(ParamDto.class))).willReturn(response);
ResultActions perform = this.mockMvc.perform(
RestDocumentationRequestBuilders.get("/images")
);
perform.andExpect(status().isOk())
.andDo(print())
.andDo(document("images-get", // 설정한 값으로 스니펫 폴더가 생성됨
requestParameters(
parameterWithName("user").description("유저 이름").optional()
),
responseFields(
fieldWithPath("imgList").description("이미지 리스트"),
fieldWithPath("imgList[].name").description("이미지 등록 회원 이름"),
fieldWithPath("imgList[].img_url").description("이미지 url"),
fieldWithPath("imgList[].reg_date").description("이미지 등록일시")
})
);
}
그리고 테스트에 성공하면 build/generated-snippets
경로에 .adoc
파일들이 생성된다.
생성된 스니펫들을 사용하자!
.adoc
파일을 생성해야 하는데, Gradle
의 경우 src/docs/asciidoc/*.adoc
경로로 생성해야 한다.
파일명은 자유롭게 설정하면 된다. .adoc
으로 생성하기만 하면 OK
그리고 스니펫들을 사용하는.. 진짜 문서로서의 기능을 하도록 조합하면 끝!
=
,==
... 들은 마크다운에서의#
,##
과 비슷하게 사용.. 제목이 된다:toc: left
설정은 왼쪽에 사이드바가 생성되고, 목차들에 링크가 자동으로 걸린다!!:toclevels: 2
설정은 목차들의 레벨을 어디까지 보여줄 것이냐..
= REST API
:toc: left
:toclevels: 2
:source-highlighter: highlightjs
== 1. 개요
== 2. 인증
== 3. 이미지
이제 생성된 스니펫들을 진짜로 포함해보자!includ::{snippets}/../~.adoc[]
으로 사용할 스니펫을 선택해서 불러오면 된다!
인텔리제이를 사용한다면 플러그인을 다운받아 작성하시길... 문법이 바로바로 이해가 쏙쏙...
== 3. 이미지
=== 3-1. 이미지 조회
===== Request
include::{snippets}/images-get/request-parameters.adoc[]
===== Request Example
include::{snippets}/images-get/http-request.adoc[]
===== Response
include::{snippets}/images-get/response-fields.adoc[]
===== Response Example
include::{snippets}/images-get/response-body.adoc[]
그럼 이렇게 자동으로 문서가 생성된다... 👏 (아직 미리보기 중..)
Gradle 버전에 맞게 수정하기!
현재 Gradle 7.0.2 버전을 사용중인데, 공식문서나 위에 설정한대로 실행하면 제대로 html파일이 생성되지 않는다...
아래와 같이 build.gradle
을 수정한다.
build를 해보면 알 수 있겠지만 compileJava > test > asciidoctor > bootJar 순으로 실행된다.
만약에 테스트도 통과하고 순서대로 다 실행되고 빌드 성공까지 떴는데 html파일이 생성되지 않으면.. 아래처럼 수정해보자...
plugins {
id 'org.springframework.boot' version '2.4.5'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'org.asciidoctor.jvm.convert' version '3.3.2' // jvm.convert를 사용해야 한다!
}
group = '...'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
// 여기 추가!
configurations {
asciidoctorExtensions
}
repositories {
mavenCentral()
}
// 이 부분도 추가
ext {
snippetsDir = file('build/generated-snippets')
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
// 이 부분이 추가됨!
asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
outputs.dir snippetsDir
useJUnitPlatform()
}
asciidoctor {
configurations 'asciidoctorExtensions'
inputs.dir snippetsDir
dependsOn test
}
bootJar {
dependsOn asciidoctor
copy {
from "${asciidoctor.outputDir}" // /html5를 사용하지 않는다!
into 'src/main/resources/static/docs' // /static/docs로 복사!
}
}
그리고 나서 build
해주면
./gradlew build
이렇게 /build/docs/asciidoc/index.html
이 생성된 것을 확인할 수 있다.
이 index.html
은 bootJar
가 실행되면서 /statics/docs
로 복사된다.
이렇게 복사된 것 까지 확인했다면 성공!jar
를 실행한 뒤에 http://localhost:8080/docs/index.html
로 접속하면 REST Docs가 잘 뜬다!!!!!
(중간에 api-doc.adoc을 index.adoc으로 변경했음. 본인이 지정한 파일명으로 접속하면 됨)
+ 참고
'STUDY > Spring' 카테고리의 다른 글
Spring Boot | spring-session-jdbc 테이블 생성이 안 될 때.. (0) | 2021.06.10 |
---|---|
Spring Boot | OAuth2 Google 로그인 (0) | 2021.06.09 |
JUnit5 | multipart/formdata 전송 테스트 ( MockMultipartFile, @RequsetPart ) (2) | 2021.06.01 |
JUnit5 | Test LifeCycle (0) | 2021.06.01 |
JUnit5 | Controller 테스트 하기 ( + OAuth2 ) (0) | 2021.05.31 |