프런트와 소통을 위해 API Spec을 제공해줘야 했습니다. 이 과정에서 제공을 위해 고려했던 과정을 공유하고자 합니다.
순서는 가장 많이 사용하는 swagger와 spring rest docs에 대해서 설명하고 그 후 제가 선택했던 방법 순으로 이야기할 예정입니다.
OAS란?
openAPI Specification의 약자로 Rest API에 대한 문서화를 위한 규격입니다.
OAS와 관련하여 자세한 내용은 OpenAPI Specification v3.1.0에서 보면 될 거 같습니다.
Swagger는 이런 OAS 스펙을 구현한 프레임워크라고 생각하면 될 거 같습니다.
사실 OAS는 v3이전에는 swagger1, swagger2로 불렸었는데 SmartBear Software라는 회사가 개발하여 가지고 있었습니다. 그러다 Linux Foundation에 기부가 되면서 새로운 버전으로 OpenAPI Specification를 개발하면서 OAS3.0으로 변경이 되었습니다. 그러면서 이 OAS스펙에 맞게 새롭게 개발이 진행되고 있습니다.
Swagger
swagger는 크게 3개의 툴로 이루어져 있습니다. Swagger Codegen
, Swagger Editor
, Swagger UI
로 되어 있습니다.

Swagger Codegen
: OAS 명세(JSON/YAML)을 기반으로 Stub 코드를 만들어 내는 역할을 한다.Swagger Editor
: OAS 명세를 작성하는 하나의 툴Swagger UI
: OAS 명세를 통해 HTML API 문서로 렌더링 한다.
- Ex) Swagger UI
spring프레임워크에 특화된 라이브러리로 springfox
와 springdoc
가 있습니다. 이러한 라이브러리들은 Annotation을 통해 자동으로 API 명세를 만들어 Swager UI로 보여주는 역할을 합니다.
springfox
의 경우 개발이 중단되기도 하였고 swagger2.0 스펙을 지원하다 보니, 최근에 개발된 springdoc
가 oas3.0 스펙을 지원하는 것으로 비교해 봤을 때 부족한 부분이 있습니다. 따라서 지속적으로 개발이 진행되고 있고 oas3.0스펙을 지원하는 springdoc를 사용하여 예시를 보여줄 예정입니다.
springdoc의 경우 여러 라이브러리를 함께 지원합니다.
- OAS 3.0
- Spring boot v3 (Java17 & Jakarta EE 9)
- JSR-303, @NotNull, @Min, @Max, and @Size
- Swagger-ui
- OAuth2
Springdoc 적용하기
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui'
설정 파일 추가
springdoc:
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
swagger-ui:
path: /api/docs
disable-swagger-default-url: true
display-request-duration: true
operations-sorter: alpha
springdoc를 사용하여 API 문서를 생성할 때 기본적으로 적용이 되는 설정입니다.
요청과 응답에 대한 media type과 swagger-ui로 렌더링 할 때의 위치와 API 요청에 대한 실행시간, API 보이는 순서를 설정했습니다. 이 외에도 여러 설정을 적용할 수가 있습니다.
springdoc-openapi properties에서 참고하여 추가적으로 작성하면 될 거 같습니다.
빈등록
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
// .components(new Components().addSecuritySchemes(
// "bererToken",
// new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("JWT")))
.info(new Info().title("Swagger with springdoc")
.description("springdoc을 통한 OAS구성")
.version("v0.0.1")
.license(new License().name("Apache 2.0").url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("문서")
.url("https://springshop.wiki.github.org/docs"));
}
@Bean
public GroupedOpenApi mainOpenApi() {
String packagesToscan[] = {"com.example.oauth.controller"};
return GroupedOpenApi.builder().group("main").packagesToScan(packagesToscan)
.build();
}
@Bean
public GroupedOpenApi adminOpenApi() {
String packagesToscan[] = {"com.example.oauth.admin.controller"};
return GroupedOpenApi.builder().group("admin").packagesToScan(packagesToscan)
.build();
}
@Bean
public GroupedOpenApi memberOpenApi() {
String packagesToscan[] = {"com.example.oauth.member.controller"};
return GroupedOpenApi.builder().group("users").packagesToScan(packagesToscan)
.build();
}
@Bean
public GroupedOpenApi shortOpenApi() {
String packagesToscan[] = {"com.example.oauth.shorted.controller"};
return GroupedOpenApi.builder().group("short").packagesToScan(packagesToscan)
.build();
}
}
먼저 OpenAPI문서를 생성하기 위해 위와 같이 bean을 등록해야 했습니다.

메인 header와 같은 느낌이라 생각하시면 될 거 같습니다. 따라서 헤더에 들어갈 여러 내용을 넣을 수 있습니다.

group openApi는 우측 상단의 group을 나누어 선택할 수 있게 하고, 그룹에 맞게 controller를 지정하면 그 내부에서 사용할 수 있습니다.
API문서 추가
@SecurityScheme(
name="bearerAuth",
type= SecuritySchemeType.HTTP,
scheme = "Bearer",
bearerFormat = "JWT"
)
@Tag(name = "Admin", description = "관리자 API")
@RestController
@RequestMapping("/admin")
public class AdminController {
@Operation(summary = "테스트1", description = "관리자 테스트 1",
security = @SecurityRequirement(name="bearerAuth"))
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "관리자 테스트1 성공",
content = @Content(schema = @Schema(implementation = AdminResponse.class))),
@ApiResponse(responseCode = "404", description = "Not Found",
content = @Content),
@ApiResponse(responseCode = "401", description = "Authentication Failure",
content = @Content(schema = @Schema(hidden = true)))
})
@GetMapping
public void adminTest1() {
}
@PostMapping
public void adminTest2() {
}
}
- SecurityScheme: Bean등록에서 components의 BearerToken과 같다
- 다만 위에는 전박적인 적용을 해주는 반면, 위 코드는 하나의 클래스에 대해 적용

또한 @ResponseBody나 @RequestBody를 통해 자동으로 API문서를 만들 때 추가해 줄 수 있다.
@GetMapping(value = "/oauth/google", produces = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void authorizationCode(@RequestParam("code") String code,
@RequestParam("scope") String scope,
@RequestParam("authuser") String user,
@RequestParam("prompt") String prompt,
HttpServletResponse response) throws IOException, URISyntaxException {
}

내가 느낀 Swagger의 장점과 단점
swagger를 많이 사용해 보지 않아도 딱 느낄 수 있었던 장점과 단점이 있었습니다.
장점
- 알록달록한 UI: 아무런 설정 없이 바로 완성된 UI를 제공해 주는 점이 좋았고, 눈에 확 들어왔습니다.
- swagger-ui: 전항에서 ui의 디자인에 대해서 말씀드렸는데, 그 외에도 테스트 동작까지 같이 수행할 수 있다는 점도 하나의 장점이라 볼 수 있었습니다.
- controller에서 명세를 간략하게 미리 볼 수 있었습니다.
단점
- 보기 힘든 코드: 많은 어노테이션이 있고 그에 대한 여러 설명이나, 샘플까지 넣을 수 있다 보니 코드가 눈에 안 들어왔습니다.
- springdoc을 위한 Bean객체, 즉 API문서화를 위한 Bean등록이 추가되어야 했습니다. 어떻게 보면 사소하지만 약간은 필요 없는 코드가 추가된 느낌을 받았습니다.
- 지속적으로
내가 관리해야 하는 코드가 늘어난 느낌이고, 코드에 집중이 안되고 분산되는 느낌도 받았습니다. controller에서 데이터가 어떤 형태로 들어오는지 더 명확하게 알 수 있고, 설명까지 넣다 보니 무슨 역할을 하는지 나중에 보더라도 쉽게 알 수 있었습니다. 다만 코드를 보려면 많은 어노테이션들이 걸리적거려 코드에 집중을 저하시켰던 부분이 있었습니다. 또한 처음에는 '어떤 데이터가 오고 가는지 한 번에 알 수 있어서 좋다'라고 생각했지만 이후 '근데 여기서 볼 필요가 있나? 어차피 테스트 코드에서 테스트 파라미터로 더 많은 예제를 볼 수 있는데 굳이?'라는 생각이 들었습니다.
Spring restdocs
Spring의 Rest Docs는 srping에서 작성된 테스트에서 생성된 snippet을 사용하여 만듭니다.
테스트를 통해 API 문서에 대해 문서와 실제 코드가 동기화되고 이 API 문서 자체가 올바르다는 신뢰성을 보장해 줍니다.
간단 용어
- Asciidoctor: asciidoc을 HTML 등으로 변환하기 위한 프로세서
- Snippet: 문서화에 필요한 문서 조각
- 문서 조각 =
http-request
,http-response
등을 나타낸다.
- 문서 조각 =
요구 사항
Spring Rest Docs를 사용하기 위해서는 최소 요구 사항이 있습니다. Java17
과 Spring Framework6
가 기반이 되어야 사용이 가능합니다.
Build Configuration
1. 아스키닥터 plugin 적용
plugins {
id "org.asciidoctor.jvm.convert" version "3.3.2"
}
2. Configurations: Asciidoctor를 확장하기 위한 의존성 구성
configurations {
asciidoctorExt
}
3. AsciidoctorExt 구성에 spring-restdocs-asciidoctor
추가
dependencies {
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:{project-version}'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:{project-version}'
}
Asciidoctor가 API문서를 생성하기 위해 snippet을 찾아볼 위치를 build/generated-snippets
로 자동 지정이 됩니다.
또한 asciidoc의 장점 중 하나인 ::
operation블록을 사용할 수 있습니다.
사용하고자 하는 test 라이브러리에 따라 다른 라이브러리를 넣어주면 됩니다.
MockMvc
: spring-restdocs-mockmvcWebClient
: spring-restdocs-webtestclientREST Assured
: spring-restdocs-restassured
5. Output Location: snippet들을 generate 하고 나온 결과물 위치
ext {
snippetsDir = file('build/generated-snippets')
}
test {
outputs.dir snippetsDir
}
6. asciidoctor가 수행할 task 설정
asciidoctor {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
baseDirFollowsSourceFile()
dependsOn test
}
baseDirFollowsSourceFile()은 Asciidoctor가 변환 작업을 할 때 소스 파일의 디렉터리 구조 유지용
여기까지 하고 빌드를 하게 되면

빌드에 여러 스니펫들이 존재하는 것을 볼 수 있습니다.
여기서 하나 알고 가야 할 점은 snippet을 generate 하기 전에 해야 할 것이 있습니다.
바로. adoc파일을 만들어야 하는데 하나의 기준점이라 생각하면 될 거 같습니다.

따라서 src/docs/asciidoc/
에 *. adoc
파일을 하나 만들고 asciidoctor를 실행해 주면 html로 변환해 주는데, 우리의 목표는 직접 프로세스를 돌리는 것이 아니라 jar로 같이 묶는 것까지 하는 것이므로 추가적인 gradle설정을 보고 가겠습니다.
7. bootJar 패키징
bootJar {
dependsOn(tasks.named("copyDocs"))
from ("${asciidoctor.outputDir}/html5") {
into 'static/docs'
}
}
순서대로 bootJar가 asciidoctor를 의존하고, asciidoctor는 test를 의존합니다.
따라서 순서는 test > asciidoctor > bootJar순으로 수행됩니다.
이후 build를 하면, ./build/generated-snippets/html5
에 html파일이 생성되고, jar안의 /static/docs/
폴더에 복사가 됩니다.
8. 파일 복사
tasks.register("copyDocs", Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static")
}
출력 결과물을 resoures.static으로 옮기기 위한 과정
9. /static/docs의 파일들 삭제
asciidoctor.doFirst {
delete file('src/main/resources/static/docs')
}
지속적으로 파일들이 새로 가져오게 되는데, 이전 값들이 남아있으면 원하는 결과가 아닐 수 있기 때문에 삭제해 줍니다.
10. index.adoc파일 생성
= OAuth API 문서
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 3
== Google OAuth Client
=== Request
include::{snippets}/login-google/http-request.adoc[]
=== Response
include::{snippets}/login-google/http-response.adoc[]
위치는 위에서 언급했듯, src/docs/asciidoc/index.adoc
을 만들어 주면 된다.
이후 bootJar로 빌드하도록 해놓았기 때문에
`./gradlew bootJar로 실행을 하게 되면 된다.
Rest Docs Test 작성
테스트는 JUnit 버전에 따라 다르게 작성할 수 있었습니다. 물론 상위호환이 되기 때문에 선택하여 사용하면 될 거 같습니다.
RestDocumentation생성
Junit5.1에서는 RestDocumentationExtension
을 어노테이션을 통해 합성하여 사용할 수 있도록 제공한다.
@RegisterExtension
final RestDocumentationExtension restDocumentationExtension = new RestDocumentationExtension();
JUnit5.0에서는 Extension을 클래스 어노테이션 ExtendWith
로 제공한다.
@ExtendWith(RestDocumentationExtension.class)
@WebMvcTest(OAuthController.class)
class OAuthControllerTest {
}
와 같이 확장하여 사용하면 된다.
또한 기본 설정의 경우에도
@BeforeEach
void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.apply(documentationConfiguration(restDocumentation)
.uris()
.withScheme("https")
.withHost("docs.api.com")
//.withPort(443) // default로 가능
.and()
.snippets()
.withEncoding("UTF-8") // default는 JVM의 기본 Charset
// .withTemplateFormat(TemplateFormats.markdown()) // 기본 asciidoctor
/**
* private List<Snippet> defaultSnippets = new ArrayList<>(Arrays.asList(CliDocumentation.curlRequest(),
* CliDocumentation.httpieRequest(), HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse(),
* PayloadDocumentation.requestBody(), PayloadDocumentation.responseBody()));
*/
// .withDefaults()
.and()
.operationPreprocessors()
//.withRequestDefaults(modifyUris().scheme("https").host("docs.api.com").port(443)) // 해당 전처리기에만 적용
.withResponseDefaults(prettyPrint())
)
.build();
}
물론 이렇게 안 하고 @AutoConfigureRestDocs
가 존재하지만, 어노테이션이다 보니 커스텀할 수 있는 부분이 더 적다.
@DisplayName("")
@Test
void redirect_login_success() throws Exception {
// when
ResultActions result = mockMvc.perform(
get("/login/google")
.accept(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
);
// then
result.andExpect(MockMvcResultMatchers.status().is3xxRedirection())
.andDo(
document("login-google",
responseHeaders(
headerWithName("Location").description("Google OAuth Login Page Redirect Url")
))
);
}
이렇게 간단하게 했을 때

이런 식으로 나오는 걸 볼 수 있다.
추가적으로 snippet을 customizing 해야 하는 경우도 생기는데

만약 curl-request라는 snippet을 만든다면, src/test/resources/org/springframework/restdocs/templates/asciidoctor
에 넣어주면 됩니다. 이후 테스트코드 입력 포맷은 AbstractFieldsSnippet
을 상속받아서 사용할 수 있습니다.
Spring restdocs의 장점과 단점
장점
- 테스트 코드를 기반으로. adoc파일을 만들기 때문에 테스트 코드가 강제가 된다.
- API문서와 코드가 완전히 동기화된다.
단점
- . adoc 형식의 문법을 알아야 했습니다.
- 적용하는데 생각보다 많은 설정이 필요했고 사용되는 코드도 점점 복잡해졌습니다.
restdocs를 사용함으로써 기존 Swagger에서 느꼈던 제품코드에 집중할 수 없었던 문제를 해결할 수 있었습니다. 또한 이전에 테스트 코드를 거의 작성하지 않았던 프로젝트에서 안 좋은 기억이 있어서 다시 한번 테스트 코드의 중요성을 느껴 테스트 커버리지도 높이던 찰나에 테스트 코드를 통한 API문서 생성은 더 좋게 다가왔다.
하지만, 가장 아쉬웠던 부분은 적용해야 할 부분이 많다였습니다. 물론 커스터마이징 할 수 있다는 것 자체가 장점으로 받아 들어졌지만, 그만큼 snippets를 새로 만들고 테스트코드에 추가하고 등의 작업이 해야 할 일이 많다고 느껴진 것은 사실이었습니다. 그리고 ui의 단순함 또한 아쉽게 느껴졌는데, 기본 UI자체가 swagger-ui보다 단조로웠던 부분이 있습니다.
내가 선택한 방법
사실 어떤 것이 더 좋냐는 위에서 나왔던 장단점들을 보며 선택을 할 수 있었지만, 현재 상황에 대해서도 생각을 해봐야 했습니다.
현 상황
저는 지금 정말 새로운 프로젝트를 들어가는 상황이었습니다. 따라서 API스펙은 주기적으로 바뀌는 상황입니다.
따라서 레거시방식이 없기 때문에 마이그레이션을 할 필요가 없었고 하나로 통일만 하면 되는 상태였습니다.
결정
사실, 처음에는 restdocs로 정하려 했는데, document()
만 하게 된다면 기본적으로 테스트 코드에 맞게 자동으로 생성해 줄 수 있어서 스펙이 변경이 될 때마다 문서화를 위한 코드도 변경하지 않고 제품코드만 변경하면 될 거 같다 생각했습니다.
그런데 인프콘에서 어느 개발자분과 소중한 시간을 가지며 질의를 가질 수 있었는데, 거기서 하셨던 말씀이 제가 생각하지 못했던 부분을 집어주셔서 그 방식과 비슷하게 선택하게 되었습니다.
'거기서 나왔던 부분은 프로젝트 초기에 API스펙이 나와도 지속적으로 나오게 될 것이고, 그때마다 고치는 것은 사실 많은 리소스를 잡는데, 그렇기 때문에 우리는 swagger-ui과 yaml로만 작성을 한다'라고 했었습니다.
협업을 하는 입장에서 API Spec을 통해 프런트가 개발을 하게 될 건데 문제는 만약 @Controller
클래스를 먼저 만들게 되면, 스펙에 따라 입력받는 dto와 반환 상태 등 여러 값이 변경되게 되고, 이에 따라 API문서화를 위한 어노테이션도 변경해야 합니다. 따라서 하나의 변경이 두 변경으로 이어지고 리소스가 많이 들어가게 됩니다.
따라서 처음에는 swagger-ui와 yaml을 통해 API Spec을 전달하고자 했고, 이후 테스트 코드를 작성하게 되면 그때 하나씩 spring rest docs와 swagger-ui를 함께 사용할 수 있게 변경하도록 결정했습니다.
1. OAS를 사용하여 작성한 YAML/JSON -> UI로 변경
yaml을 swagger ui로 변환하기 위해 크게 2가지 방식으로 많이 진행한다.
이 중에서 1번의 경우 버전업이 될 경우 계속 다운로드하여야 한다 생각이 돼서 2번으로 진행했습니다.
먼저 generate해주는 라이브러리는 gradle에서 작업을 수행할 수 있도록 도와주는데 이러한 tasks
를 수행하기 위해
plugins {
...
id 'org.hidetake.swagger.generator' version '2.19.2'
}
dependencies {
...
swaggerUI 'org.webjars:swagger-ui:3.52.5'
}
룰 넣어주었다.
이후 직접 만든 api를 넣어주었다.

openapi: 3.0.3
info:
version: 1.0.0
title: Simple API
description: A simple API to illustrate OpenAPI concepts
servers:
- url: https://example.io/v1
components:
securitySchemes:
BasicAuth:
type: http
scheme: basic
security:
- BasicAuth: []
paths:
/artists:
get:
description: Returns a list of artists
# ----- Added lines ----------------------------------------
parameters:
- name: limit
in: query
description: Limits the number of items on a page
schema:
type: integer
- name: offset
in: query
description: Specifies the page number of the artists to be displayed
schema:
type: integer
# ---- /Added lines ----------------------------------------
responses:
'400':
description: Invalid request
content:
application/json:
schema:
type: object
properties:
message:
type: string
post:
responses:
'201':
description: "성공"
(예시를 가져왔습니다)
이 openapi를 ui로 변경하기 위해
gradle에 swaggerSources
를 넣어주면 된다.
swaggerSources {
oauthtest {
inputFile = file("src/main/resources/api.yaml")
}
}
여기까지 작성 후 generateSwaggerUIOauthtest를 실행하게 되면
./gradlew generateSwaggerUIOauthtest

swagger-ui가 적용된 폴더를 확인할 수 있다.
이후 build를 할 때에는 jar안에 넣어줘야 배포 시 접근이 가능하기 때문에, swagger-ui-oauthtest
를 static폴더 내부로 옮겨야 합니다.
tasks.register('copySwaggerUI') {
dependsOn generateSwaggerUIOauthtest
doLast {
copy {
def generateSwaggerUITask = tasks.named('generateSwaggerUIOauthtest', GenerateSwaggerUI).get()
from "${generateSwaggerUITask.outputDir}"
into "${project.buildDir}/resources/main/static/docs"
}
}
}
bootJar {
dependsOn 'copySwaggerUI'
}
따라서 위와 같이 실행하게 되면

정상적으로 동작하게 됩니다.
이렇게 yaml을 통해 작성을 하다. 추후 Controller에서 test코드의 작성과 함께 restdocs를 적용하게 되었습니다.
2. spring-restdocs를 yaml로 변경하여 ui로 변경할 수 있도록 적용하기
spring-restdocs의 결과물로는 asciidoct
파일이 들어오게 된다. 하지만 swagger-ui에 적용하려면 yaml
이나 json
으로 만들어야 하기 때문에 변환을 해주는 과정이 필요하다 이를 도와주는 라이브러리가 있는데 spring rest docs api specification
입니다.

이 라이브러리의 task들을 넣기 위해서
plugins {
...
id 'com.epages.restdocs-api-spec' version '0.18.2'
}
dependencies {
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
}
위와 같이 gradle을 넣어주었습니다.
이후 제공해 주는 tasks 또한 넣어주면 코드를 변환할 준비는 끝납니다.
openapi3 {
setServer("http://localhost:8008")
title = "Simple API2"
description = "description OpenAPI concepts"
version= "1.0.0"
format = "yaml"
}
여기서 생각해야 할 부분은
test코드에서 파일을 만드는 과정을 MockMvcRestDocumentationWrapper
의 document로 만들어야 한다는 것이다.
@Test
void redirect_login_success() throws Exception {
// when
ResultActions result = mockMvc.perform(
RestDocumentationRequestBuilders.get("/login/google")
.accept(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
);
// then
result.andExpect(MockMvcResultMatchers.status().is3xxRedirection())
.andDo(
MockMvcRestDocumentationWrapper.document("/login-google",
new ResourceSnippetParametersBuilder()
.tag("Google login")
.description("OAuth Login")
.responseFields(
PayloadDocumentation.fieldWithPath("email").description("이메일")
)
)
);
}
이렇게
gradlew openapi3
를 하게 되면,

위와 같이 yaml값을 만들어지는 것을 볼 수 있습니다.
이제 이 yaml을 jar파일에 넣어줘야 하기 때문에 swagger때와 같이 실행할 수 있도록 task의 의존성을 연결해 줘야 합니다.
bootJar {
dependsOn 'copySwaggerUI'
}
tasks.withType(GenerateSwaggerUI) {
dependsOn 'openapi3'
}
tasks.register('copySwaggerUI') {
dependsOn 'generateSwaggerUIOauthtest', 'generateSwaggerUIOauthtest2'
doLast {
copy {
def generateSwaggerUITask = tasks.named('generateSwaggerUIOauthtest', GenerateSwaggerUI).get()
from "${generateSwaggerUITask.outputDir}"
into "${project.buildDir}/resources/main/static/docs/v1"
}
copy {
def generateSwaggerUITask2 = tasks.named("generateSwaggerUIOauthtest2", GenerateSwaggerUI).get()
from "${generateSwaggerUITask2.outputDir}"
into "${project.buildDir}/resources/main/static/docs/v2"
}
}
}
따라서 위와 같이 이전 yaml만 적용했을 때의 generateSwaggerUIOauthtest
와 restdocs를 yaml로 변경한 generateSwaggerUIOauthtest2
를 의존하고 실행을 하게 되면

이렇게 spring rest docs를 정상적으로 yaml로 변경하고 swagger-ui로 적용하는 것을 볼 수 있습니다.
결론
이렇게 실제로 yaml을 직접 swagger-ui로 적용시키고, spring-restdocs 또한 yaml로 변경 후 swagger-ui로 적용하면서 API를 보냈습니다. swagger만을 사용했을 때의 문제점은 spring-restdocs를 통해 보완이 가능했던 점을 겪을 수 있었고, restdocs만을 사용했을 때의 문제점 또한 순수 yaml과 swagger-ui를 통해 trade-off를 할 수 있었습니다.
막 개발이 시작되고 MVP기능에 따른 요구 사항이 계속 바뀌는 현시점에서 swagger만을 사용한다면, controller에 어노테이션이 들어가면서 지속적으로 docs를 최신화하고, 메인 기능을 볼 수 없다는 단점과 spring-restdocs를 사용하기에는 테스트 코드를 작성해야 하는데, 내가 테스트 코드를 완성하고 통과되기까지 API 문서를 공유 못한다는 점과 ui자체가 보기 힘들고, test기능도 없다는 단점이 있었기에 위와 같은 과정을 통해 수정했습니다.
위 방식이 best practice도 아닐 것이고, 각자에게 맞는 도구가 있기 때문에 원하는 방식으로 하면 될 것이라 생각합니다.
참고 문헌
Swagger:
- About Swagger Specification | Documentation | Swagger
- springdoc.org
- Swagger-ui github
- [Spring-API First Develope 연재-1] Spring-docs를 이용한 Swagger API 문서 자동생성 (sk.com)
- [SpringBoot] Swagger API 문서 자동화 간단 연동, 테스트하기 (tistory.com)
- Gradle swagger generator plugin - github
Restdocs:
- Spring Rest Docs 적용 | 우아한형제들 기술블로그 (woowahan.com)
- Spring REST Docs v1.0.1 레퍼런스 :: 스프링부트는 사랑입니다 (tistory.com)
- Spring Rest Docs API Specification Integration - github
Swagger to Restdocs:
- SwaggerUI + Spring REST Docs 함께 사용하기(feat. Rest Assured) (tistory.com)
- 내가 만든 API를 널리 알리기 - Spring REST Docs 가이드편 - 컬리 기술 블로그 (kurly.com)
- ePages-de/restdocs-api-spec: Adds API specification support to Spring REST Docs (github.com)
- OpenAPI Specification을 이용한 더욱 효과적인 API 문서화 | 카카오페이 기술 블로그 (kakaopay.com)