실행 환경 Spring Rest Docs Gradle 설정적용장점 단점 Swaager 알아보기 전에 알아야 할 개념 Swaager 종류 뭘 써야할까? Swagger 적용 의존성 추가 application.yml 파일 설정 Config 클래스 추가 Authorization Header는? 작성 Request, Response 객체 설정 장점 단점 RestDocs + Swagger-UI우리들의 선택은? Reference
실행 환경
- java :
11
- gradle :
7.4.1
Spring Rest Docs
- 먼저 전 프로젝트에서 사용했던 Rest Docs를 보겠습니다.
Gradle 설정
plugins { id 'org.springframework.boot' version '2.7.1' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'org.asciidoctor.jvm.convert' version '3.3.2'. // (1) id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { asciidoctorExtensions // (2) } repositories { mavenCentral() } ext { set('snippetsDir', file("build/generated-snippets")) // (3) } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor' // (4) testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' // (5) } tasks.named('test') { outputs.dir snippetsDir useJUnitPlatform() } tasks.named('asciidoctor') { // (6) configurations 'asciidoctorExtensions' sources{ include("**/index.adoc") } baseDirFollowsSourceFile() inputs.dir snippetsDir dependsOn test } asciidoctor.doFirst { //(7) delete file('src/main/resources/static/docs') } task createDocument(type: Copy) { // (8) dependsOn asciidoctor from file("build/docs/asciidoc") into file("src/main/resources/static") } bootJar { // (9) dependsOn createDocument }
- (1) : gradle 7버전부터는
org.asciidoctor.jvm.convert
을 사용해야 합니다.
- (2) :
asciidoctorExtensions
을 설정해줍니다.
- (4), (5) :
asciidoctorExtensions
과 docs 생성을 위한spring-restdocs-mockmvc
의존성을 추가해줍니다.
- (6) : dependsOn으로 test task가 실행된 이후 실행되는 task입니다.
- sources에 명시한 adoc만 html로 변환하게 해줍니다.
baseDirFollowsSourceFile
의 경우 docs/asciidoctor/domain과 같이 폴더 구조로 adoc을 만들었을 때 index.adoc에서 include했을 경우 path를 찾지 못하기 때문에 설정해줘야 합니다.
- (7) : 문서 최신화를 위해 앞전에 만들었던 docs파일을 삭제합니다.
doFirst
로 인해 asciidoctor가 실행되기 전에 실행하게 됩니다.
- (8) : 완성된 html을 static 폴더에 넣어 서버를 띄우게 되면 볼 수 있도록 합니다.
- (9) : bootJar가 실행되기 전에 createDocument task를 실행하게 됩니다.
- 이를 통해 build시에 docs파일이 jar파일에 들어갈 수 있습니다.
적용
@RestController public class TestController { @GetMapping("/users") public ResponseEntity<TestResponse> get() { return ResponseEntity.ok(new TestResponse("test")); }
@SpringBootTest @AutoConfigureMockMvc @AutoConfigureRestDocs class TestControllerTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @DisplayName("레스트 독스 테스트") @Test void TestControllerTest() throws Exception { // given // when mockMvc.perform(get("/users") .contentType(MediaType.APPLICATION_JSON_VALUE)) .andExpectAll( status().isOk(), jsonPath("name").value("test") ) // then .andDo(document("test", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()), responseFields( fieldWithPath("name").type(JsonFieldType.STRING).description("테스트") ))); } }
- 테스트를 통해 스니펫을 생성해줍니다.
- adoc 파일을 생성합니다 (
src/docs/asciidoc
)
ifndef::snippets[] :snippets: ../../../build/generated-snippets endif::[] :doctype: book :icons: font :source-highlighter: highlightjs :toc: left :toclevels: 3 :sectlinks: :docinfo: shared-head == 예약 게스트 API === 예약 생성 ==== Request ===== Request HTTP Example include::{snippets}/test/http-request.adoc[] ==== Response include::{snippets}/test/response-fields.adoc[] ===== Response HTTP Example include::{snippets}/test/http-response.adoc[]
- 이후 빌드를 해보면 자동적으로 html 파일이
src/main/resources/static
으로 들어가게 됩니다.

- 이후 서버를 실행하게 되면 아래와 같이 docs파일이 생성됩니다.

장점
- 테스트 기반으로 동작하기 때문에 프로덕션 코드에는 영향이 없습니다.
- 테스트 코드가 강제되므로 문서의 신뢰성이 높습니다.
- 문서가 매우 직관적입니다.
단점
- 엔드 포인트마다 수많은 코드를 작성해야합니다.
- 테스트가 없다면 프론트에게 넘겨줄 문서가 없습니다.
- Swagger와는 다르게 테스트를 즉석으로 할 수 없습니다.
Swaager
알아보기 전에 알아야 할 개념
- Open API : 개방된 API라는 뜻으로 누구나 사용될 수 있도록 API의 endpoint가 개방되어 있습니다.
- OpenAPI : RESTful API를 기 정의된 규칙에 맞게 API spec을 json이나 yaml로 표현하는 방식을 의미합니다.
- 직접 소스코드나 문서를 보지 않고 서비스를 이해할 수 있다는 장점이 있습니다.
- RESTful API 디자인에 대한 정의 표준이라고 생각하면 됩니다.
- Swaager는 OpenAPI를 구현하기 위한 도구입니다.
Swaager 종류


Springfox Swaager
- 2018년까지 Springfox Swaager는 많은 사용자들이 사용했지만 2018년 6월을 마지막으로 업데이트가 중단되었습니다. ( 2년만에 업데이트가 되긴 했습니다. )
Springdoc


- Springfox Swaager의 업데이트가 중단한 사이 Springadoc이 나오게 되었습니다.
- Springfox보다 좀 더 쉽게 사용할 수 있게 만들었다고 합니다.
뭘 써야할까?

- Springdoc이 꾸준하게 업데이트를 하고 있다는 점에서 springadoc를 사용하는 것이 좋을 것 같습니다.
- Swaager-ui를 포함하는 형태로 가지고 있습니다.
Swagger 적용
의존성 추가
implementation 'org.springdoc:springdoc-openapi-ui:1.6.9'
application.yml 파일 설정
springdoc: version: '@project.version@' swagger-ui: path: /api-docs operations-sorter: method tags-sorter: alpha api-docs: path: /api-docs/json
- api-docs.path
- 기본값 : /v3/api-docs
- api를 OpenAPI3을 이용하여 json 형식화 한 것의 경로
- sorter : method : 메서드순 , alpha: 알파벳 오름차순
- swagger-ui.path
- 기본값 : swagger-ui.html
- 이외에도 여러 값 지정가능
Config 클래스 추가
package com.example.demo; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; @Configuration public class OpenApiConfig { @Bean public OpenAPI openAPI(@Value("${springdoc.version}") String appVersion) { Info info = new Info() .title("만두만두만두만두") .version(appVersion) .description("이것은 만두 API입니다."); return new OpenAPI() .components(new Components()) .info(info); } }


Authorization Header는?
@Configuration class OpenApiConfig { @Bean fun customOpenAPI(): OpenAPI? { return OpenAPI().components( Components().addSecuritySchemes( "bearer-key", SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT") ) ) } }
작성
@Tag(name = "유저", description = "유저 관련 API입니다.") @RestController public class TestController { @Operation(summary = "유저 다건 조회", description = "유저 단건 조회 API입니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = @Content(schema = @Schema(implementation = TestResponse.class))), @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(schema = @Schema(implementation = TestResponse.class))) }) @GetMapping("/users") public ResponseEntity<TestResponse> get() { return ResponseEntity.ok(new TestResponse("test")); } @Operation(summary = "유저 가입", description = "유저 가입 API입니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "성공", content = @Content(schema = @Schema(implementation = TestResponse.class))), @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(schema = @Schema(implementation = TestResponse.class))) }) @PostMapping("/users") public ResponseEntity<TestResponse> post( @RequestBody TestRequest testRequest ) { TestResponse response = new TestResponse("1"); URI location = ServletUriComponentsBuilder.fromCurrentRequest() .path("{id}") .buildAndExpand(response.getName()) .toUri(); return ResponseEntity.created(location) .body(response); } }
- @Tag
- api 그룹 설정을 위한 어노테이션
- name이 같은 것끼리 하나의 api그룹으로 묶게 됩니다.
- @Operation
- api 상세 정보 설정을 위한 어노테이션
- summary, description, responses, parameters 속성 등 추가 가능
- @ApiResponses
- 여러개의 @ApiResponse를 묶기 위한 어노테이션
- @ApiResponse
- api의 response 설정을 위한 어노테이션
- responseCode, description, implementation( responsebody로 제공될 클래스 타입만 설정 가능 )
- @Parameter
- api parameter 설정을 위한 어노테이션
- name, description, in( 파라미터 위치)



Request, Response 객체 설정
public class TestRequest { @Schema(description = "이름", defaultValue = "testName") private String name; } public class TestResponse { @Schema(description = "반환 이름", defaultValue = "testName") private String name; }
- @Schema
- Request, Resposne 객체에 대한 설정을 위한 어노테이션
- description, defaultValue, example

이외에도 세세히 설정할 수 있습니다.
장점
- API문서에서 API테스트를 즉시 해볼 수 있습니다.
- 문서가 이쁩니다.
단점
- 테스트 코드 없이도 생성할 수 있어서, API 문서를 신뢰하기 어렵습니다.
- 프로덕션 코드에 영향이 있습니다.
RestDocs + Swagger-UI
- Swagger는 프로덕션 코드에 덕지덕지 붙고 Swagger 코드를 작성해야 한다는 단점이 있고 Rest Docs는 문서 작성을 위한 코드가 테스트 코드에 있기 때문에 테스크 코드가 강제되며 문서를 통해 테스트가 불가능하다.
- 두 개의 장점만 가진 방법은 없을까?
restdocs-api-spec
ePages-de • Updated Aug 18, 2022