Spring Rest Docs로 나온 Snippet들을 이용하여 API 문서를 만들 때, adoc 파일을 작성하여 만드는 방법 이외에 swagger 혹은 redoc을 이용하여 만드는 방법이 있습니다. 이 방법들을 사용하면 따로 adoc 파일을 작성하지 않아도 된다는 큰 장점이 존재해서 빠르게 API 문서를 만들어야 한다면 도입해 볼만하다고 생각합니다.
1. restdocs-api-spec 설정 추가
`swagger`나 `redoc`을 사용하기 위해서 기존의 Snippet들을 json으로 만들어 줘야하는데 이를 자동으로 변환하기 위해 `restdocs-api-spec` 플러그인 설정을 Gradle 빌드 스크립트에 추가한다. restdocs-api-spec의 장점은 Spring Rest Docs 기반으로 작성된 API 문서 코드를 아래와 같이 수정만 해주면 된다는 점이다. 이 기능을 이용하기 위해서는 빌드 스크립트를 수정해야한다.
// 기존 Rest Docs 기반 Snippet 생성 코드
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
document(
"identifier",
requestFields(...),
responseFields(...)
)
// restdocs-api-spec
import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*;
document(
"identifier",
requestFields(...),
responseFields(...)
)
https://github.com/ePages-de/restdocs-api-spec?tab=readme-ov-file#getting-started
GitHub - ePages-de/restdocs-api-spec: Adds API specification support to Spring REST Docs
Adds API specification support to Spring REST Docs - ePages-de/restdocs-api-spec
github.com
빌드 스크립트를 공식문서를 바탕으로 다음과 같이 설정해 주었다.
`build.gradle`
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.3'
id 'io.spring.dependency-management' version '1.1.7'
id "org.asciidoctor.jvm.convert" version "3.3.2"
id 'com.epages.restdocs-api-spec' version '0.18.2'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
asciidoctorExt
}
repositories {
mavenCentral()
}
ext {
snippetsDir = file('build/generated-snippets')
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
//rest docs
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
//restdocs-api-spec
testImplementation('com.epages:restdocs-api-spec-mockmvc:0.18.2')
}
tasks.named('test') {
useJUnitPlatform()
outputs.dir snippetsDir
}
tasks.named('asciidoctor') {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
dependsOn tasks.test
}
bootJar {
dependsOn tasks.asciidoctor
dependsOn 'openapi3'
from ("${asciidoctor.outputDir}") {
into 'static/docs'
}
from("build/api-spec/openapi3.yaml") {
into 'static/docs/api'
}
from("src/docs/redoc/index.html") {
into 'static/docs/redoc/api'
}
from("src/docs/swagger/index.html") {
into 'static/docs/swagger/api'
}
}
bootRun {
dependsOn tasks.asciidoctor
dependsOn 'openapi3'
classpath += files(tasks.asciidoctor.outputDir)
doFirst {
copy {
from("src/docs/redoc/index.html")
into("${tasks.asciidoctor.outputDir}/static/docs/redoc")
}
copy {
from("src/docs/swagger/index.html")
into("${tasks.asciidoctor.outputDir}/static/docs/swagger")
}
copy {
from("build/api-spec/openapi3.yaml")
into("${tasks.asciidoctor.outputDir}/static/docs")
}
}
systemProperty "spring.web.resources.static-locations",
"classpath:/static/,file:${tasks.asciidoctor.outputDir}"
}
openapi3 {
server = 'https://localhost:8080'
title = 'My API'
description = 'My API description'
version = '0.1.0'
format = 'yaml'
}
./gradelw openapi3
위 명령어를 통해 OAS(Open API Specification)을 지키는 build/api-spec/openapi3.yaml 파일이 만들어 진다. 따라서, bootRun과 build task에 이 파일을 static/docs으로 복사하는 작업을 추가했다.
2. UI 라이브러리 선택하기 (Swagger, Redoc ...)
위에서 만든 yaml 파일과 javascript cdn을 이용하면 UI 템플릿을 간단하게 적용할 수 있다. redoc과 swagger 공식문서들을 살펴보면 둘다 cdn으로 OAS yaml파일을 보기 좋은 API 문서로 만드는 방법을 소개한다.
https://github.com/Redocly/redoc
GitHub - Redocly/redoc: 📘 OpenAPI/Swagger-generated API Reference Documentation
📘 OpenAPI/Swagger-generated API Reference Documentation - Redocly/redoc
github.com
https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/installation.md
swagger-ui/docs/usage/installation.md at 2ab53f4f501e5a4a9e6cb031f49732be9ae28678 · swagger-api/swagger-ui
Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API. - swagger-api/swagger-ui
github.com
이 글들을 참고하여 다음 디렉토리 구조로 index.html을 생성해 주었다.

`src/docs/redoc/index.html`
<redoc spec-url="../openapi3.yaml"></redoc>
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"> </script>
`src/docs/swagger/index.html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="SwaggerUI" />
<title>SwaggerUI</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui-bundle.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: '../openapi3.yaml',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>
redoc을 사용하는경우 request body와 response body의 json이 문자열 평문으로 나오는 문제가 있는데, gradle task를 추가해서 해결하였습니다. 아래 task를 통해 실제 나온 openapi3.yaml 파일의 value: 를 수정하였습니다.

`gradle task`
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
def yamlMapper = new YAMLMapper()
def jsonMapper = new ObjectMapper()
tasks.register('fixOpenApiExamples') {
dependsOn tasks.openapi3
doLast {
def file = file("build/api-spec/openapi3.yaml")
def root = yamlMapper.readTree(file)
root.findParents('value')
.each { JsonNode parent ->
JsonNode v = parent.get('value')
if (v?.isTextual()) {
String txt = v.asText().trim()
if (txt.startsWith('{') || txt.startsWith('[')) {
try {
((ObjectNode) parent)
.set('value', jsonMapper.readTree(txt))
} catch (ignored) {}
}
}
}
yamlMapper.writeValue(file, root)
}
}
bootRun을 실행하기 전에 다음과 같이 fixOpenApiExamples 작업을 실행시키면 아래처럼 바뀌게 됩니다.
./gradlew fixOpenApiExamples bootRun

Swagger의 경우에도 다음과 같이 접속하면 우리가 자동으로 생성하던 그 템플릿을 그대로 볼 수 있다.
http://localhost:8080/docs/swagger/index.html

3. 목차(side bar) 수정하기
redoc같은 경우 기본적으로 왼쪽에 목차까지 제공하는데 해당 목차를 ui에 띄우는 방식이 가장 처음으로 나오는 uri를 기준으로 분류한다.
따라서, /api/items 의 경우 아래처럼 하나로 묶이게 된다.

`MockMvcRestDocumentation.document()` 메서드를 `MockMvcRestDocumentationWrapper.document()`로만 바꿨을 때는 deafult 설정으로 /api/** 일 때 api묶이게 된다.
이를 원하는 그룹으로 묶기 위해서는 시작 uri의 기준점을 재설정하거나 혹은 tag를 달아주는 것이다. tag를 달아주는 방식은 다음과 같다.
document(
"items/create-item",
resource(
ResourceSnippetParameters.builder()
.tag("Item")
.summary("아이템 생성")
.description("아이템을 생성한다.")
.requestFields(
fieldWithPath("name").type(JsonFieldType.STRING).description("아이템 이름"),
fieldWithPath("description").type(JsonFieldType.STRING).description("아이템 부가 설명").optional(),
fieldWithPath("price").type(JsonFieldType.NUMBER).description("아이템 가격")
).
responseFields(
fieldWithPath("id").type(JsonFieldType.NUMBER).description("아이템 id"),
fieldWithPath("name").type(JsonFieldType.STRING).description("아이템 이름"),
fieldWithPath("description").type(JsonFieldType.STRING).description("아이템 부가 설명"),
fieldWithPath("price").type(JsonFieldType.NUMBER).description("아이템 가격")
).build()
)
);
기존의 스니펫들을 ResourceSnippetParameters로 묶어주며 tag를 달아주면 해당 tag로 묶여서 관리되게 된다.

4. 마무리하며
restdocs-api-spec을 이용하여 OAS를 지키는 yaml파일을 생성하여 이를 redoc과 swagger를 이용해서 보기 좋은 API 문서로 만드는 방법을 시도해보았습니다. Swagger의 디자인도 나쁘지 않지만 저는 redoc ui가 더 가독성이 좋다고 생각합니다. 이런 디자인 같은 경우 개인 취향이기 때문에 자신이 원하는 디자인을 선택해서 사용하면 좋을것 같습니다.
'Spring Boot' 카테고리의 다른 글
| Spring Boot 로깅 (2) - logback-spring.xml 설정 및 Grafana Loki 연동 (0) | 2025.09.21 |
|---|---|
| Spring Boot 로깅 (1) - 기초 (0) | 2025.09.16 |
| Spring Rest Docs (3) - Validation 규칙을 API 문서에 추가하기 (restdocs-api-spec) (1) | 2025.07.08 |
| Spring Rest Docs 사용하기 (1) - 기본 설정과 API 문서 생성 (0) | 2025.07.04 |
| Spring Boot에서 발생한 TimeZone 문제 (0) | 2025.05.04 |