Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 오류해결
- 바인딩
- Tomcat
- WORK폴더
- dockerfile #dockerrun #빌드
- 클래스명과 파일명 동일한 이유
- sql린이#공부중
- 클래스명
- brew #mariadb #Django #Python
- 개발자포트폴리오
- Integer
- 근데왜지
- 인터페이스
- ArrayList
- JSON #javascript
- 인스턴스오류 #Tomcat #mysql
- 서터 세팅이 잘못인가
- 객체정렬 #Java8
- 자바프로그래밍
- 나중에도 같은문제면 이렇게 시도해봐야겠다
- iBATIS
- 서버이전
- 클래스
- aws #ec2 #instance #클라우드 #cloud #it
- Spring REST Docs #Swagger #JAVA #REST API
- 포트폴리오 작성
- ROLLUP#합계
- list
- Java
- Spring #Scheduler #JAVA
Archives
- Today
- Total
HoneyBee
Spring REST Docs + Swagger UI 본문
요약
- AS-IS
- 현재 API 문서 작성을 위해 Controller 위에 API 문서 작성을 위한 어노테이션 사용 중
- 정의에 대한 내용이 들어가다보니 코드가 간결하지 않고 복잡성이 더해짐.
- 현재 API 문서 작성을 위해 Controller 위에 API 문서 작성을 위한 어노테이션 사용 중
- TO-MAYBE
- controller에 어노테이션 삭제 → 코드 간결화
- Spring REST Docs 활용하여 API 문서 작성
- TEST code 실행 후 성공 시에만 API 문서 생성 가능
- 생성된 REST Docs 파일을 Swagger-UI를 통해 출력
Swagger vs Spring REST Docs
Swagger
장점
- 어노테이션 추가로 API 문서 자동 생성
- UI 를 통해 바로 테스트 가능
단점
- controller 로직이 지저분함
- 운영코드에 스웨거 애노테이션이 침투하기 시작하며 생각보다 많은 코드를 작성
- Swagger 사용이 익숙하지 않을 경우 테스트에 어려움
- ex. 필요한 변수들 넣기에 불편해서 Postman 사용하는 경우 존재
- controller 로직이 지저분함
Spring REST Docs
장점
- 테스트 코드 강제
- API 문서가 작성되려면 테스트가 완료 되어야 가능함.
- Postman 사용에 익숙한 경우 Import해서 사용 가능
- 테스트 코드 강제
단점
- UI를 통해 API 실행을 할수 없음
환경
- Java 17
- WebFlux
- gradle kotlin DSL → ex. build.gradle.kts
Process
- Spring REST Docs 적용
- restdocs-api-spec 라이브러리 사용해서 Open-api-3.0.1 문서 생성
- Swagger-ui 를 통해 문서 import
Spring REST Docs 적용
- build.gradle.kts
plugins {
java
id("org.springframework.boot") version "3.0.1"
id("io.spring.dependency-management") version "1.1.0"
id("org.springdoc.openapi-gradle-plugin") version "1.6.0"
id("org.asciidoctor.jvm.convert") version "3.3.2"
id ("com.epages.restdocs-api-spec") version "0.18.2"
}
/*
생략
*/
java.sourceCompatibility = JavaVersion.VERSION_17
val asciidoctorExt by configurations.creating
repositories {
mavenCentral()
}
dependencies {
//
*
* 필요한 요소 추가
*
//
//Test
testImplementation("io.projectreactor:reactor-test:$ioProjectreactor")
//Spring REST Docs
asciidoctorExt("org.springframework.restdocs:spring-restdocs-asciidoctor")
testImplementation("org.springframework.restdocs:spring-restdocs-webtestclient")
testImplementation("com.epages:restdocs-api-spec:0.18.2")
testImplementation("com.epages:restdocs-api-spec-webtestclient:0.18.2")
}
val snippetsDir by extra { file("build/generated-snippets") }
val staticsDir by extra { file("src/main/resources/static") }
tasks.test {
outputs.dir(snippetsDir)
}
tasks.asciidoctor {
inputs.dir(snippetsDir)
// configurations(asciidoctorExt)
// dependsOn(tasks.test)
}
tasks.withType<Test> {
useJUnitPlatform()
}
tasks.bootJar {
dependsOn(":openapi3") // OpenAPI 작성 자동화를 위해 패키징 전에 openapi3 태스크 선실행을 유발
}
//build.gradle.kts 사용하기 위함
openapi3 {
setServer("http://localhost:10078")
title = "Your title"
description = "Your description"
version = "0.1.0"
format = "json"
outputFileNamePrefix = "open-api-3.0.1"
outputDirectory = "$staticsDir/docs"
}
- ReadControllerTest.java
package /* 패키지 경로 */;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
import com.epages.restdocs.apispec.WebTestClientRestDocumentationWrapper;
import com.maestro.user.workspace.read.adapter.in.ReadWorkspaceController;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.restdocs.headers.HeaderDocumentation;
import org.springframework.restdocs.payload.PayloadDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
@WebFluxTest(controllers = ReadController.class)
@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureWebTestClient
public class ReadControllerTest {
private WebTestClient webTestClient;
private String userId = "필요한 필드";
@BeforeEach
void setUp(ApplicationContext applicationContext, RestDocumentationContextProvider restDocumentation) {
this.webTestClient = WebTestClient.bindToApplicationContext(applicationContext).configureClient()
.filter(documentationConfiguration(restDocumentation))
.build();
}
@Test
void findAccessibleWorkspacesForSwagger() {
webTestClient
.get()
.uri("{테스트 할 API 경로}")
.accept(MediaType.APPLICATION_JSON)
.header("userId", userId)
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(WebTestClientRestDocumentationWrapper
.document("accessible",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
HeaderDocumentation.responseHeaders(
headerWithName(HttpHeaders.CONTENT_TYPE)
.description(MediaType.APPLICATION_JSON_UTF8_VALUE)
),
requestHeaders(
headerWithName("userId").description("The ID of the user making the request")
),
responseFields(
PayloadDocumentation.fieldWithPath("returnCode").description("The return code of the response"),
PayloadDocumentation.fieldWithPath("returnMessage").description("The return message of the response"),
PayloadDocumentation.fieldWithPath("timestamp").description("The timestamp of the response"),
PayloadDocumentation.subsectionWithPath("data").description("An array containing the data of the response"),
PayloadDocumentation.fieldWithPath("totalPage").description("The total number of pages"),
PayloadDocumentation.fieldWithPath("currentPage").description("The current page number"),
PayloadDocumentation.fieldWithPath("totalCount").description("The total number of items"),
PayloadDocumentation.fieldWithPath("pageSize").description("The number of items per page")
))
);
}
}
주의 할점
- test관련 메서드 폴더 위치
- 필자는 최초 test 패키지와 메소드 생성 시 test에 위치한 main 메서드 클래스보다 상위 패키지 위치에서 만들었음.
- test관련 메서드 폴더 위치
- 공식 문서의 ./gradlew openapi3 를 잊지 말 것![](https://blog.kakaocdn.net/dn/6iVc3/btsFjV0l7A3/LvgUUoEXDrvTBKkcjc4z4k/img.png)
Swagger-ui 를 통해 문서 import
위에 코드를 참고해서 테스트 실행할 경우 지정한 경로에 파일 생성 확인 : open-api-3.0.1.json
/src/main/resources/static/docs 위치에 다운로드 받은 swagger 파일 압축해제 후 dist 폴더 아래 있는 파일들 옮기기
index.html 파일 아래 script에 있는 uri 수정
<script> window.onload = function() { // Begin Swagger UI call region const ui = SwaggerUIBundle({ url: "./open-api-3.0.1.json", dom_id: '#swagger-ui', deepLinking: true, presets: [ SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset ], plugins: [ SwaggerUIBundle.plugins.DownloadUrl ], layout: "StandaloneLayout" }); // End Swagger UI call region window.ui = ui; }; </script>
index.html 크롬에서 실행 시 확인 가능
- 꼭 ./gradlew openapi3 실행 후 확인하기
- 꼭 ./gradlew openapi3 실행 후 확인하기
참고 자료
- Spring Rest Docs와 Swagger 조합하기 : https://shirohoo.github.io/backend/test/2021-07-17-swagger-rest-docs/
- 내가 만든 API를 널리 알리기 - Spring REST Docs 가이드편 : https://helloworld.kurly.com/blog/spring-rest-docs-guide/#%EB%B0%8B%EB%B0%8B%ED%95%9C%EA%B2%8C-%EC%95%84%EC%89%AC%EC%9A%B0%EB%8B%88%EA%B9%8C-spring-rest-docs-%EC%97%90-swagger-%EB%A5%BC-%EB%84%A3%EC%96%B4%EB%B3%B4%EC%9E%90
- https://github.com/swagger-api/swagger-ui/releases/tag/v3.51.1
- TOSS SLASH21 — 테스트커버리지 100% 요약/DEMO : https://medium.com/byungkyu-ju/toss-slash21-%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%BB%A4%EB%B2%84%EB%A6%AC%EC%A7%80-100-%EC%9A%94%EC%95%BD-demo-2fb8b52cf2a9
- Spring REST Docs 와 Swagger 를 사용하여 API 명세서 작성 자동화하기 : https://velog.io/@nefertiri/Spring-REST-Docs-%EC%99%80-Swagger-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-API-%EB%AA%85%EC%84%B8%EC%84%9C-%EC%9E%91%EC%84%B1-%EC%9E%90%EB%8F%99%ED%99%94%ED%95%98%EA%B8%B0
- https://github.com/ePages-de/restdocs-api-spechttps://github.com/ePages-de/restdocs-api-spec
'Language > Java' 카테고리의 다른 글
Spring 에서 배치 프로그램 사용해보기 (0) | 2024.02.27 |
---|---|
멀티 스레드 @Async 사용기, CompletableFuture (0) | 2024.02.26 |
JDK21의 신기능 Virtual Thread 알아보기(카카오 테크 밋업) (0) | 2024.02.26 |
[객체 리스트 정렬] 내림 차순으로 정렬하기 (0) | 2023.09.25 |
[JAVA 개념공부] List와 ArrayList의 차이 (0) | 2021.10.25 |