🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

OpenAPI로 API 문서화 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 21. · 3 Views

OpenAPI로 API 문서화 완벽 가이드

Spring Boot 프로젝트에서 springdoc-openapi를 사용하여 Swagger UI로 자동 API 문서를 생성하는 방법을 단계별로 배웁니다. 실무에서 바로 활용할 수 있는 어노테이션과 스키마 정의 방법까지 다룹니다.


목차

  1. OpenAPI_스펙_소개
  2. springdoc-openapi_설정
  3. Swagger_UI_접속
  4. Operation_어노테이션
  5. 요청_응답_스키마_정의
  6. API_그룹화

1. OpenAPI 스펙 소개

어느 날 신입 개발자 김개발 씨는 프론트엔드 팀으로부터 이런 요청을 받았습니다. "API 명세서 주시면 개발 시작할게요!" 그제야 김개발 씨는 깨달았습니다.

API를 만들었지만 문서화를 하나도 안 했다는 사실을 말이죠.

OpenAPI는 RESTful API를 표준화된 방식으로 문서화하는 스펙입니다. 마치 건축 설계도가 건물을 짓는 규칙을 담고 있듯이, OpenAPI는 API의 엔드포인트, 파라미터, 응답 구조를 명확하게 정의합니다.

이를 통해 개발자들은 코드를 보지 않고도 API 사용법을 정확히 알 수 있습니다.

다음 코드를 살펴봅시다.

{
  "openapi": "3.0.1",
  "info": {
    "title": "사용자 관리 API",
    "version": "1.0.0",
    "description": "사용자 CRUD를 제공하는 API입니다"
  },
  "paths": {
    "/api/users": {
      "get": {
        "summary": "사용자 목록 조회",
        "responses": {
          "200": {
            "description": "성공",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

김개발 씨는 당황했습니다. API는 잘 만들었지만 문서화는 전혀 생각하지 못했습니다.

급하게 워드 문서를 열어 엔드포인트 주소를 적기 시작했습니다. "/api/users는 GET 방식이고, 응답은 JSON 배열이고..." 하나씩 적다 보니 벌써 30분이 지났습니다.

그때 옆자리 박시니어 씨가 물었습니다. "수동으로 문서 작성하세요?

OpenAPI 스펙 쓰면 자동으로 되는데요." OpenAPI란 무엇일까요? 쉽게 비유하자면, OpenAPI는 레스토랑의 메뉴판과 같습니다.

메뉴판에는 음식 이름, 가격, 재료, 조리 방법이 적혀 있습니다. 손님은 메뉴판만 보고도 무엇을 주문할지 결정할 수 있습니다.

마찬가지로 OpenAPI는 API의 모든 정보를 구조화된 형식으로 담고 있어서, 개발자가 API 명세서만 보고도 어떻게 호출해야 하는지 정확히 알 수 있습니다. OpenAPI가 없던 시절에는 어땠을까요?

개발자들은 위키 문서, 워드 파일, 이메일 등 다양한 형식으로 API를 설명했습니다. 문제는 API가 변경될 때마다 문서도 수동으로 업데이트해야 한다는 점이었습니다.

코드는 바뀌었는데 문서는 예전 버전 그대로인 경우가 많았습니다. 프론트엔드 개발자가 문서대로 호출했는데 에러가 나면, "아, 그 API는 지난주에 파라미터 이름이 바뀌었어요"라는 답변을 듣곤 했습니다.

더 큰 문제는 팀마다 문서 형식이 제각각이라는 점이었습니다. A팀은 마크다운, B팀은 엑셀, C팀은 위키를 사용했습니다.

신입 사원이 들어올 때마다 "우리 팀은 이렇게 문서화해요"라고 별도로 교육해야 했습니다. 바로 이런 문제를 해결하기 위해 OpenAPI 스펙이 등장했습니다.

OpenAPI는 표준화된 형식으로 API를 기술합니다. JSON 또는 YAML 형식으로 작성되며, API의 엔드포인트, HTTP 메서드, 요청 파라미터, 응답 스키마, 에러 코드 등 모든 정보를 담을 수 있습니다.

가장 큰 장점은 도구가 이 스펙을 읽어서 자동으로 문서를 생성할 수 있다는 점입니다. 또한 OpenAPI 스펙은 기계가 읽을 수 있는 형식이라는 특징이 있습니다.

사람만 읽는 워드 문서와 달리, OpenAPI 스펙 파일은 도구가 파싱해서 자동으로 클라이언트 SDK를 생성하거나, 테스트 코드를 만들거나, API 모니터링 도구와 연동할 수 있습니다. 위의 예제 코드를 살펴보겠습니다.

먼저 openapi 필드는 사용하는 OpenAPI 버전을 명시합니다. 현재는 3.0.x 버전이 가장 많이 사용됩니다.

info 섹션에는 API의 제목, 버전, 설명이 들어갑니다. 마치 책의 표지와 같습니다.

가장 중요한 부분은 paths 섹션입니다. 여기에 모든 엔드포인트 정보가 담깁니다.

/api/users 경로에 GET 메서드가 정의되어 있고, 200 응답 코드일 때 User 배열을 반환한다는 것을 명시하고 있습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 쇼핑몰 API를 개발한다고 가정해봅시다. 상품 조회, 장바구니 추가, 주문 생성 등 수십 개의 엔드포인트가 있습니다.

OpenAPI 스펙으로 정의하면 프론트엔드 팀은 Swagger UI 같은 도구로 인터랙티브하게 API를 테스트할 수 있습니다. QA 팀은 스펙 파일을 기반으로 자동화된 테스트를 작성할 수 있습니다.

심지어 TypeScript 타입 정의를 자동 생성하여 타입 안정성까지 확보할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 OpenAPI 스펙 파일을 수동으로 작성하고 관리하려는 것입니다. 코드가 변경되면 스펙 파일도 수동으로 업데이트해야 하는데, 이는 결국 동기화 문제를 일으킵니다.

따라서 코드에서 자동으로 스펙을 생성하는 도구를 사용하는 것이 바람직합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨는 말했습니다. "Spring Boot에서는 springdoc-openapi 라이브러리를 쓰면 자동으로 OpenAPI 스펙이 생성돼요." OpenAPI 스펙을 이해하면 API 문서화가 얼마나 중요한지, 그리고 표준화된 방식이 왜 필요한지 알 수 있습니다.

여러분도 오늘 배운 내용을 바탕으로 체계적인 API 문서를 만들어 보세요.

실전 팁

💡 - OpenAPI 3.0 버전을 사용하세요. 2.0(Swagger)은 레거시입니다

  • 스펙 파일은 수동 작성보다 코드 기반 자동 생성을 권장합니다
  • Swagger Editor에서 스펙 파일의 유효성을 검증할 수 있습니다

2. springdoc-openapi 설정

김개발 씨는 OpenAPI의 중요성을 깨달았습니다. 하지만 JSON 파일을 일일이 작성하는 것은 너무 번거로웠습니다.

"더 쉬운 방법은 없을까요?" 박시니어 씨가 웃으며 대답했습니다. "당연히 있죠.

springdoc-openapi 라이브러리를 쓰면 됩니다."

springdoc-openapi는 Spring Boot 애플리케이션에서 OpenAPI 3 스펙을 자동으로 생성해주는 라이브러리입니다. 마치 자동차의 네비게이션이 지도를 자동으로 업데이트하듯이, 이 라이브러리는 코드에서 자동으로 API 문서를 만들어냅니다.

의존성을 추가하고 간단한 설정만 하면 바로 사용할 수 있습니다.

다음 코드를 살펴봅시다.

// build.gradle (Gradle)
dependencies {
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}

// application.yml
springdoc:
  api-docs:
    path: /api-docs
    enabled: true
  swagger-ui:
    path: /swagger-ui.html
    enabled: true
    operations-sorter: method
    tags-sorter: alpha
  default-consumes-media-type: application/json
  default-produces-media-type: application/json

김개발 씨는 박시니어 씨의 화면을 들여다봤습니다. 화면에는 깔끔한 API 문서 페이지가 떠 있었습니다.

"어떻게 이렇게 예쁘게 만들었어요?" 박시니어 씨는 간단히 답했습니다. "의존성 하나 추가했을 뿐이에요." springdoc-openapi는 정확히 무엇일까요?

쉽게 비유하자면, springdoc-openapi는 자동 번역기와 같습니다. 여러분이 한국어로 말하면 자동으로 영어로 번역해주는 것처럼, Spring 코드를 작성하면 자동으로 OpenAPI 스펙으로 변환해줍니다.

여러분은 평소처럼 Controller를 작성하기만 하면 되고, 라이브러리가 알아서 문서를 생성합니다. 이 라이브러리가 없던 시절에는 어땠을까요?

과거에는 Swagger 2를 많이 사용했습니다. 하지만 Swagger 2는 설정이 복잡하고, OpenAPI 3 스펙을 지원하지 않았습니다.

또한 Spring Boot 3와 호환성 문제가 있어서 많은 개발자들이 불편을 겪었습니다. 문서 하나 만들려고 XML 설정 파일을 여러 개 작성해야 했고, 버전 업그레이드 때마다 설정을 다시 수정해야 하는 경우가 많았습니다.

바로 이런 불편함을 해결하기 위해 springdoc-openapi가 등장했습니다. springdoc-openapi를 사용하면 최소한의 설정만으로 즉시 API 문서를 생성할 수 있습니다.

의존성 하나만 추가하면 기본 설정이 자동으로 적용되며, 별도 코드 작성 없이도 모든 Controller가 문서화됩니다. 또한 OpenAPI 3 스펙을 완벽히 지원하여 최신 표준을 따릅니다.

무엇보다 Spring Boot 3와 완벽하게 호환됩니다. Spring Boot의 자동 설정 기능을 활용하므로, 복잡한 Bean 등록이나 XML 설정이 필요 없습니다.

그냥 의존성만 추가하면 끝입니다. 위의 설정 코드를 살펴보겠습니다.

먼저 Gradle 파일에서 springdoc-openapi-starter-webmvc-ui 의존성을 추가합니다. 버전 2.3.0은 Spring Boot 3 기반입니다.

Spring Boot 2를 사용한다면 1.x 버전을 사용해야 합니다. 다음으로 application.yml에서 상세 설정을 합니다.

api-docs.path는 OpenAPI 스펙 JSON이 제공될 경로입니다. 기본값은 /v3/api-docs이지만, 원하는 경로로 변경할 수 있습니다.

swagger-ui.path는 Swagger UI 웹 페이지 경로입니다. 여기서는 /swagger-ui.html로 설정했습니다.

operations-sorter를 method로 설정하면 HTTP 메서드 순서대로 정렬되고, tags-sorter를 alpha로 설정하면 태그가 알파벳 순서로 정렬됩니다. default-consumes-media-typedefault-produces-media-type은 기본 Content-Type을 설정합니다.

대부분의 REST API는 JSON을 사용하므로 application/json으로 설정하는 것이 일반적입니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 마이크로서비스 아키텍처 환경을 생각해봅시다. 주문 서비스, 결제 서비스, 배송 서비스가 각각 독립적으로 운영됩니다.

각 서비스마다 springdoc-openapi를 설정하면, 모든 서비스의 API 문서가 자동으로 생성됩니다. API Gateway에서 이들을 통합하여 하나의 통합 문서로 제공할 수도 있습니다.

또한 CI/CD 파이프라인에서 OpenAPI 스펙 파일을 추출하여 자동으로 클라이언트 SDK를 생성하거나, 계약 테스트를 수행하는 데 활용할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 프로덕션 환경에서도 Swagger UI를 활성화하는 것입니다. Swagger UI는 개발 및 테스트 용도로만 사용해야 하며, 보안상 이유로 프로덕션에서는 비활성화해야 합니다.

enabled: false 옵션을 프로덕션 프로파일에 설정하세요. 또한 너무 많은 엔드포인트가 있는 경우 Swagger UI 로딩이 느려질 수 있습니다.

이럴 때는 @Hidden 어노테이션으로 불필요한 엔드포인트를 문서에서 제외하거나, 그룹화 기능을 사용하여 문서를 분할하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 안내대로 의존성을 추가하고 설정 파일을 작성했습니다. 단 5분 만에 설정이 끝났습니다.

"이제 서버를 재시작하고 Swagger UI에 접속해보세요." springdoc-openapi 설정을 마스터하면 최소한의 노력으로 전문적인 API 문서를 만들 수 있습니다. 여러분도 지금 바로 프로젝트에 적용해 보세요.

실전 팁

💡 - Spring Boot 3는 springdoc-openapi 2.x, Spring Boot 2는 1.x 버전을 사용하세요

  • 프로덕션 환경에서는 swagger-ui.enabled를 false로 설정하세요
  • API 문서 경로는 보안상 예측하기 어려운 경로로 변경하는 것을 권장합니다

3. Swagger UI 접속

설정을 마친 김개발 씨는 기대에 차서 서버를 재시작했습니다. "이제 어디로 접속하면 되나요?" 박시니어 씨가 브라우저 주소창에 주소를 입력했습니다.

화면에 깔끔한 API 문서 페이지가 펼쳐졌습니다. "와, 이게 자동으로 만들어진 거예요?"

Swagger UI는 OpenAPI 스펙을 시각적으로 보여주는 인터랙티브 문서 페이지입니다. 마치 온라인 쇼핑몰에서 상품을 클릭하여 상세 정보를 보듯이, Swagger UI에서는 각 API를 클릭하여 파라미터, 응답 예시, 직접 테스트까지 할 수 있습니다.

코드 없이도 API를 바로 호출해볼 수 있는 강력한 도구입니다.

다음 코드를 살펴봅시다.

// 애플리케이션 실행 후 브라우저에서 접속
// http://localhost:8080/swagger-ui.html

// 또는 OpenAPI JSON 스펙 직접 확인
// http://localhost:8080/api-docs

// 특정 그룹의 문서만 확인
// http://localhost:8080/swagger-ui.html?urls.primaryName=user-api

// Swagger UI 커스터마이징을 위한 설정
@Configuration
public class SwaggerConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("사용자 관리 API")
                .version("v1.0")
                .description("사용자 CRUD를 제공하는 RESTful API입니다")
                .contact(new Contact()
                    .name("김개발")
                    .email("dev@example.com")));
    }
}

김개발 씨는 화면을 보고 감탄했습니다. 자신이 만든 모든 API가 깔끔하게 정리되어 있었습니다.

Controller 이름별로 그룹화되어 있고, 각 엔드포인트의 HTTP 메서드가 색깔별로 구분되어 있었습니다. GET은 파란색, POST는 초록색, DELETE는 빨간색이었습니다.

Swagger UI란 무엇일까요? 쉽게 비유하자면, Swagger UI는 박물관의 오디오 가이드와 같습니다.

박물관에서 전시물 앞에 서면 오디오 가이드가 자동으로 설명을 들려줍니다. 마찬가지로 Swagger UI는 각 API에 대한 설명을 보여주고, 심지어 직접 실행까지 할 수 있게 해줍니다.

개발자는 코드를 읽거나 Postman 같은 별도 도구 없이도 브라우저만으로 API를 테스트할 수 있습니다. Swagger UI가 없던 시절에는 어땠을까요?

개발자들은 API를 테스트하려면 curl 명령어를 타이핑하거나, Postman에서 일일이 요청을 만들어야 했습니다. 엔드포인트 주소를 복사하고, 헤더를 설정하고, 바디에 JSON을 작성하는 과정이 번거로웠습니다.

특히 신입 개발자나 프론트엔드 개발자에게는 진입 장벽이 높았습니다. 더 큰 문제는 API 명세와 실제 동작이 다를 때였습니다.

문서에는 userId라고 적혀 있는데 실제로는 user_id를 사용해야 하는 경우가 종종 있었습니다. 개발자는 여러 번 시행착오를 거쳐야 했습니다.

바로 이런 불편함을 해결하기 위해 Swagger UI가 등장했습니다. Swagger UI를 사용하면 브라우저만으로 모든 API를 탐색할 수 있습니다.

각 엔드포인트를 클릭하면 요청 파라미터, 헤더, 바디 스키마, 응답 예시가 모두 표시됩니다. "Try it out" 버튼을 누르면 실제로 API를 호출하고 응답을 확인할 수 있습니다.

또한 실시간으로 코드와 동기화됩니다. Controller에 새로운 메서드를 추가하면 서버를 재시작한 후 Swagger UI를 새로고침하면 바로 반영됩니다.

별도로 문서를 업데이트할 필요가 없습니다. 무엇보다 팀 협업에 탁월합니다.

프론트엔드 개발자는 Swagger UI를 보고 어떤 파라미터를 보내야 하는지 정확히 알 수 있습니다. QA 엔지니어는 각 API를 직접 테스트하며 버그를 찾을 수 있습니다.

심지어 비개발자인 기획자나 PM도 API가 어떻게 동작하는지 시각적으로 이해할 수 있습니다. 위의 코드를 살펴보겠습니다.

기본적으로는 http://localhost:8080/swagger-ui.html로 접속하면 됩니다. 포트 번호는 여러분의 Spring Boot 설정에 따라 다를 수 있습니다.

이 주소로 접속하면 Swagger UI 웹 페이지가 열립니다. 만약 OpenAPI 스펙 JSON을 직접 확인하고 싶다면 http://localhost:8080/api-docs로 접속하면 됩니다.

이 JSON 파일은 도구가 읽을 수 있는 형식이므로, 자동화 스크립트나 코드 생성 도구에 활용할 수 있습니다. SwaggerConfig 클래스는 Swagger UI의 메타 정보를 커스터마이징합니다.

Info 객체에 API 제목, 버전, 설명, 담당자 정보를 설정할 수 있습니다. 이 정보는 Swagger UI 페이지 상단에 표시됩니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 API 리뷰 미팅을 진행한다고 가정해봅시다.

회의실 화면에 Swagger UI를 띄워놓고, 각 엔드포인트를 하나씩 클릭하며 설명합니다. "이 API는 사용자 목록을 조회하는데요, 페이지네이션 파라미터로 page와 size를 받습니다.

Try it out 버튼을 눌러볼까요?" 실제로 API를 호출하여 응답을 확인하면 모든 참석자가 명확하게 이해할 수 있습니다. 또한 외부 파트너사에 API를 제공할 때도 Swagger UI 링크만 전달하면 됩니다.

별도로 PDF 문서를 만들 필요가 없습니다. 파트너사 개발자는 Swagger UI에서 직접 테스트하며 통합 개발을 진행할 수 있습니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 인증이 필요한 API를 Swagger UI에서 테스트할 때 토큰을 설정하지 않는 것입니다.

Swagger UI 우측 상단의 "Authorize" 버튼을 클릭하여 Bearer 토큰을 입력해야 인증이 필요한 API를 테스트할 수 있습니다. 또한 Swagger UI는 개발 환경에서만 노출하는 것이 안전합니다.

프로덕션 환경에서 공개하면 API 구조가 외부에 노출되어 보안 위험이 생길 수 있습니다. 환경별로 설정을 분리하세요.

다시 김개발 씨의 이야기로 돌아가 봅시다. 김개발 씨는 Swagger UI에서 자신이 만든 API를 하나씩 클릭해보았습니다.

"Try it out" 버튼을 눌러 실제로 호출해보니 응답이 바로 화면에 표시되었습니다. "이렇게 편할 수가!" Swagger UI를 제대로 활용하면 API 개발과 테스트가 훨씬 효율적으로 변합니다.

여러분도 오늘 배운 내용을 바탕으로 팀에 Swagger UI를 도입해 보세요.

실전 팁

💡 - 인증이 필요한 API는 우측 상단 "Authorize" 버튼에서 토큰을 설정하세요

  • Swagger UI 경로는 application.yml에서 변경할 수 있습니다
  • 프로덕션 환경에서는 보안을 위해 Swagger UI를 비활성화하세요

4. Operation 어노테이션

Swagger UI는 잘 작동했지만, 김개발 씨는 뭔가 아쉬웠습니다. API 설명이 너무 간단했기 때문입니다.

"이 API가 정확히 뭘 하는지 더 자세히 설명할 수는 없을까요?" 박시니어 씨가 코드를 열며 말했습니다. "@Operation 어노테이션을 쓰면 됩니다."

@Operation 어노테이션은 각 API 엔드포인트에 상세한 설명을 추가하는 도구입니다. 마치 상품에 상세 설명 라벨을 붙이듯이, API에 제목, 설명, 태그, 응답 코드 정보를 명시할 수 있습니다.

이를 통해 API 사용자는 무엇을 요청하고 무엇을 받게 되는지 명확히 이해할 수 있습니다.

다음 코드를 살펴봅시다.

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

@RestController
@RequestMapping("/api/users")
@Tag(name = "사용자 관리", description = "사용자 CRUD API")
public class UserController {

    @Operation(
        summary = "사용자 목록 조회",
        description = "페이지네이션을 지원하는 사용자 목록 조회 API입니다. " +
                     "page와 size 파라미터로 원하는 범위의 데이터를 가져올 수 있습니다."
    )
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "조회 성공"),
        @ApiResponse(responseCode = "400", description = "잘못된 파라미터"),
        @ApiResponse(responseCode = "500", description = "서버 오류")
    })
    @GetMapping
    public ResponseEntity<List<User>> getUsers(
        @Parameter(description = "페이지 번호 (0부터 시작)", example = "0")
        @RequestParam(defaultValue = "0") int page,
        @Parameter(description = "페이지 크기", example = "10")
        @RequestParam(defaultValue = "10") int size
    ) {
        // 실제 구현 코드
        return ResponseEntity.ok(userService.getUsers(page, size));
    }
}

김개발 씨는 Swagger UI 화면을 보며 고민했습니다. "GET /api/users"라는 제목만으로는 이 API가 정확히 무엇을 하는지 알기 어려웠습니다.

파라미터에 어떤 값을 넣어야 하는지, 어떤 에러가 발생할 수 있는지 추가 설명이 필요했습니다. @Operation 어노테이션이란 무엇일까요?

쉽게 비유하자면, @Operation은 박물관 전시물 옆의 안내판과 같습니다. 전시물만 덩그러니 놓여 있으면 관람객은 이것이 무엇인지 알기 어렵습니다.

하지만 안내판에 제목, 설명, 제작 연도, 작가 정보가 적혀 있으면 관람객은 전시물을 완전히 이해할 수 있습니다. 마찬가지로 @Operation은 API에 대한 모든 메타 정보를 담아서 사용자가 명확하게 이해할 수 있게 해줍니다.

어노테이션 없이 API를 만들면 어떻게 될까요? springdoc-openapi는 기본적으로 메서드 이름과 파라미터 타입만으로 문서를 생성합니다.

예를 들어 getUsers라는 메서드는 "get users"라는 제목으로 표시됩니다. 하지만 이것만으로는 부족합니다.

이 API가 전체 사용자를 가져오는지, 활성 사용자만 가져오는지, 페이지네이션을 지원하는지 알 수 없습니다. 또한 응답 코드에 대한 정보도 없습니다.

200 응답 외에 어떤 에러 코드가 발생할 수 있는지, 각 에러 코드가 무엇을 의미하는지 사용자는 추측해야 합니다. 바로 이런 문제를 해결하기 위해 @Operation 어노테이션이 제공됩니다.

@Operation을 사용하면 API의 목적을 명확히 설명할 수 있습니다. summary는 한 줄 요약이고, description은 상세 설명입니다.

사용자는 이 설명만 읽고도 API의 모든 것을 이해할 수 있습니다. @ApiResponses를 사용하면 가능한 모든 응답 코드를 문서화할 수 있습니다.

200 성공 응답뿐만 아니라, 400 에러는 어떤 경우에 발생하는지, 500 에러는 언제 나타나는지 명시할 수 있습니다. 이를 통해 클라이언트 개발자는 에러 처리 로직을 정확히 작성할 수 있습니다.

@Tag는 Controller 전체를 그룹화합니다. Swagger UI에서 "사용자 관리"라는 섹션 아래에 모든 사용자 관련 API가 모이게 됩니다.

이는 수십 개의 엔드포인트가 있을 때 문서를 체계적으로 구조화하는 데 유용합니다. @Parameter는 각 요청 파라미터에 대한 설명을 추가합니다.

파라미터 이름만으로는 용도를 알기 어려운 경우가 많습니다. page라는 파라미터가 0부터 시작하는지 1부터 시작하는지, size는 최댓값이 얼마인지 명시할 수 있습니다.

example 속성으로 예시 값을 제공하면 사용자가 Swagger UI에서 바로 테스트할 수 있습니다. 위의 코드를 살펴보겠습니다.

먼저 Controller 클래스에 @Tag 어노테이션을 붙여 그룹 이름을 설정합니다. name은 Swagger UI에 표시될 제목이고, description은 이 그룹에 대한 설명입니다.

각 메서드에는 @Operation 어노테이션을 추가합니다. summary는 "사용자 목록 조회"처럼 간결하게 작성하고, description은 페이지네이션 지원 여부, 파라미터 의미 등을 상세히 설명합니다.

@ApiResponses는 배열 형태로 여러 응답 코드를 정의합니다. 각 @ApiResponse는 HTTP 상태 코드와 그 의미를 담고 있습니다.

200은 성공, 400은 잘못된 파라미터, 500은 서버 오류입니다. 파라미터에는 @Parameter 어노테이션을 붙여 설명과 예시를 제공합니다.

description에는 파라미터의 의미를, example에는 실제 사용할 수 있는 값을 적습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 결제 API를 개발한다고 가정해봅시다. 결제 요청은 다양한 검증 조건이 있습니다.

카드 번호 형식, 유효기간, CVV 등 여러 파라미터가 필요하고, 각 파라미터가 잘못되면 다른 에러 코드가 반환됩니다. @Operation으로 이 모든 조건을 명시하면, 클라이언트 개발자는 한 번에 정확한 요청을 만들 수 있습니다.

또한 외부 API를 공개하는 경우, @Operation으로 작성한 문서는 공식 API 레퍼런스가 됩니다. 별도로 문서 사이트를 만들 필요 없이 Swagger UI가 곧 개발자 포털이 됩니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 설명을 너무 길게 작성하는 것입니다.

description에 코드 구현 세부 사항까지 적으면 오히려 가독성이 떨어집니다. API 사용자가 알아야 할 것만 간결하게 작성하세요.

또한 응답 스키마가 변경되었는데 @ApiResponse 설명을 업데이트하지 않으면 문서와 실제 동작이 달라집니다. 코드 리뷰 시 어노테이션 내용도 함께 검토하는 습관을 들이세요.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 안내대로 @Operation을 추가했습니다.

Swagger UI를 새로고침하니 API 설명이 훨씬 상세해졌습니다. "이제 프론트엔드 팀에서도 바로 이해할 수 있겠어요!" @Operation 어노테이션을 제대로 활용하면 코드만으로도 완벽한 API 문서를 만들 수 있습니다.

여러분도 오늘 배운 내용을 바탕으로 API에 상세한 설명을 추가해 보세요.

실전 팁

💡 - summary는 간결하게, description은 상세하게 작성하세요

  • 가능한 모든 HTTP 응답 코드를 @ApiResponses에 명시하세요
  • @Parameter의 example 속성으로 사용자가 바로 테스트할 수 있게 하세요

5. 요청 응답 스키마 정의

API 설명은 잘 작성했지만, 김개발 씨는 또 다른 문제를 발견했습니다. 요청 바디와 응답 바디의 구조가 Swagger UI에 제대로 표시되지 않았습니다.

"JSON 필드가 어떤 타입인지, 필수인지 선택인지 어떻게 표시하나요?" 박시니어 씨가 DTO 클래스를 열며 답했습니다. "@Schema 어노테이션을 쓰면 됩니다."

@Schema 어노테이션은 요청과 응답의 데이터 구조를 상세히 정의합니다. 마치 설계 도면에 각 부품의 치수와 재질을 명시하듯이, @Schema는 각 필드의 타입, 설명, 필수 여부, 예시 값을 정확하게 표현합니다.

이를 통해 클라이언트 개발자는 정확히 어떤 형식의 데이터를 보내고 받아야 하는지 알 수 있습니다.

다음 코드를 살펴봅시다.

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Email;

@Schema(description = "사용자 생성 요청 DTO")
public class CreateUserRequest {

    @Schema(
        description = "사용자 이름",
        example = "홍길동",
        requiredMode = Schema.RequiredMode.REQUIRED,
        minLength = 2,
        maxLength = 50
    )
    @NotBlank(message = "이름은 필수입니다")
    private String name;

    @Schema(
        description = "이메일 주소",
        example = "hong@example.com",
        requiredMode = Schema.RequiredMode.REQUIRED,
        format = "email"
    )
    @Email
    @NotBlank
    private String email;

    @Schema(
        description = "나이 (선택 사항)",
        example = "25",
        minimum = "1",
        maximum = "150"
    )
    private Integer age;

    // getter, setter 생략
}

@Schema(description = "사용자 응답 DTO")
public class UserResponse {

    @Schema(description = "사용자 ID", example = "1")
    private Long id;

    @Schema(description = "사용자 이름", example = "홍길동")
    private String name;

    @Schema(description = "이메일 주소", example = "hong@example.com")
    private String email;

    @Schema(description = "생성 일시", example = "2025-01-15T10:30:00")
    private LocalDateTime createdAt;

    // getter, setter 생략
}

김개발 씨는 Swagger UI에서 POST API를 클릭했습니다. 요청 바디 섹션에는 그냥 빈 JSON 객체만 표시되었습니다.

"사용자가 어떤 필드를 보내야 하는지 어떻게 알죠?" 이런 상태로는 프론트엔드 개발자가 계속 질문할 수밖에 없었습니다. @Schema 어노테이션이란 무엇일까요?

쉽게 비유하자면, @Schema는 제품 상세 스펙 시트와 같습니다. 노트북을 살 때 단순히 "노트북"이라는 이름만 보고 구매하지 않습니다.

CPU 종류, 메모리 용량, 화면 크기, 무게 등 상세 스펙을 확인합니다. 마찬가지로 @Schema는 JSON 객체의 각 필드가 어떤 타입인지, 어떤 제약 조건이 있는지, 어떤 예시 값을 사용할 수 있는지 명확히 보여줍니다.

스키마 정의 없이 API를 만들면 어떻게 될까요? springdoc-openapi는 Java 타입 정보만으로 기본 스키마를 생성합니다.

String 타입은 문자열로, Integer 타입은 숫자로 표시됩니다. 하지만 이것만으로는 부족합니다.

이름 필드가 최소 몇 글자여야 하는지, 이메일 형식인지 일반 문자열인지, 필수 필드인지 선택 필드인지 알 수 없습니다. 클라이언트 개발자는 실제로 API를 호출해보고 에러를 받아본 후에야 요구 사항을 파악할 수 있습니다.

"아, 이름이 필수였구나. 이메일 형식이 잘못됐구나." 이런 시행착오는 개발 시간을 낭비하게 만듭니다.

바로 이런 문제를 해결하기 위해 @Schema 어노테이션이 제공됩니다. @Schema를 사용하면 필드별 상세 정보를 모두 문서화할 수 있습니다.

description으로 필드의 용도를 설명하고, example로 실제 사용 가능한 값을 보여줍니다. 사용자는 Swagger UI에서 이 예시 값을 그대로 복사해서 테스트할 수 있습니다.

requiredMode를 설정하면 필수 필드와 선택 필드를 구분할 수 있습니다. Swagger UI에서 필수 필드는 별표로 표시되어 한눈에 알아볼 수 있습니다.

minLength, maxLength, minimum, maximum 같은 속성으로 유효성 검증 조건도 명시할 수 있습니다. Bean Validation 어노테이션과의 연동도 강력한 기능입니다.

@NotBlank, @Email, @Size 같은 Bean Validation 어노테이션을 함께 사용하면, springdoc-openapi가 자동으로 이를 인식하여 스키마에 반영합니다. 코드에서 검증하는 조건이 문서에도 그대로 표시되므로 항상 동기화됩니다.

위의 코드를 살펴보겠습니다. 먼저 DTO 클래스 자체에 @Schema 어노테이션을 붙여 전체 설명을 추가합니다.

"사용자 생성 요청 DTO"라는 설명이 Swagger UI의 스키마 섹션에 표시됩니다. 각 필드에도 @Schema를 붙입니다.

name 필드는 "사용자 이름"이라는 설명과 함께 "홍길동"이라는 예시 값을 제공합니다. requiredMode를 REQUIRED로 설정하여 필수 필드임을 명시합니다.

minLengthmaxLength로 문자열 길이 제약을 표현합니다. email 필드는 format = "email"로 이메일 형식임을 나타냅니다.

Swagger UI는 이를 인식하여 입력 필드에 이메일 검증을 추가할 수 있습니다. @Email Bean Validation 어노테이션과 함께 사용하면 코드와 문서가 완벽히 일치합니다.

age 필드는 requiredMode를 명시하지 않았으므로 선택 필드입니다. minimummaximum으로 유효한 범위를 정의합니다.

1세에서 150세까지만 허용한다는 것을 명확히 보여줍니다. 응답 DTO인 UserResponse에도 동일하게 @Schema를 적용합니다.

id, name, email, createdAt 각 필드의 의미와 예시 값을 제공합니다. 클라이언트 개발자는 이 스키마를 보고 정확히 어떤 형식의 응답이 오는지 알 수 있습니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 전자상거래 주문 API를 개발한다고 가정해봅시다.

주문 요청은 복잡한 중첩 구조를 가집니다. 주문자 정보, 배송지 정보, 주문 상품 목록, 결제 정보 등이 포함됩니다.

각 필드마다 @Schema를 적용하면, 프론트엔드 개발자는 어떤 구조의 JSON을 만들어야 하는지 정확히 알 수 있습니다. 또한 @Schema로 정의한 스키마는 TypeScript 타입 생성기에 활용할 수 있습니다.

openapi-generator 같은 도구로 OpenAPI 스펙에서 자동으로 TypeScript 인터페이스를 생성하면, 프론트엔드에서도 타입 안정성을 확보할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 @Schema와 Bean Validation의 조건을 다르게 설정하는 것입니다. @Schema에는 maxLength = 50이라고 적어놓고, 실제 검증에서는 @Size(max = 100)을 사용하면 문서와 동작이 달라집니다.

항상 두 가지를 일치시켜야 합니다. 또한 민감한 정보는 accessMode를 설정하여 문서에서 숨길 수 있습니다.

비밀번호 같은 필드는 accessMode = Schema.AccessMode.WRITE_ONLY로 설정하면 응답 예시에는 표시되지 않습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 안내대로 DTO에 @Schema를 추가했습니다. Swagger UI를 새로고침하니 요청 바디와 응답 바디의 구조가 완벽하게 표시되었습니다.

각 필드의 설명, 타입, 필수 여부, 예시 값까지 모두 보였습니다. "이제 프론트엔드 팀에서 질문이 없겠는데요?" @Schema 어노테이션을 제대로 활용하면 데이터 구조를 명확히 전달할 수 있습니다.

여러분도 오늘 배운 내용을 바탕으로 모든 DTO에 상세한 스키마를 정의해 보세요.

실전 팁

💡 - @Schema와 Bean Validation 조건을 항상 일치시키세요

  • 민감한 정보는 accessMode로 숨길 수 있습니다
  • example 값은 실제로 동작하는 현실적인 값으로 작성하세요

6. API 그룹화

프로젝트가 커지면서 API가 수십 개로 늘어났습니다. Swagger UI를 열어보니 모든 API가 한 페이지에 나열되어 있어 찾기가 어려웠습니다.

"API를 카테고리별로 나눌 수는 없을까요?" 김개발 씨가 물었습니다. 박시니어 씨가 고개를 끄덕이며 답했습니다.

"API 그룹화 기능을 사용하면 됩니다."

API 그룹화는 관련된 API들을 논리적인 단위로 묶어서 문서를 체계적으로 구성하는 기능입니다. 마치 도서관에서 책을 주제별로 분류하듯이, 사용자 관리 API, 상품 관리 API, 주문 관리 API를 각각 별도 그룹으로 나눌 수 있습니다.

이를 통해 사용자는 원하는 API를 빠르게 찾고 이해할 수 있습니다.

다음 코드를 살펴봅시다.

// application.yml - 그룹 정의
springdoc:
  group-configs:
    - group: user-api
      display-name: "사용자 관리 API"
      paths-to-match:
        - /api/users/**
    - group: product-api
      display-name: "상품 관리 API"
      paths-to-match:
        - /api/products/**
    - group: admin-api
      display-name: "관리자 API"
      paths-to-match:
        - /api/admin/**

// Java 코드로 그룹 정의
@Configuration
public class OpenAPIConfig {

    @Bean
    public GroupedOpenApi userApi() {
        return GroupedOpenApi.builder()
            .group("user-api")
            .displayName("사용자 관리 API")
            .pathsToMatch("/api/users/**")
            .build();
    }

    @Bean
    public GroupedOpenApi productApi() {
        return GroupedOpenApi.builder()
            .group("product-api")
            .displayName("상품 관리 API")
            .pathsToMatch("/api/products/**")
            .addOpenApiCustomizer(openApi ->
                openApi.info(new Info()
                    .title("상품 API")
                    .version("v1.0")
                    .description("상품 CRUD 및 검색 기능을 제공합니다")))
            .build();
    }

    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
            .group("admin-api")
            .displayName("관리자 API")
            .pathsToMatch("/api/admin/**")
            .packagesToScan("com.example.admin")
            .build();
    }
}

김개발 씨는 Swagger UI를 스크롤하며 한숨을 쉬었습니다. 50개가 넘는 API가 한 페이지에 모두 나열되어 있었습니다.

사용자 API와 상품 API, 관리자 API가 뒤섞여 있어서 원하는 API를 찾으려면 한참을 검색해야 했습니다. "이렇게 많으면 나도 헷갈리는데, 다른 팀 사람들은 어떻게 찾지?" API 그룹화란 무엇일까요?

쉽게 비유하자면, API 그룹화는 백화점의 층별 안내와 같습니다. 백화점에서 모든 상품을 1층에 쌓아놓으면 손님이 원하는 물건을 찾기 어렵습니다.

하지만 1층은 화장품, 2층은 여성 의류, 3층은 남성 의류처럼 나누면 손님은 원하는 층으로 바로 이동할 수 있습니다. 마찬가지로 API 그룹화는 관련된 API를 논리적인 단위로 묶어서 사용자가 쉽게 탐색할 수 있게 해줍니다.

그룹화 없이 모든 API를 한 곳에 나열하면 어떻게 될까요? 프로젝트 초기에는 API가 몇 개 되지 않아 문제없습니다.

하지만 프로젝트가 성장하면 수십, 수백 개의 엔드포인트가 생깁니다. Swagger UI 페이지는 끝없이 길어지고, 원하는 API를 찾으려면 검색 기능에 의존해야 합니다.

더 큰 문제는 역할에 따라 필요한 API가 다르다는 점입니다. 프론트엔드 개발자는 사용자 API만 필요한데, 관리자 API와 내부 시스템 API까지 모두 보입니다.

관리자는 관리자 API만 보고 싶은데, 일반 사용자 API가 섞여 있어 혼란스럽습니다. 바로 이런 문제를 해결하기 위해 API 그룹화 기능이 제공됩니다.

API 그룹화를 사용하면 관련된 엔드포인트를 하나의 문서로 묶을 수 있습니다. 사용자 관리 API, 상품 관리 API, 주문 관리 API를 각각 별도 그룹으로 만들면, Swagger UI 우측 상단의 드롭다운에서 원하는 그룹을 선택할 수 있습니다.

경로 기반 그룹화가 가장 일반적입니다. /api/users로 시작하는 모든 엔드포인트는 사용자 API 그룹으로, /api/products로 시작하는 엔드포인트는 상품 API 그룹으로 묶습니다.

경로 패턴만 지정하면 자동으로 분류됩니다. 패키지 기반 그룹화도 가능합니다.

com.example.user 패키지의 모든 Controller는 사용자 API로, com.example.admin 패키지는 관리자 API로 분류할 수 있습니다. 이는 패키지 구조가 명확할 때 유용합니다.

각 그룹마다 독립적인 메타 정보를 설정할 수 있습니다. 제목, 설명, 버전, 담당자 정보를 그룹별로 다르게 지정하면, 마치 별도의 API 문서처럼 보입니다.

위의 코드를 살펴보겠습니다. 먼저 application.yml 방식입니다.

springdoc.group-configs 아래에 그룹 목록을 정의합니다. 각 그룹은 group ID, display-name 표시 이름, paths-to-match 경로 패턴을 가집니다.

설정 파일만으로 간단하게 그룹화할 수 있습니다. Java 코드 방식은 더 유연합니다.

GroupedOpenApi Bean을 생성하여 각 그룹을 정의합니다. pathsToMatch로 경로 패턴을 지정하거나, packagesToScan으로 패키지를 지정할 수 있습니다.

addOpenApiCustomizer를 사용하면 그룹별로 OpenAPI 스펙을 커스터마이징할 수 있습니다. 상품 API 그룹에는 "상품 API"라는 별도 제목과 설명을 추가했습니다.

이를 통해 각 그룹이 독립적인 문서처럼 보입니다. 관리자 API는 경로와 패키지를 모두 사용했습니다.

/api/admin으로 시작하면서 com.example.admin 패키지에 있는 Controller만 포함됩니다. 이중 필터링으로 더 정확한 그룹화가 가능합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 B2B와 B2C API를 모두 제공하는 플랫폼을 생각해봅시다.

B2C 사용자는 상품 조회, 주문, 결제 API만 필요합니다. B2B 고객사는 대량 주문, 재고 조회, 정산 API를 사용합니다.

두 그룹을 분리하면 각 사용자는 자신에게 필요한 API만 보게 됩니다. 또한 버전별 그룹화도 가능합니다.

/api/v1은 v1 그룹으로, /api/v2는 v2 그룹으로 나누면, 구버전과 신버전 API를 동시에 문서화하면서도 혼란을 방지할 수 있습니다. 외부 공개용 API와 내부 시스템용 API를 분리하는 것도 좋은 전략입니다.

외부 API는 public-api 그룹으로 공개하고, 내부 API는 internal-api 그룹으로 접근을 제한할 수 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 그룹을 너무 많이 만드는 것입니다. 그룹이 10개, 20개로 늘어나면 오히려 찾기가 더 어려워집니다.

논리적으로 명확한 기준으로 5개 이하의 그룹으로 나누는 것이 적절합니다. 또한 하나의 Controller가 여러 그룹에 중복으로 포함되지 않도록 주의해야 합니다.

경로 패턴이 겹치면 같은 API가 여러 그룹에 나타나서 혼란을 줄 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.

박시니어 씨의 도움으로 API를 3개 그룹으로 나눴습니다. 사용자 API, 상품 API, 관리자 API로 분류하니 Swagger UI가 훨씬 깔끔해졌습니다.

우측 상단 드롭다운에서 원하는 그룹을 선택하면 해당 API만 표시되었습니다. "이제 다른 팀에서도 쉽게 찾을 수 있겠어요!" API 그룹화를 제대로 활용하면 대규모 프로젝트에서도 체계적인 문서를 유지할 수 있습니다.

여러분도 오늘 배운 내용을 바탕으로 API를 논리적으로 분류해 보세요.

실전 팁

💡 - 그룹은 5개 이하로 유지하여 복잡도를 낮추세요

  • 경로 패턴이 겹치지 않도록 주의하세요
  • 외부 공개 API와 내부 API를 반드시 분리하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Spring#OpenAPI#Swagger#springdoc#APIDocumentation

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스