티스토리 뷰
오늘날 소프트웨어 개발에서 REST API는 서비스 간의 핵심적인 소통 수단으로 자리 잡았습니다. 프론트엔드, 모바일 앱, 그리고 다른 백엔드 서비스들이 서로 유기적으로 작동하기 위해서는 API가 제공하는 기능을 명확하게 이해하고 활용할 수 있어야 합니다. 하지만 이 '이해'를 돕는 과정이 생각보다 많은 시간과 노력을 요구하곤 합니다.
수동으로 작성되는 API 문서는 빠르게 변하는 비즈니스 요구사항과 개발 과정 속에서 종종 최신 코드와 불일치하는 문제에 직면합니다. 이는 개발팀 간의 불필요한 커뮤니케이션 비용을 증가시키고, 서비스 출시 지연의 원인이 되기도 합니다. 이러한 문제를 해결하고 개발 생산성을 극대화하기 위한 해법으로 Springdoc Swagger (springdoc-openapi)가 각광받고 있습니다.
이 가이드에서는 Spring Boot 환경에서 springdoc-openapi를 활용하여 REST API 문서를 효율적으로 자동화하는 방법을 완벽하게 다룰 것입니다. 단순한 설정부터 고급 기능 활용, 그리고 실전 프로젝트에서의 베스트 프랙티스까지, 여러분의 API 개발 문화를 한 단계 업그레이드할 수 있는 통찰을 제공하겠습니다.

1. Springdoc Swagger, 왜 필요할까요? (API 문서화의 중요성)
소프트웨어 개발에서 API는 마치 건축 설계도와 같습니다. 건물을 짓기 위해 설계도가 필요한 것처럼, 서비스가 제대로 작동하고 다른 서비스와 연동되기 위해서는 API의 구조와 사용법을 명확하게 정의한 문서가 필수적입니다. 이 API 문서는 개발팀 내부뿐만 아니라 협업하는 외부 팀(프론트엔드, 모바일, 다른 백엔드 팀)에게도 중요한 의사소통 수단이 됩니다.
1.1. API 문서화의 중요성
- 협업 촉진: 프론트엔드 개발자는 백엔드 API 문서를 기반으로 화면을 구성하고 데이터를 요청합니다. 명확한 문서는 불필요한 질문과 오해를 줄여 개발 속도를 높입니다.
- 온보딩 효율성: 새로운 팀원이 합류했을 때, 잘 정리된 API 문서는 서비스의 전반적인 구조와 작동 방식을 빠르게 이해하는 데 도움을 줍니다.
- 유지보수 용이성: 시간이 지나더라도 API의 기능과 제약 조건을 파악하는 데 문서가 큰 역할을 합니다. 이는 향후 기능 개선이나 버그 수정 시 중요한 참고 자료가 됩니다.
- 외부 연동 표준: 외부 파트너사나 고객에게 API를 제공할 때, 표준화된 문서는 신뢰도를 높이고 원활한 연동을 가능하게 합니다.
1.2. 기존 API 문서화 방식의 문제점
과거에는 API 문서를 Word, Excel, Notion, Confluence 등 다양한 도구를 사용하여 수동으로 작성하는 경우가 많았습니다. 이러한 방식은 다음과 같은 치명적인 문제점들을 안고 있었습니다.
- 코드와 문서의 불일치: API 기능이 변경될 때마다 문서를 수동으로 업데이트해야 하는데, 이 과정이 누락되거나 지연되는 경우가 빈번했습니다. 결국, 개발자들은 코드를 직접 확인하거나 담당자에게 문의해야만 하는 상황에 놓이게 됩니다. 이는 'Single Source of Truth (단일 진실 공급원)'의 부재로 이어집니다.
- 시간 소모: 문서를 작성하고 유지보수하는 데 상당한 시간과 인력이 소모됩니다. 이는 개발자가 핵심 비즈니스 로직 개발에 집중할 시간을 빼앗는 결과를 초래합니다.
- 일관성 부족: 여러 사람이 문서를 작성할 경우, 문서의 형식이나 내용에 일관성이 떨어지기 쉽습니다. 이는 문서를 읽고 이해하는 데 방해가 됩니다.
- 실행 가능성 부재: 대부분의 수동 문서는 단순히 정보를 나열하는 형태이므로, 실제로 API를 호출하고 응답을 확인하기 위해서는 별도의 도구(Postman, curl 등)를 사용해야 하는 번거로움이 있습니다.
1.3. Springdoc-openapi가 제공하는 가치
이러한 문제점들을 해결하기 위해 등장한 것이 바로 API 문서 자동화 도구이며, Spring Boot 환경에서 가장 강력하고 널리 사용되는 도구가 springdoc-openapi입니다. springdoc-openapi는 OpenAPI Specification (OAS) 3.0 표준을 따르며, 코드에 어노테이션(Annotation)을 추가하는 것만으로 API 문서를 자동으로 생성하고 관리할 수 있게 해줍니다.
OpenAPI Specification 3.0은 REST API를 사람이 읽을 수 있고 기계가 이해할 수 있는 형태로 기술하기 위한 표준 명세입니다. springdoc-openapi는 이 표준에 맞춰 Spring Boot 애플리케이션의 컨트롤러 코드에서 API 정보를 추출하여 JSON 또는 YAML 형태로 OpenAPI 문서를 생성하고, 이를 기반으로 Swagger UI라는 인터랙티브(대화형) 웹 인터페이스를 제공합니다.
springdoc-openapi를 사용함으로써 얻을 수 있는 핵심 가치는 다음과 같습니다.
- 코드와 문서의 동기화: API 로직 변경 시 코드만 수정하면 문서도 자동으로 업데이트되므로, 항상 최신 상태의 문서를 유지할 수 있습니다.
- 생산성 향상: 문서 작성 및 유지보수에 드는 시간과 노력을 획기적으로 줄여 개발자가 핵심 업무에 집중할 수 있게 합니다.
- 인터랙티브 문서: Swagger UI를 통해 API 목록 확인, 요청 파라미터 입력, API 호출 및 응답 확인까지 한 번에 가능하여 개발 및 테스트 효율을 높입니다.
- 표준화된 포맷: OpenAPI 표준을 따르므로, 다른 OpenAPI 호환 도구(예: Swagger Codegen을 이용한 클라이언트 코드 자동 생성)와 연동이 용이합니다.
결론적으로 springdoc-openapi는 개발 생산성을 극대화하고, 팀 내외부의 커뮤니케이션을 원활하게 하며, 궁극적으로는 더욱 견고하고 신뢰할 수 있는 API를 구축하는 데 필수적인 도구입니다. 이제 다음 섹션에서는 여러분의 Spring Boot 프로젝트에 springdoc-openapi를 어떻게 적용하는지 자세히 알아보겠습니다.
2. Spring Boot 프로젝트에 Springdoc Swagger 기본 설정하기
이제 springdoc-openapi가 왜 필요한지 이해했으니, 실제로 여러분의 Spring Boot 프로젝트에 이를 적용해 볼 차례입니다. springdoc-openapi는 최소한의 설정만으로도 강력한 기능을 제공하며, 기본적인 통합은 매우 간단합니다.
2.1. 의존성 추가 (Gradle/Maven)
가장 먼저 build.gradle (Gradle) 또는 pom.xml (Maven) 파일에 springdoc-openapi 관련 의존성을 추가해야 합니다. 여기서 중요한 것은 springdoc-openapi-starter-webmvc-ui 의존성입니다. 이 의존성은 springdoc-openapi의 핵심 기능과 함께 API 문서를 웹에서 시각적으로 탐색하고 테스트할 수 있는 Swagger UI까지 자동으로 포함합니다.
Gradle (build.gradle):
dependencies {
// Spring Boot Starter Web (REST API 개발의 기본)
implementation 'org.springframework.boot:spring-boot-starter-web'
// Springdoc OpenAPI Starter WebMVC UI (Swagger UI 포함)
// 최신 버전을 사용하세요. (예: 2.5.0)
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
// Lombok (선택 사항이지만 DTO 작성을 편리하게 함)
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// 테스트 관련 의존성
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Maven (pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version> <!-- Spring Boot 버전에 맞게 조정 -->
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springdoc-swagger-guide</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springdoc-swagger-guide</name>
<description>Springdoc Swagger Guide for REST API Documentation</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Starter Web (REST API 개발의 기본) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Springdoc OpenAPI Starter WebMVC UI (Swagger UI 포함) -->
<!-- 최신 버전을 사용하세요. (예: 2.5.0) -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>
<!-- Lombok (선택 사항이지만 DTO 작성을 편리하게 함) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 테스트 관련 의존성 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
버전 정보: springdoc-openapi-starter-webmvc-ui의 버전은 여러분의 Spring Boot 버전에 맞춰 선택해야 합니다. 일반적으로 springdoc-openapi 프로젝트의 GitHub 저장소나 Maven Central에서 최신 호환 버전을 확인할 수 있습니다. 예를 들어, Spring Boot 3.x 버전에서는 springdoc-openapi 2.x 버전대를 사용하는 것이 일반적입니다.
2.2. 최소한의 설정으로 Swagger UI 활성화
의존성을 추가하고 프로젝트를 다시 빌드(Build)하면, 놀랍게도 별다른 추가 설정 없이 springdoc-openapi가 자동으로 API 문서를 생성하고 Swagger UI를 활성화합니다. 이는 Spring Boot의 자동 설정(Auto-configuration) 기능 덕분입니다.
애플리케이션이 실행된 후, 웹 브라우저에서 다음 URL 중 하나로 접근하면 Swagger UI를 확인할 수 있습니다.
http://localhost:8080/swagger-ui.htmlhttp://localhost:8080/swagger-ui/index.html(최신springdoc-openapi버전에서 권장되는 경로)
또한, OpenAPI 명세(Specification) 자체는 다음 경로에서 JSON 또는 YAML 형식으로 확인할 수 있습니다.
- JSON:
http://localhost:8080/v3/api-docs - YAML:
http://localhost:8080/v3/api-docs.yaml
기본 포트(8080)나 컨텍스트 경로가 변경되었다면 해당 URL도 변경해야 합니다.
2.3. 기본 설정 커스터마이징 (application.yml)
springdoc-openapi는 대부분의 설정을 application.yml 또는 application.properties 파일을 통해 손쉽게 커스터마이징할 수 있도록 지원합니다. 몇 가지 유용한 설정을 살펴보겠습니다.
# application.yml
springdoc:
# OpenAPI Specification 버전은 springdoc-openapi가 자동으로 관리하며, 일반적으로 명시할 필요 없습니다.
swagger-ui:
# Swagger UI의 접근 경로를 변경합니다. (기본: /swagger-ui/index.html)
path: /my-custom-swagger-ui
# API 요청 시 인증 헤더를 보낼 수 있는 Enable Try it Out 버튼 활성화
try-it-out-enabled: true
# API 리스트 정렬 방식 (none, alpha, method)
doc-expansion: list # 'list' 또는 'none' (기본: list)
# 특정 API만 보이도록 필터링 기능 활성화
filter: true
# UI 상단에 보이는 타이틀 (Swagger UI 웹페이지의 HTML <title> 태그)
display-request-duration: true # API 응답 시간 표시
api-docs:
# OpenAPI 정의 파일의 경로를 변경합니다. (기본: /v3/api-docs)
path: /api-docs-json
# API 문서 JSON 파일의 기본 이름을 변경합니다. (기본: default)
group-configs:
- group: 'default'
paths-to-match: '/**' # 모든 경로 포함
display-name: '기본 API 문서'
# OpenAPI Info 객체 설정 (문서 최상단 정보)
info:
title: "My Spring Boot REST API" # 문서 제목
description: "Springdoc-openapi를 활용한 REST API 문서 자동화 가이드" # 문서 설명
version: "1.0.0" # API 버전
terms-of-service: "http://example.com/terms" # 약관 URL
contact:
name: "개발팀"
url: "http://example.com/contact"
email: "support@example.com"
license:
name: "Apache 2.0"
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
위 설정을 적용하면 Swagger UI가 http://localhost:8080/my-custom-swagger-ui 경로로 접근 가능해지며, 문서의 제목, 설명, 연락처 등 기본 정보가 변경됩니다. doc-expansion: list 설정은 API 목록을 기본적으로 모두 펼쳐 보여주고, filter: true는 검색창을 활성화하여 특정 API를 쉽게 찾을 수 있도록 돕습니다.
2.4. 기본 Swagger UI 살펴보기
애플리케이션을 실행하고 http://localhost:8080/swagger-ui/index.html (또는 여러분이 설정한 경로)에 접속하면 다음과 같은 화면을 볼 수 있습니다.
- API 목록: 애플리케이션에 정의된 모든 REST API 엔드포인트가 컨트롤러별로 그룹화되어 표시됩니다.
- HTTP 메서드: 각 API 엔드포인트는 GET, POST, PUT, DELETE 등 해당 HTTP 메서드와 함께 표시됩니다.
- 요약 정보: 각 API의 간단한 설명이 나타납니다.
- 상세 정보 확장/축소: 각 API 항목을 클릭하면 상세 정보를 확장하거나 축소할 수 있습니다.
- "Try it out" 기능: 확장된 API 정보 내에는 "Try it out" 버튼이 있습니다. 이 버튼을 클릭하면 요청 파라미터를 직접 입력하고 API를 호출하여 실제 응답을 즉시 확인할 수 있습니다. 이는 개발 및 테스트 과정에서 매우 유용한 기능입니다.
- 응답 스키마: API가 반환할 수 있는 응답의 구조(DTO)를 보여줍니다.
기본적으로 springdoc-openapi는 컨트롤러 이름과 메서드 시그니처(인수 및 반환 타입)를 기반으로 API 정보를 유추하여 문서를 생성합니다. 하지만 이 정보만으로는 API의 목적, 각 파라미터의 의미, 반환되는 데이터의 상세 구조 등을 명확하게 설명하기에는 부족합니다. 다음 섹션에서는 @Operation, @Schema 등 다양한 어노테이션을 사용하여 API 문서를 더욱 상세하고 풍부하게 만드는 방법을 다루겠습니다.
3. 핵심 어노테이션을 활용한 API 문서 상세화 (기본 기능)
기본 설정만으로도 springdoc-openapi는 API 엔드포인트를 식별하여 Swagger UI에 표시하지만, 문서의 상세 수준은 다소 부족합니다. API의 목적, 각 파라미터의 역할, 반환되는 응답의 정확한 형태 등을 명확히 하기 위해서는 핵심 어노테이션을 활용하여 문서에 추가 정보를 부여해야 합니다. 이 섹션에서는 주로 사용되는 어노테이션들을 예시와 함께 상세히 설명합니다.
우리가 문서화할 간단한 사용자 관리 API를 가정해 봅시다. 이 API는 사용자 목록 조회와 사용자 생성 기능을 가집니다.
3.1. DTO (Data Transfer Object) 정의
API의 요청 본문(Request Body)이나 응답 본문(Response Body)에 사용될 DTO 클래스를 먼저 정의합니다. 여기에 @Schema 어노테이션을 사용하여 DTO 자체와 각 필드에 대한 상세 설명을 추가할 수 있습니다.
// src/main/java/com/example/springdoc/swagger/guide/dto/UserDto.java
package com.example.springdoc.swagger.guide.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor // 예제 편의를 위한 생성자
@Schema(description = "사용자 정보 DTO") // DTO 자체에 대한 설명
public class UserDto {
@Schema(description = "사용자 고유 ID", example = "1", type = "integer", format = "int64", accessMode = Schema.AccessMode.READ_ONLY)
private Long id; // 일반적으로 생성 시에는 ID가 부여되지 않고, 조회 시에만 사용되므로 READ_ONLY
@Schema(description = "사용자 이름", example = "홍길동", requiredMode = Schema.RequiredMode.REQUIRED)
private String name; // 필수 필드임을 명시
@Schema(description = "사용자 이메일 주소", example = "hong.gildong@example.com", requiredMode = Schema.RequiredMode.REQUIRED)
private String email; // 필수 필드임을 명시
@Schema(description = "사용자 나이", example = "30", type = "integer")
private Integer age; // 선택 필드
}
@Schema(description = "...", example = "...", ...): 클래스나 필드에 대한 상세 정보를 제공합니다.description: 해당 요소에 대한 설명.example: Swagger UI에 표시될 예시 값.type,format: 데이터 타입과 형식.integer,int64등.requiredMode: 필드의 필수 여부를 나타냅니다 (REQUIRED,NOT_REQUIRED,AUTO).accessMode: 필드의 접근 모드 (READ_ONLY,WRITE_ONLY,READ_WRITE).id와 같이 생성 시에는 없고 조회 시에만 존재하는 필드에 유용합니다.
3.2. 컨트롤러 및 API 메서드 문서화
이제 실제 API를 정의하는 컨트롤러에서 springdoc-openapi 어노테이션을 활용해 문서를 상세화해 봅시다.
// src/main/java/com/example/springdoc/swagger/guide/controller/UserController.java
package com.example.springdoc.swagger.guide.controller;
import com.example.springdoc.swagger.guide.dto.UserDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@RestController
@RequestMapping("/api/v1/users")
@Tag(name = "사용자 관리 API", description = "시스템 사용자 정보를 관리하는 API (조회, 생성, 수정, 삭제)") // 컨트롤러 전체에 대한 태그 (그룹핑)
public class UserController {
private final List<UserDto> users = new ArrayList<>();
private final AtomicLong counter = new AtomicLong(0);
// 초기 데이터 설정 (예시)
public UserController() {
users.add(new UserDto(counter.incrementAndGet(), "김철수", "kim.cs@example.com", 25));
users.add(new UserDto(counter.incrementAndGet(), "이영희", "lee.yh@example.com", 30));
}
/**
* `@Operation`: API 메서드에 대한 요약 및 상세 설명, 응답 등을 정의합니다.
*/
@Operation(
summary = "모든 사용자 목록 조회", // API 메서드의 간략한 요약
description = "시스템에 등록된 모든 사용자 목록을 페이지네이션 없이 반환합니다. 이름으로 검색할 수 있습니다.", // API 메서드의 상세 설명
parameters = { // 쿼리 파라미터가 있을 경우 @Parameter 사용
@Parameter(name = "name", description = "검색할 사용자 이름 (부분 일치)", example = "김", required = false)
},
responses = { // API 응답에 대한 정보
@ApiResponse(responseCode = "200", description = "성공적으로 사용자 목록 조회",
content = @Content(mediaType = "application/json",
array = @ArraySchema(schema = @Schema(implementation = UserDto.class)))), // 응답 본문 스키마 정의
@ApiResponse(responseCode = "404", description = "사용자 정보를 찾을 수 없음 (검색 결과가 없는 경우)",
content = @Content(mediaType = "application/json",
schema = @Schema(example = "[]"))) // 404 예시, 빈 배열 반환
}
)
@GetMapping
public ResponseEntity<List<UserDto>> getAllUsers(@RequestParam(required = false) String name) {
List<UserDto> filteredUsers = users.stream()
.filter(user -> name == null || user.getName().contains(name))
.toList();
// 검색 결과가 없어도 200 OK와 빈 배열을 반환하는 것이 RESTful API의 일반적인 패턴이지만,
// 여기서는 예시를 위해 404 응답을 조건부로 추가합니다.
// 실제 운영 환경에서는 200 OK와 빈 배열을 반환하는 것이 좋습니다.
if (filteredUsers.isEmpty() && name != null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ArrayList<>());
}
return ResponseEntity.ok(filteredUsers);
}
@Operation(
summary = "특정 사용자 상세 조회",
description = "주어진 ID에 해당하는 사용자 한 명의 상세 정보를 반환합니다.",
parameters = {
@Parameter(name = "id", description = "조회할 사용자의 고유 ID", example = "1", required = true)
},
responses = {
@ApiResponse(responseCode = "200", description = "성공적으로 사용자 상세 정보 조회",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class))),
@ApiResponse(responseCode = "404", description = "해당 ID의 사용자를 찾을 수 없음")
}
)
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUserById(@PathVariable @Parameter(hidden = true) Long id) { // @Parameter는 메서드 파라미터에도 직접 적용 가능
return users.stream()
.filter(user -> user.getId().equals(id))
.findFirst()
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@Operation(
summary = "새로운 사용자 생성",
description = "새로운 사용자 정보를 시스템에 등록합니다. ID는 자동으로 할당됩니다.",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( // 요청 본문 설명
description = "생성할 사용자 정보 (ID 필드는 제외하고 전송)",
required = true,
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class, example = "{\"name\": \"박영희\", \"email\": \"park.yh@example.com\", \"age\": 28}")) // 요청 예시 추가
),
responses = {
@ApiResponse(responseCode = "201", description = "사용자 생성 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터 (필수 필드 누락 등)",
content = @Content(mediaType = "application/json",
schema = @Schema(example = "{\"message\": \"필수 필드가 누락되었습니다.\"}")))
}
)
@PostMapping
@ResponseStatus(HttpStatus.CREATED) // HTTP 201 Created 응답
public UserDto createUser(@Valid @RequestBody UserDto userDto) {
userDto.setId(counter.incrementAndGet());
users.add(userDto);
return userDto;
}
@Operation(
summary = "사용자 정보 업데이트",
description = "특정 ID의 사용자 정보를 업데이트합니다. 부분 업데이트(PATCH)가 아닌 전체 업데이트(PUT)를 가정합니다.",
parameters = {
@Parameter(name = "id", description = "업데이트할 사용자의 고유 ID", example = "1", required = true)
},
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "업데이트할 사용자 정보",
required = true,
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class))
),
responses = {
@ApiResponse(responseCode = "200", description = "사용자 정보 업데이트 성공",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class))),
@ApiResponse(responseCode = "404", description = "해당 ID의 사용자를 찾을 수 없음"),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터")
}
)
@PutMapping("/{id}")
public ResponseEntity<UserDto> updateUser(@PathVariable Long id, @Valid @RequestBody UserDto updatedUserDto) {
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getId().equals(id)) {
updatedUserDto.setId(id); // ID 유지
users.set(i, updatedUserDto);
return ResponseEntity.ok(updatedUserDto);
}
}
return ResponseEntity.notFound().build();
}
@Operation(
summary = "특정 사용자 삭제",
description = "주어진 ID에 해당하는 사용자 정보를 시스템에서 삭제합니다.",
parameters = {
@Parameter(name = "id", description = "삭제할 사용자의 고유 ID", example = "1", required = true)
},
responses = {
@ApiResponse(responseCode = "204", description = "사용자 삭제 성공 (응답 본문 없음)"),
@ApiResponse(responseCode = "404", description = "해당 ID의 사용자를 찾을 수 없음")
}
)
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT) // HTTP 204 No Content 응답
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean removed = users.removeIf(user -> user.getId().equals(id));
if (removed) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.notFound().build();
}
}
3.3. 주요 Springdoc Swagger 어노테이션 상세 설명
@Tag:- 용도: 컨트롤러 클래스에 적용하여 해당 컨트롤러의 API들을 논리적인 그룹으로 묶고, 그룹에 대한 설명(
name,description)을 제공합니다. Swagger UI에서 이 태그 이름으로 API가 그룹화됩니다. - 위치: 컨트롤러 클래스 상단.
- 예시:
@Tag(name = "사용자 관리 API", description = "시스템 사용자 정보를 관리하는 API")
- 용도: 컨트롤러 클래스에 적용하여 해당 컨트롤러의 API들을 논리적인 그룹으로 묶고, 그룹에 대한 설명(
@Operation:- 용도: API 메서드에 적용하여 해당 API의 상세한 정보를 정의합니다. API의 핵심적인 설명 역할을 합니다.
- 위치: 컨트롤러 메서드 상단.
- 주요 속성:
summary: API의 짧은 요약. Swagger UI에서 API 목록에 표시됩니다.description: API의 상세 설명.operationId: 해당 operation을 고유하게 식별하는 ID. 클라이언트 코드 생성 시 메서드 이름으로 사용될 수 있습니다.deprecated: 해당 API가 더 이상 사용되지 않음을 표시 (true/false).hidden: 해당 API를 문서에서 숨길지 여부 (true/false).
@Parameter:- 용도: API 요청 파라미터(경로 변수
@PathVariable, 쿼리 파라미터@RequestParam, 헤더 파라미터@RequestHeader등)에 대한 상세 정보를 정의합니다. - 위치: 컨트롤러 메서드의 파라미터에 직접 적용하거나,
@Operation내의parameters배열 안에 정의. - 주요 속성:
name: 파라미터 이름 (자동으로 추론되지만, 명시적으로 지정할 수 있습니다).description: 파라미터의 상세 설명.in: 파라미터의 위치 (query,header,path,cookie).required: 파라미터의 필수 여부.example: 파라미터의 예시 값.schema: 파라미터의 데이터 타입 및 스키마 정보 (@Schema와 유사).hidden: 해당 파라미터를 문서에서 숨길지 여부.
- 용도: API 요청 파라미터(경로 변수
@RequestBody:- 용도: HTTP 요청 본문(Request Body)에 대한 상세 정보를 정의합니다. 주로
@PostMapping,@PutMapping등에서 사용됩니다. - 위치:
@Operation내에 정의. - 주요 속성:
description: 요청 본문의 상세 설명.required: 요청 본문의 필수 여부.content: 요청 본문의 미디어 타입(application/json등)과 해당 타입의 스키마(@Content와@Schema조합)를 정의합니다.example속성을@Schema내에 정의하여 요청 본문 예시를 제공할 수 있습니다.
- 용도: HTTP 요청 본문(Request Body)에 대한 상세 정보를 정의합니다. 주로
@ApiResponse:- 용도: API 호출에 대한 응답 정보를 정의합니다. HTTP 상태 코드별로 다양한 응답을 문서화할 수 있습니다.
- 위치:
@Operation내의responses배열 안에 정의. - 주요 속성:
responseCode: HTTP 응답 상태 코드 (예: "200", "201", "400", "401", "404", "500").description: 해당 응답 코드에 대한 설명.content: 응답 본문의 미디어 타입(application/json등)과 해당 타입의 스키마(@Content와@Schema조합)를 정의합니다. 배열 형태의 응답은@ArraySchema를 사용합니다.
이러한 어노테이션들을 적절히 활용하면, Swagger UI는 단순히 API 목록을 보여주는 것을 넘어, API의 계약(Contract)을 명확하고 상세하게 시각화하여 제공합니다. 이는 프론트엔드 개발자나 외부 파트너가 API를 사용하는 데 필요한 모든 정보를 한눈에 파악하고, "Try it out" 기능을 통해 실제 동작을 검증할 수 있도록 돕습니다. 다음 섹션에서는 더 복잡한 시나리오와 고급 커스터마이징 방법을 다루어 보겠습니다.
4. 고급 기능 및 커스터마이징으로 문서화 수준 높이기
기본 어노테이션만으로도 상당 수준의 문서화가 가능하지만, 실제 복잡한 애플리케이션에서는 보안 설정, API 그룹핑, 커스텀 응답 객체 처리 등 더 많은 고급 기능과 커스터마이징이 필요합니다. 이 섹션에서는 이러한 고급 기능들을 활용하여 문서화 수준을 한층 더 높이는 방법을 다룹니다.
4.1. 보안 설정 (JWT/OAuth2) 문서화
대부분의 REST API는 인증(Authentication) 및 인가(Authorization)를 위해 JWT (JSON Web Token)나 OAuth2와 같은 보안 메커니즘을 사용합니다. springdoc-openapi는 이러한 보안 메커니즘을 문서화하고 Swagger UI에서 쉽게 테스트할 수 있도록 지원합니다.
다음은 JWT (Bearer Token) 인증 방식을 Swagger UI에 통합하는 예시입니다. 이를 위해 OpenAPI 빈(Bean)을 직접 구성해야 합니다.
// src/main/java/com/example/springdoc/swagger/guide/config/OpenApiConfig.java
package com.example.springdoc.swagger.guide.config;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// @OpenAPIDefinition 어노테이션을 사용하여 전역적인 OpenAPI 정보를 정의할 수도 있습니다.
@OpenAPIDefinition(
info = @io.swagger.v3.oas.annotations.info.Info(
title = "Springdoc Swagger 가이드 API",
description = "Spring Boot 기반 REST API 문서 (OpenAPIDefinition)",
version = "v1.0",
contact = @Contact(name = "Dev Team", email = "dev@example.com"),
license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0")
),
servers = {
@Server(url = "http://localhost:8080", description = "로컬 개발 서버"),
@Server(url = "https://api.yourdomain.com", description = "운영 서버")
}
)
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
// 전역적인 API 정보 (@OpenAPIDefinition과 중복될 경우, @Bean 방식이 우선합니다.)
.info(new Info().title("Springdoc Swagger Guide API Documentation")
.description("Spring Boot RESTful API 문서 자동화 가이드입니다.")
.version("1.0.0")
.termsOfService("http://swagger.io/terms/")
.license(new io.swagger.v3.oas.models.info.License().name("Apache 2.0").url("http://springdoc.org")))
// JWT (Bearer Token) 보안 스키마 정의
.addSecurityItem(new SecurityRequirement().addList("bearerAuth")) // 모든 API에 bearerAuth 보안 적용
.components(new Components()
.addSecuritySchemes("bearerAuth", // 스키마 이름 (위에 addList에서 사용)
new SecurityScheme()
.name("bearerAuth")
.type(SecurityScheme.Type.HTTP) // HTTP 기반 보안 스키마
.scheme("bearer") // Bearer 토큰 사용
.bearerFormat("JWT") // Bearer 토큰의 포맷은 JWT
.description("JWT 토큰을 입력해주세요. (예: Bearer eyJhbGci...)"))); // 사용자 안내 메시지
// OAuth2 Password Flow 예시 (필요시 추가)
/*
.addSecurityItem(new SecurityRequirement().addList("oauth2_password_flow"))
.components(new Components()
.addSecuritySchemes("oauth2_password_flow",
new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.flows(new OAuthFlows()
.password(new OAuthFlow()
.authorizationUrl("http://localhost:8080/oauth/authorize")
.tokenUrl("http://localhost:8080/oauth/token")
.scopes(new Scopes().addString("read", "read access").addString("write", "write access"))
)
)
)
);
*/
}
}
@OpenAPIDefinition: 클래스 레벨에서 전역적인 API 정보를 정의할 수 있습니다.Info,Server,SecurityScheme등을 선언할 수 있습니다.OpenAPI빈:@Configuration클래스에서OpenAPI타입의@Bean을 정의하면,springdoc-openapi가 이를 사용하여 문서의 전역 설정을 커스터마이징합니다.Components().addSecuritySchemes(): 여기에서SecurityScheme을 정의합니다.name,type,scheme,bearerFormat,description등을 설정합니다.addSecurityItem(new SecurityRequirement().addList("bearerAuth")): 정의된bearerAuth보안 스키마를 문서의 모든 API에 적용하겠다는 의미입니다. 특정 API에만 적용하려면@SecurityRequirement(name = "bearerAuth")어노테이션을 해당 컨트롤러 또는 메서드에 추가하면 됩니다.
이 설정을 추가하면 Swagger UI 상단에 "Authorize" 버튼이 생기고, 이를 통해 JWT 토큰을 입력하여 인증이 필요한 API를 테스트할 수 있게 됩니다.
4.2. API 그룹핑 (Grouping APIs)
애플리케이션에 API가 많아지면 Swagger UI가 너무 길어져 가독성이 떨어질 수 있습니다. springdoc-openapi는 GroupedOpenApi 빈을 사용하여 API를 논리적인 그룹으로 나눌 수 있는 기능을 제공합니다.
// src/main/java/com/example/springdoc/swagger/guide/config/OpenApiGroupConfig.java
package com.example.springdoc.swagger.guide.config;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiGroupConfig {
@Bean
public GroupedOpenApi userApi() {
return GroupedOpenApi.builder()
.group("사용자-서비스") // 그룹 이름
.pathsToMatch("/api/v1/users/**") // 이 그룹에 포함될 API 경로 패턴
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("관리자-서비스") // 그룹 이름
.pathsToMatch("/api/v1/admin/**") // 이 그룹에 포함될 API 경로 패턴
// .packagesToScan("com.example.springdoc.swagger.guide.admin.controller") // 특정 패키지 스캔도 가능
.build();
}
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("공개-서비스")
.pathsToMatch("/api/public/**")
.build();
}
}
이 설정을 추가하면 Swagger UI 상단에 드롭다운 메뉴가 생성되어, 정의된 그룹별로 API 문서를 필터링하여 볼 수 있습니다. pathsToMatch는 Ant 스타일 경로 매칭 패턴을 사용하여 특정 경로에 해당하는 API를 그룹에 포함시킵니다. packagesToScan을 사용하면 특정 패키지 내의 컨트롤러만 그룹에 포함할 수 있습니다.
4.3. DTO 예제 및 응답 객체 상세화
앞서 @Schema 어노테이션을 통해 DTO 필드에 example 값을 추가했습니다. 이는 Swagger UI의 "Example Value" 탭에 표시되어 사용 예시를 제공합니다. 또한, @Content 어노테이션 내의 example 속성을 사용하여 요청/응답 본문 전체에 대한 JSON/YAML 예시를 직접 제공할 수도 있습니다.
// UserController.java (예시)
// ...
@Operation(
summary = "새로운 사용자 생성",
description = "새로운 사용자 정보를 시스템에 등록합니다. ID는 자동으로 할당됩니다.",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "생성할 사용자 정보 (ID 필드는 제외하고 전송)",
required = true,
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = UserDto.class,
example = "{\"name\": \"박영희\", \"email\": \"park.yh@example.com\", \"age\": 28}" // JSON 문자열 예시
)
)
),
// ...
)
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserDto createUser(@Valid @RequestBody UserDto userDto) {
// ...
return userDto;
}
// ...
복잡한 응답 객체의 경우, 여러 개의 @ApiResponse를 사용하여 다양한 HTTP 상태 코드(예: 200 OK, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error)에 대한 응답 스키마와 예시를 명확히 문서화하는 것이 좋습니다. 이를 통해 API 소비자는 예상 가능한 모든 응답 시나리오를 미리 파악할 수 있습니다.
4.4. 다국어 지원 (Internationalization - I18N)
springdoc-openapi는 다국어 지원을 위한 기본적인 기능을 제공합니다. application.yml에 springdoc.locale을 설정하고, Spring의 메시지 리소스 번들을 활용하여 문서의 텍스트를 여러 언어로 제공할 수 있습니다.
# application.yml
springdoc:
locale: ko_KR # 기본 언어를 한국어로 설정
# messages.properties (기본 영어)
api.title=My REST API
api.description=My awesome REST API documentation
# messages_ko_KR.properties (한국어)
api.title=나의 REST API
api.description=나의 멋진 REST API 문서화
이 기능을 활성화하려면 OpenAPI 빈에서 Info 객체 등을 구성할 때 spring.messages.basename에 따라 메시지 코드를 참조하도록 설정해야 합니다. 자세한 내용은 springdoc-openapi의 공식 문서를 참조하는 것이 좋습니다.
4.5. 기타 커스터마이징
- Swagger UI 경로 변경:
springdoc.swagger-ui.path를 사용하여 Swagger UI의 접근 URL을 변경할 수 있습니다 (예:/docs). - API Docs 경로 변경:
springdoc.api-docs.path를 사용하여 OpenAPI JSON/YAML 파일의 접근 URL을 변경할 수 있습니다 (예:/openapi). - 응답 필드 숨기기:
@Schema(hidden = true)를 DTO 필드에 적용하여 특정 필드를 문서에서 숨길 수 있습니다. 이는 민감한 정보나 내부적으로만 사용되는 필드를 외부에 노출하지 않을 때 유용합니다.
이러한 고급 기능과 커스터마이징 옵션들을 활용하면, 단순히 API를 문서화하는 것을 넘어, 팀의 특성과 프로젝트의 요구사항에 완벽하게 부합하는 맞춤형 API 문서를 생성하고 관리할 수 있습니다. 다음 섹션에서는 springdoc-openapi를 실제 프로젝트에서 효과적으로 활용하기 위한 실전 팁과 베스트 프랙티스를 살펴보겠습니다.
5. Springdoc Swagger 실전 활용 및 Best Practice
springdoc-openapi는 강력한 도구이지만, 그 효과를 극대화하려면 단순히 적용하는 것을 넘어 실제 개발 워크플로우에 통합하고 일관된 베스트 프랙티스를 따르는 것이 중요합니다. 이 섹션에서는 springdoc-openapi를 효과적으로 사용하는 팁과 실전 전략을 제시합니다.
5.1. 일관된 어노테이션 사용 습관
- 규칙 설정: 팀 내에서
@Operation,@Parameter,@ApiResponse,@Schema등의 어노테이션 사용 규칙을 명확히 설정합니다. 예를 들어, 모든 API에는summary와description을 필수로 작성하도록 하고, 모든 응답 코드(200, 400, 401, 404, 500 등)에 대한@ApiResponse를 명시하도록 강제할 수 있습니다. - 템플릿 활용: 공통적으로 사용되는 에러 응답 객체(예:
ErrorResponseDto)는@Schema와@ApiResponse를 이용하여 한 번만 정의하고 재활용하도록 합니다. - 명확한 설명:
description속성을 사용할 때는 단순히 "사용자 이름"이 아니라 "생성할 사용자의 고유한 이름. 2자 이상 20자 이하"처럼 상세한 제약 조건과 의미를 함께 작성하여 문서의 품질을 높입니다.
5.2. DTO (Data Transfer Object) 중심의 문서화
- 엔티티 노출 금지: 데이터베이스 엔티티(Entity) 클래스를 API 요청/응답에 직접 사용하지 마십시오. 이는 보안, 유연성, 버전 관리 측면에서 많은 문제를 야기합니다. 항상 API 계약을 위한 DTO를 별도로 정의하고, 여기에
@Schema어노테이션을 사용하여 문서화해야 합니다. DTO는 API의 "계약"이며, 엔티티는 "구현"입니다. - 재활용 가능한 DTO: 여러 API에서 공통적으로 사용되는 DTO는 별도의 패키지에 모아두고 재활용하여 일관성을 유지합니다.
- 내부 필드 숨기기: DTO에 내부적으로만 사용되는 필드가 있다면
@Schema(hidden = true)를 사용하여 문서에서 숨길 수 있습니다. 예를 들어, 민감한 정보나 내부 로직에만 필요한 임시 필드 등이 해당됩니다.
5.3. 자동화된 유효성 검증과 문서 연동
Spring Boot에서는 jakarta.validation (구 javax.validation) 어노테이션을 사용하여 요청 본문이나 파라미터의 유효성을 검증합니다. springdoc-openapi는 이러한 유효성 검증 어노테이션(@NotNull, @Size, @Min, @Max, @Pattern 등)을 인식하여 자동으로 문서에 반영합니다.
// UserDto.java (일부 발췌)
package com.example.springdoc.swagger.guide.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "사용자 정보 DTO")
public class UserDto {
@Schema(description = "사용자 고유 ID", example = "1", type = "integer", format = "int64", accessMode = Schema.AccessMode.READ_ONLY)
private Long id;
@NotBlank(message = "이름은 필수입니다.")
@Size(min = 2, max = 20, message = "이름은 2자 이상 20자 이하입니다.")
@Schema(description = "사용자 이름", example = "홍길동", requiredMode = Schema.RequiredMode.REQUIRED, minLength = 2, maxLength = 20)
private String name;
@NotBlank(message = "이메일은 필수입니다.")
@Email(message = "유효한 이메일 형식이 아닙니다.")
@Schema(description = "사용자 이메일 주소", example = "hong.gildong@example.com", requiredMode = Schema.RequiredMode.REQUIRED, format = "email")
private String email;
@Min(value = 18, message = "나이는 18세 이상이어야 합니다.")
@Schema(description = "사용자 나이", example = "30", type = "integer", minimum = "18")
private Integer age;
}
위와 같이 DTO에 유효성 검증 어노테이션을 추가하면, springdoc-openapi는 @Schema의 minLength, maxLength, pattern, minimum 등의 속성을 자동으로 채워 넣습니다. 이는 API 소비자가 요청 데이터를 구성할 때 어떤 제약 조건이 있는지 명확하게 알 수 있도록 하여 불필요한 에러를 줄여줍니다.
5.4. 개발 워크플로우에 통합하는 전략
- 코드 리뷰에 문서 리뷰 포함: 코드 리뷰 시 기능 구현뿐만 아니라
springdoc-openapi어노테이션이 적절하게 작성되었는지, 문서가 코드의 변경사항을 정확히 반영하는지 함께 검토합니다. - CI/CD 파이프라인 연동:
- 정적 문서 생성:
springdoc-openapi-starter-webmvc-api(UI가 없는 버전) 의존성을 사용하여 빌드 시점에 OpenAPI JSON/YAML 파일을 자동으로 생성할 수 있습니다. 이 파일을 정적 리소스로 호스팅하거나, 다른 문서 관리 시스템에 통합할 수 있습니다. - 클라이언트 코드 자동 생성: 생성된 OpenAPI 명세 파일을
Swagger Codegen과 같은 도구에 전달하여 프론트엔드나 모바일 애플리케이션에서 사용할 클라이언트 SDK를 자동으로 생성할 수 있습니다. 이는 개발 생산성을 극대화하는 강력한 방법입니다.
- 정적 문서 생성:
- API 디자인 우선 접근 (Design-First API Development): 때로는 코드를 작성하기 전에 OpenAPI 명세 파일을 먼저 작성하여 API 계약을 확정하고, 이를 기반으로 백엔드 및 프론트엔드 개발을 동시에 진행하는 "API 디자인 우선(Design-First)" 접근 방식을 사용할 수 있습니다.
springdoc-openapi는 이 과정에서 생성된 명세와 실제 코드 간의 동기화를 유지하는 데 도움을 줄 수 있습니다. - 외부 문서 플랫폼 연동: Confluence, Notion, GitHub Wiki 등 다른 문서 플랫폼과 연동하여, Swagger UI의 라이브 문서를 링크하거나, 정적으로 생성된 OpenAPI 명세를 주기적으로 업데이트하여 중앙 집중식으로 관리할 수 있습니다.
5.5. 테스트 코드와의 연동 (선택 사항)
고급 시나리오에서는 실제 API 테스트 코드에서 springdoc-openapi가 생성하는 OpenAPI 명세의 유효성을 검증할 수도 있습니다. 예를 들어, springdoc-openapi가 생성하는 /v3/api-docs 엔드포인트를 호출하여 반환되는 JSON의 구조나 특정 필드의 존재 여부 등을 JUnit 테스트를 통해 확인할 수 있습니다. 이는 문서가 항상 올바르게 생성되고 있는지 보장하는 일종의 "문서 테스트"가 됩니다.
// src/test/java/com/example/springdoc/swagger/guide/OpenApiDocsTest.java
package com.example.springdoc.swagger.guide;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.hamcrest.Matchers.containsString;
@SpringBootTest
@AutoConfigureMockMvc
public class OpenApiDocsTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldDisplaySwaggerUi() throws Exception {
mockMvc.perform(get("/swagger-ui.html"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("Swagger UI")));
}
@Test
void shouldGenerateOpenApiDocs() throws Exception {
mockMvc.perform(get("/v3/api-docs"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().string(containsString("\"title\":\"Springdoc Swagger Guide API Documentation\"")))
.andExpect(content().string(containsString("\"/api/v1/users\":"))) // 특정 API 경로가 포함되어 있는지 확인
.andExpect(content().string(containsString("\"bearerAuth\":"))); // 보안 스키마가 포함되어 있는지 확인
}
@Test
void shouldGenerateOpenApiYamlDocs() throws Exception {
mockMvc.perform(get("/v3/api-docs.yaml"))
.andExpect(status().isOk())
.andExpect(content().contentType("application/yaml"))
.andExpect(content().string(containsString("title: Springdoc Swagger Guide API Documentation")));
}
}
이러한 베스트 프랙티스를 따르고 springdoc-openapi를 개발 워크플로우에 깊이 통합함으로써, API 문서는 더 이상 귀찮은 부가 작업이 아니라, 견고하고 효율적인 개발을 위한 핵심 자산으로 거듭날 수 있습니다.
6. 마무리: Springdoc Swagger를 통한 효율적인 API 개발 문화
지금까지 springdoc-openapi를 Spring Boot 프로젝트에 통합하고 활용하는 다양한 방법을 살펴보았습니다. 단순한 의존성 추가부터 핵심 어노테이션을 이용한 상세 문서화, 그리고 보안 설정 및 API 그룹핑과 같은 고급 기능, 나아가 실전 활용을 위한 베스트 프랙티스까지, springdoc-openapi가 제공하는 광범위한 기능을 통해 여러분의 API 개발 과정을 혁신할 수 있음을 확인했습니다.
6.1. Springdoc Swagger 도입의 최종적인 이점 요약
springdoc-openapi를 도입함으로써 얻을 수 있는 핵심적인 이점들은 다음과 같습니다.
- 생산성 극대화: API 문서화에 드는 수동 작업을 없애고, 개발자가 핵심 비즈니스 로직 개발에 온전히 집중할 수 있도록 합니다.
- 향상된 협업: 프론트엔드, 모바일, 백엔드 개발자 간의 API 계약에 대한 오해를 줄이고, 명확하고 인터랙티브한 문서를 통해 커뮤니케이션 비용을 절감합니다.
- 더 빠른 온보딩: 새로운 팀원이 프로젝트에 합류했을 때, API의 전체적인 구조와 상세 사용법을 빠르게 파악할 수 있도록 돕습니다.
- 일관되고 정확한 문서: 코드를 기반으로 문서가 자동 생성되므로, 항상 최신 상태의 정확한 문서를 유지할 수 있습니다. 이는 "Single Source of Truth" 원칙을 지키는 가장 효과적인 방법입니다.
- 테스트 및 디버깅 용이성: Swagger UI의 "Try it out" 기능을 통해 실제 API 호출 및 응답을 즉시 확인할 수 있어, 개발 중 테스트 및 디버깅 시간을 단축합니다.
- 표준화된 API 명세: OpenAPI Specification 3.0 표준을 따르므로,
Swagger Codegen과 같은 다양한 도구와 연동하여 클라이언트 SDK 자동 생성 등 추가적인 자동화를 구현할 수 있는 기반을 마련합니다.
6.2. 지속 가능한 API 문서화 전략 제시
API 문서는 한 번 만들고 끝나는 작업이 아닙니다. 비즈니스 요구사항과 기술 스택의 변화에 따라 API 또한 지속적으로 발전하고 변경됩니다. springdoc-openapi는 이러한 변화에 유연하게 대응할 수 있는 지속 가능한 API 문서화 전략의 핵심 도구입니다.
API 문화를 성공적으로 정착시키기 위해서는 다음과 같은 노력이 필요합니다.
- 문서화를 개발 과정의 일부로 인식: 문서는 개발 완료 후 추가하는 "부록"이 아니라, API 설계 단계부터 함께 고민하고 코딩과 동시에 작성해야 하는 "필수 요소"임을 팀 전체가 인식해야 합니다.
- 자동화 도구의 적극적인 활용:
springdoc-openapi와 같은 자동화 도구를 최대한 활용하여 수동 작업을 최소화하고, 개발자의 부담을 줄여야 합니다. - 코드 리뷰를 통한 품질 관리: 코드 리뷰 시 어노테이션의 정확성과 문서의 최신 상태를 함께 점검하여 문서의 품질을 지속적으로 관리합니다.
- 피드백과 개선의 순환: API 사용자의 피드백을 적극적으로 수렴하여 문서의 가독성과 유용성을 지속적으로 개선해 나갑니다.
6.3. 결론
springdoc-openapi는 단순히 API 문서를 생성하는 도구를 넘어, 팀의 개발 문화를 혁신하고 생산성을 비약적으로 향상시킬 수 있는 강력한 촉매제입니다. 이 가이드를 통해 springdoc-openapi의 잠재력을 최대한 발휘하고, 여러분의 Spring Boot 프로젝트에서 REST API 문서 자동화의 진정한 가치를 경험하시기를 바랍니다.
효율적이고 전문적인 API 개발은 더 이상 꿈이 아닙니다. springdoc-openapi와 함께라면 현실이 될 수 있습니다. 지금 바로 여러분의 프로젝트에 적용해 보세요!
참고 자료:
- Springdoc OpenAPI GitHub: https://github.com/springdoc/springdoc-openapi
- Springdoc OpenAPI 공식 문서: https://springdoc.org/
- OpenAPI Specification: https://swagger.io/specification/
'DEV > JAVA' 카테고리의 다른 글
| Spring AI 완벽 가이드: 자바(Java) 개발자를 위한 LLM 기반 AI 애플리케이션 개발 마스터하기 (0) | 2026.01.04 |
|---|---|
| Spring AI, 자바 개발자를 위한 AI 활용법: 장점과 단점 심층 분석 (0) | 2026.01.04 |
| 자바, 버전별 주요 특징 및 변화 (3) | 2025.07.15 |
| AWS S3 파일업로드 (0) | 2019.12.31 |
| spring boot + jasypt (0) | 2017.09.15 |
- Total
- Today
- Yesterday
- 업무자동화
- 도커
- 크로미움
- 코드생성AI
- 데이터베이스
- selenium
- 배민
- 펄
- n8n
- 배민문방구
- 웹스크래핑
- 오픈소스DB
- 웹개발
- AI솔루션
- 비즈니스성장
- Oracle
- Rag
- spring프레임워크
- 직구
- llm최적화
- 해외
- 생산성향상
- 자바AI개발
- springai
- 개발생산성
- 프롬프트엔지니어링
- restapi
- SEO최적화
- springboot
- Java
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 | 29 | 30 | 31 |