최근 간단한 프로젝트에서 Swagger
를 처음 사용해보았다. 나는 Spring Rest Docs
와 Postman
에서 제공하는 API 명세서만 사용해봤는데, 확실히 디자인이나 기능적인 측면에서 Swagger
가 괜찮았다. 심지어 Postman
으로 옮길 수도 있어서 활용 방식은 뛰어나다고 생각한다.
그래서 이전까진 Spring Rest Docs
와 Swagger
를 동시에 사용하는 건 딱히 생각이 없었지만, 이번에 해보려고 한다! 테스트 강제해서 API 명세서의 신뢰성을 높이는 Spring Rest Docs
의 장점과 깔끔한 디자인, API 테스트 기능 등을 제공하는 Swagger
의 장점을 합쳐서 사용해보려고 한다.
Swagger
의 가장 큰 단점이었던 main 코드에 어노테이션을 덕지덕지 붙이는 것을 보완할 수 있는 방법이라 굉장히 매력적으로 다가왔다! 그럼 이제 기존 Spring Rest Docs
에 Swagger
를 같이 사용하는 방법을 알아보자!
로컬 환경 : Spring Boot 3.2.3, Java 17, Windows 10
// GenerateSwaggerUI import
import org.hidetake.gradle.swagger.generator.GenerateSwaggerUI
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.3'
id 'io.spring.dependency-management' version '1.1.4'
// openAPI 플러그인 추가
id 'com.epages.restdocs-api-spec' version '0.18.2'
// swaggerUI 플러그인 추가
id 'org.hidetake.swagger.generator' version '2.18.2'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
// 생성된 API 스펙이 어느 위치에 있는지 지정
swaggerSources {
sample {
setInputFile(file("${buildDir}/api-spec/openapi3.yaml"))
}
}
dependencies {
... 생략
// REST Docs
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
// 8. openAPI3 추가
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.19.4'
// 9. SwaggerUI 추가
swaggerUI 'org.webjars:swagger-ui:5.18.2'
}
// OpenAPI3 설정
openapi3 {
server = 'http://localhost:8080'
title = "API 문서"
description = "RestDocsWithSwagger Docs"
version = "0.0.1"
format = "yaml"
}
tasks.named('test') {
useJUnitPlatform()
}
// JWT (Bearer Token)를 Authorization 헤더에 명시할 수 있게 설정
tasks.withType(GenerateSwaggerUI) {
dependsOn 'openapi3'
doFirst {
def swaggerUIFile = file("${openapi3.outputDirectory}/openapi3.yaml")
def securitySchemesContent = " securitySchemes:\n" + \
" bearerAuth:\n" + \
" type: http\n" + \
" scheme: bearer\n" + \
" bearerFormat: JWT\n" + \
" name: Authorization\n" + \
" in: header\n" + \
" description: \"Use 'your-access-token' as the value of the Authorization header\"\n" + \
"security:\n" +
" - bearerAuth: [] # Apply the security scheme here"
swaggerUIFile.append securitySchemesContent
}
}
// 생성된 Swagger UI 파일들 복사
tasks.register('copyDocument', Copy) {
dependsOn generateSwaggerUISample
from file("build/swagger-ui-sample/") // A
into file("src/main/resources/static/docs") // A 위치의 파일을 여기에 복사
}
bootJar {
dependsOn copyDocument // 문서 작성 후에 .jar 생성
}
[Spring] restdocs + swagger 같이 사용하기 | Dev_ch 이 글의 설정을 바탕으로 내 프로젝트에 맞게 조금 변경하였다!! 이렇게 올려주셔서 감사합니다!!
우선 document
메서드의 import 패키지를 import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
로 변경해야 한다.
// 기존 document
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
// 변경된 document
import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
Swagger
의 @Tag
와 @Operation(summary = "")
를 사용하기 위해서는 resource(ResourceSnippetParameters.builder().build())
를 사용하면 된다. 자세한 방법은 아래 코드를 통해 알아보면 좋겠다!
@Test
@DisplayName("즐겨찾기 추가")
@WithMockCustomUser
void addInfoBookmark() throws Exception {
// when
String chemId = "6646d897d7e4fb17f5ee2362";
String bearerToken = "Bearer Json Web Token";
ResultActions result = mockMvc.perform(
RestDocumentationRequestBuilders.post("/api/save/bookmark/{chemId}", chemId)
.header("Authorization", bearerToken)
);
doNothing().when(infoBookmarkService).saveInfoBookmark(chemId);
// then
result.andExpect(status().isOk())
.andExpect(content().string("즐겨찾기 추가 성공"))
.andDo(document("add-bookmark",
// JSON 값 예쁘게 출력
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
// Tag, Summary, Request, Response 명세
resource(ResourceSnippetParameters.builder()
.tag("Bookmark API") // tag 지정
.summary("즐겨찾기 추가") // summary 지정
// 요청 헤더 설명
.requestHeaders(
headerWithName(HttpHeaders.AUTHORIZATION).attributes(key("type").value("String"))
.attributes(tokenFormat()).description("JWT (Your Token)")
)
// 요청 파라미터 설명
.pathParameters(
parameterWithName("chemId").attributes(key("type").value("String")).description("즐겨찾기 하려는 분자의 ID(PK)")
)
.build()
)
));
}
기존 Rest Docs
에서는 document
메서드의 인자값으로 요청 및 응답에 대한 명세를 설정해줬다.
resource
메서드에 ResourceSnippetParameters
를 인자값으로 넣어줄 때 tag
, summary
, 기존 requestHeaders
, pathParameters
, requestFields
, responseFields
등을 빌더 패턴으로 변경해주면 Swagger
에서 어노테이션으로 덕지덕지 붙인 설정을 테스트 코드로 만들 수 있다!!
tag
메서드의 값이 같은 경우에는 각 API 명세가 하나의 그룹으로 이루어지게 된다. 아래 결과를 확인하면 바로 이해할 수 있을 것이다!
gradle
로 build
를 하고 서버를 실행한 뒤, 도메인_주소/docs/index.html
로 접속을 하면 아래와 같이 Swagger UI가 적용된 것을 확인할 수 있다!!
API 문서 상단쪽에 Authorize
라는 버튼을 확인할 수 있다! 이건 Gradle
에서 아래와 같이 설정했기 때문에 볼 수 있다.
tasks.withType(GenerateSwaggerUI) {
dependsOn 'openapi3'
doFirst {
def swaggerUIFile = file("${openapi3.outputDirectory}/openapi3.yaml")
def securitySchemesContent = " securitySchemes:\n" + \
" bearerAuth:\n" + \
" type: http\n" + \
" scheme: bearer\n" + \
" bearerFormat: JWT\n" + \
" name: Authorization\n" + \
" in: header\n" + \
" description: \"Use 'your-access-token' as the value of the Authorization header\"\n" + \
"security:\n" +
" - bearerAuth: [] # Apply the security scheme here"
swaggerUIFile.append securitySchemesContent
}
}
간략히 설명하면 HTTP
요청을 보낼 때, Authorization
헤더에 Authorize
버튼을 클릭하고 입력한 값을 Bearer Token
으로 지정하여 보낼 수 있게 설정한 것이다. 아래 사진과 함게 살펴보자.
위와 같이 로그인 후 발급받은 Access Token을 Authorize
버튼을 클릭한 뒤 입력란에 넣어주고 아래 Authorize
버튼은 다시 클릭하면 설정이 완료된다.
아래 Authorize
버튼은 다시 누르면 위와 같이 변경되게 된다. 보통 로그인 후 인증이 필요한 API를 요청할 때 이렇게 사용하면 된다. 만약 토큰을 사용하지 않을 거라면 Logout
버튼을 클릭하면 Authorization
헤더에 값을 명시하지 않고 요청을 보낸다.
잘 안보일 수 있겠지만, 빨간색 줄을 그은 부분이 Authorization
헤더다. 이 헤더의 값을 살펴보면 JWT
로 인증을 할 때 토큰의 접두어로 붙여야 하는 "Bearer "값이 자동으로 붙어 있는 것을 확인할 수 있다. 이렇게 한번 설정해주면 계속 그 값이 유지되어 조금 더 편하게 API 요청을 보낼 수 있다!
이 글과 같은 주제의 다른 글들 대부분은 Swagger를 설정하고 생성되는 openapi3.yaml
파일을 가지고 도커로 API 문서를 따로 배포하는 형태가 많았다. 물론 이 방식이 서버 실행 파일의 크기를 줄여주고 보안상 좋다는 것은 알고 있지만, 나는 로컬로 바로 확인하고 싶었다. 그래서 더 찾아보니 Swagger UI를 바로 적용할 수 있는 방법을 알려주는 글을 찾게 되었다! (참고에 명시한 글)
이전까지는 Swagger
는 단점밖에 보이지 않아 사용할 생각이 없었지만, 이렇게 Spring Rest Docs
와 함께 사용하는 방식으로 사용하니 앞으로는 이 방식으로만 사용할 것 같다 ㅋㅋㅋ 나는 이 방식이 굉장히 좋게 느껴졌고, 다른 개발자분들도 이런 생각을 하고 통합할 수 있게 만드셨다는 점에서 굉장히 대단하다고 느꼈다!