Spring REST Docs 와 Swagger
API 문서
우리 회사는 보통 word 로 API 문서를 만들었다. 수정사항 업데이트 하기가 매우 귀찮았고 까먹을 때가 많았다.
Spring REST Docs
- RESTful (자원을 이름으로 구분하여 해당 자원의 상태를 주고받는 모든 것 ) 서비스에 대한 문서 생성을 도와준다.
- 테스트를 통해 API 문서를 만듬
- HTML, PDF 문서로 생성가능 Hosting 도 가능
- 변경에 따른 최신화 보장
Swagger
- 문서에 대한 명세 보다는 API를 쉽게 호출해 볼수 있는 것에 초점
- 테스트 코드가 없어도 문서화 가능 (Controller에 Annotation만 붙이면됨)
- API 테스트 UI 제공
OpenAPI Spec
- Rest API 에 대한 설명, 생성, 사용 및 시각화 하기 위한 인터페이스 파일 사용
- YAML 로 작성하면 이를 Swagger HTML로 바꿔주는 Spec
사용법
아래 Entity를 이용해서 Controller에 CURD를 작성했다.
@Entity @Data @AllArgsConstructor @NoArgsConstructor @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String account; private String email; private String phoneNumber; private LocalDateTime createdAt; private LocalDateTime updatedAt; }
build.gradle
아래 공식 문서를 참조 한다. 그런데 나의 환경에서는 마지막 단계인 통합 Document 구성이 안되었다….. 글 마지막에 troubleshoot 방법으로 해결 했다.
Test Code
아래 위치에 java 파일을 생성 한다.
@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) @SpringBootTest public class UserApiDoc { //org.springframework.test.web.servlet private MockMvc mockMvc; @BeforeEach public void setup(WebApplicationContext context, RestDocumentationContextProvider restDocumentation){ this.mockMvc = MockMvcBuilders.webAppContextSetup(context) .apply(documentationConfiguration(restDocumentation)) // documentationConfiguration 가 resDocumentation 의 인스턴스를 얻게 해줌 .build(); System.out.println("before ~~~~~~~~~"); } @Test //org.springframework.restdocs.request.RequestDocumentation //org.springframework.restdocs.payload.PayloadDocumentation public void testRead() throws Exception{ this.mockMvc.perform(get("/api/user/{id}", 1L)) .andDo(print()) .andDo(document("user", pathParameters( parameterWithName("id").description("user id") ), responseFields( fieldWithPath("resultCode").description("response code"), fieldWithPath("data.id").description("id"), fieldWithPath("data.account").description("account"), fieldWithPath("data.email").description("email"), fieldWithPath("data.phoneNumber").description("phone number"), fieldWithPath("data.createdAt").description("create"), fieldWithPath("data.updatedAt").description("modify") ) )); } }
JUnit5 기반으로 작성. 스프링 부트 기반 테스트를 위해 @SpringBootTest(어플을 띄움)를 Class에 선언해 주고 테스트 수행전에 mockMvc를 설정해준다. 그리고 테스트에서 API 전송후 그 Result를 저장한다.
성공하면 build/generated-snippets/{user}/*.adoc 형태로 저장 된다. 다음으로 asciidoc 파일을 만들어 발생한 asciidoc을 html로 변환 한다.
user.adoc
:snippets: build/generated-snippets = RESTful Notes API Guide :doctype: user :icons: font :source-highLighter: highlightjs :toc: left :toclevels: 4 :sectnums: :sectlinks: :sectanchors: [[api]] == User Api include::{snippets}/user/curl-request.adoc[] include::{snippets}/user/http-request.adoc[] include::{snippets}/user/path-parameters.adoc[] include::{snippets}/user/http-response.adoc[] include::{snippets}/user/response-fields.adoc[]그
이렇게 한 뒤 Build를 하게 되면 build/docs/asciidoc 에 user.html 파일이 생성된다. 이를 was 에서 접근할 수 있도록 resource 폴더에 옮기려면 아래 처럼 build.gradle 파일을 수정 한다.
bootJar { dependsOn asciidoctor // from ("build/docs/asciidoc") { // into 'static/docs' // } copy { from "${asciidoctor.outputDir}" into "src/main/resources/static/docs/" } }
한번 더 build를 하면 src/main/resources/static/docs/user.html 파일이 생성된 것을 알 수 있고 localhost:8080/docs/user.html로 접근시 화면을 볼 수 있다.
restdocs-api-spec
Swagger에서 돌릴 수 OpenAPI Spec 으로 구성된 yaml 파일을 자동으로 생성해주는 Open Source
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.1.md#oasDocument
build.gradle
plugins { ... id 'com.epages.restdocs-api-spec' version '0.11.4' } dependencies { ... testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.11.4' //2.2 } // open api 3 spec 사용시 openapi3 { server = 'http://localhost:8080' title = 'User API' description = 'User Api Description' // tagDescriptionsPropertiesFile = 'src/docs/tag-descriptions.yaml' version = '0.1.0' format = 'yaml' copy { from "build/api-spec" into "src/main/resources/static/docs/" } } //from : https://github.com/ePages-de/restdocs-api-spec#gradle
Test Code
//import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; //import static com.epages.restdocs.apispec.ResourceDocumentation.resource; //import com.epages.restdocs.apispec.ResourceSnippetParameters; public void testRead() throws Exception{ this.mockMvc.perform(get("/api/user/{id}", 1)) .andDo(print()) .andDo(document("user", pathParameters( parameterWithName("id").description("user id") ), responseFields( fieldWithPath("resultCode").description("response code"), fieldWithPath("data.id").description("id"), fieldWithPath("data.account").description("account"), fieldWithPath("data.email").description("email"), fieldWithPath("data.phoneNumber").description("phone number"), fieldWithPath("data.createdAt").description("create"), fieldWithPath("data.updatedAt").description("modify 시간") ) )); this.mockMvc.perform(get("/api/user/{id}", 1)) .andDo(document("user", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), resource( ResourceSnippetParameters.builder().description("User Information CURD").summary("User Information") .pathParameters( parameterWithName("id").description("user id") ) .responseFields( fieldWithPath("resultCode").description("response code"), fieldWithPath("data.id").description("id"), fieldWithPath("data.account").description("account"), fieldWithPath("data.email").description("email"), fieldWithPath("data.phoneNumber").description("phone number"), fieldWithPath("data.createdAt").description("create"), fieldWithPath("data.updatedAt").description("modify 시간") ).build() ) )); }
테스트 코드 실행 후 gradle openapi3 하게 되면 yaml 파일이 만들어 진다.
Swagger를 Docker에 올려 해당 yaml 파일을 호스팅 하고 싶다면 아래 명령으로 docker를 실행 시킨다.
docker run -p 80:8080 -e URLS="[{url: 'http://localhost:8080/docs/openapi3.yaml', name: 'user'}, {url: 'https://petstore.swagger.io/v2/swagger.json', name: 'sample'}]" -v /tmp/swagger:/usr/share/nginx/docs/ swaggerapi/swagger-ui
Application 에서 CORS 헤더를 주기 위해 WebConfig 라는 java 파일을 만들고 아래와 같이 코딩한다.
package org.yg.practice.flyway.configs; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer{ @Override public void addCorsMappings(CorsRegistry registry){ System.out.println("add mapping ~!"); registry.addMapping("/**"); } }
그 후 localhost로 접속 시 Swagger UI를 볼 수 있다.
참고
fast campus
Trouble Shoot
Spring Rest Docs 공식 사이트 가이드 대로 Gradle 세팅을 진행 했는데 asciidoctor 에서 에러가 발생하면서 build가 되지 않았다. 그래서 아래 URL을 참고하여 문제를 해결 했다.
https://github.com/spring-projects/spring-restdocs/issues/680
build.gradle
plugins { ... //id "org.asciidoctor.convert" version "1.5.9.2" id 'org.asciidoctor.jvm.convert' version '3.2.0' } configurations { asciidoctorExt } dependencies { ... asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor' ... } asciidoctor { configurations 'asciidoctorExt' ... }
다른 부분은 공식 문서와 같게 하니 잘 동작 했다.
블로그 구독하기 !!