Swagger는 HTTP API를 문서화하고 테스트하기 위한 도구와 명세 생태계다.
REST API 서버를 개발하다 보면 엔드포인트가 늘어나면서 문제가 생긴다.
GET /users
POST /users
GET /users/{id}
POST /login
POST /orders
이 API들이 어떤 파라미터를 받고, 어떤 응답을 반환하고, 인증은 어떻게 하는지 코드만 보고 파악하기는 점점 어려워진다.
Swagger를 사용하면 API의 구조를 명세로 정의하고, 이를 기반으로 사람이 읽을 수 있는 API 문서를 만들 수 있다. 대표적으로 Swagger UI를 사용하면 브라우저에서 API 목록을 확인하고 직접 요청까지 보내볼 수 있다.
여기서 Swagger와 OpenAPI를 구분할 필요가 있다.
OpenAPI는 API를 표현하기 위한 표준 명세이고, Swagger는 그 명세를 활용하는 여러 도구들의 이름이라고 이해하는 것이 좋다.
OpenAPI와 Swagger의 관계
과거에는 Swagger Specification이라는 이름으로 API 명세가 발전했지만 현재 표준의 이름은 OpenAPI Specification(OAS)이다.
구조를 단순화하면 다음과 같다.
OpenAPI Specification -> API 명세 작성 -> Swagger UI / Code Generator
Swagger UI -> API 문서 / 테스트
예를 들어 OpenAPI 문서에 다음과 같은 API를 정의할 수 있다.
paths:
/users:
get:
summary: Get users
responses:
'200':
description: Success
이 명세를 Swagger UI가 읽으면 브라우저에서 API 문서를 만들어준다.
따라서 Swagger를 이해할 때 Swagger 자체가 API 서버는 아니라는 점을 먼저 잡아두는 게 좋다.
Swagger는 API를 설명하고 확인하기 위한 도구다.
API 문서가 필요한 이유
API가 몇 개 없을 때는 문서의 필요성을 크게 느끼지 못할 수 있다.
하지만 프로젝트가 커지면 API를 사용하는 사람이 개발자 한 명이 아닐 수 있다.
Frontend -> GET /users / POST /login
Backend -> Database
프론트엔드 개발자는 다음 정보를 알아야 한다.
URL
HTTP Method
Request Parameter
Request Body
Response Body
Status Code
Authentication
예를 들어 로그인 API가 있다고 하자.
POST /login
이것만으로는 부족하다.
{
"email": "user@test.com",
"password": "1234"
}
응답이 무엇인지도 알아야 한다.
{
"accessToken": "...",
"refreshToken": "..."
}
Swagger/OpenAPI를 사용하면 이런 API 계약을 명세로 관리할 수 있다.
Swagger UI
Swagger에서 가장 익숙하게 접하게 되는 것이 Swagger UI다.
API 명세를 기반으로 웹 페이지를 만들어서 API 목록을 보여준다.
Users -> GET /users / POST /users
Authentication -> POST /login / POST /logout
Orders -> GET /orders / POST /orders
각 API를 펼치면 Request Parameter, Request Body, Response, Status Code 등을 확인할 수 있다.
그리고 Try it out 기능을 통해 실제 서버에 요청을 보내볼 수도 있다.
Swagger UI -> Try it out -> HTTP Request -> API Server -> HTTP Response
그래서 Swagger UI는 단순한 문서 페이지라기보다 API 탐색 및 간단한 테스트 도구로도 사용할 수 있다.
OpenAPI 문서
OpenAPI 문서는 보통 YAML 또는 JSON으로 작성한다.
간단한 예시는 다음과 같다.
openapi: 3.0.0
info:
title: User API
version: 1.0.0
paths:
/users/{id}:
get:
summary: Get user
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Success
이 문서에는 API의 구조가 들어간다.
OpenAPI -> info / paths / components
paths -> /users/{id} -> GET -> parameters / responses
components -> schemas / securitySchemes
실제 프로젝트에서는 API가 많아지기 때문에 components를 활용해 공통 Schema나 Security Scheme 등을 정의하는 경우가 많다.
Path
paths는 API Endpoint를 정의한다.
paths:
/users:
get:
...
post:
...
여기서 /users가 Path이고 get, post가 HTTP Method다.
/users -> GET / POST
이런 방식으로 API의 외부 인터페이스를 표현한다.
Parameter
API가 입력값을 필요로 하는 경우 Parameter를 정의한다.
예를 들어:
GET /users/123
에서 123이 사용자 ID라면 Path Parameter로 표현할 수 있다.
parameters:
- name: id
in: path
required: true
schema:
type: integer
Query Parameter도 정의할 수 있다.
GET /users?page=1&size=20
parameters:
- name: page
in: query
schema:
type: integer
- name: size
in: query
schema:
type: integer
이렇게 하면 Swagger UI에서 어떤 입력값이 필요한지 표시할 수 있다.
Request Body
POST나 PUT 같은 API에서는 Request Body를 사용하는 경우가 많다.
예를 들어 회원가입 API가 있다면:
{
"email": "user@test.com",
"password": "1234",
"nickname": "user"
}
OpenAPI에서는 Schema를 통해 이 구조를 표현할 수 있다.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UserCreateRequest'
그리고 실제 Schema는 별도로 관리할 수 있다.
components:
schemas:
UserCreateRequest:
type: object
required:
- email
- password
properties:
email:
type: string
password:
type: string
nickname:
type: string
이렇게 하면 여러 API에서 동일한 데이터 구조를 재사용할 수 있다.
Response
API가 반환하는 데이터도 명세에 포함할 수 있다.
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/User'
Status Code도 명시할 수 있다.
200 OK / 201 Created / 400 Bad Request / 401 Unauthorized / 403 Forbidden / 404 Not Found / 500 Internal Server Error
이렇게 해두면 API를 사용하는 입장에서 서버가 어떤 상황에서 어떤 응답을 반환하는지 파악하기 쉬워진다.
Schema
API에서 사용하는 데이터 구조를 정의하는 부분이 Schema다.
예를 들어 User라는 객체가 있다면:
User:
type: object
properties:
id:
type: integer
name:
type: string
email:
type: string
다른 API에서도 이를 참조할 수 있다.
$ref -> #/components/schemas/User
실제 프로젝트에서 API가 많아지면 Schema를 재사용하는 것이 상당히 중요해진다.
User -> GET /users / GET /users/{id} / POST /users / PUT /users/{id}
각 API의 Request와 Response 구조를 별도로 정의하면서도 공통 모델은 재사용할 수 있다.
Authentication
Swagger에서는 인증 방식도 명세로 표현할 수 있다.
대표적으로 Bearer Token을 사용하는 JWT 인증이 있다.
Authorization: Bearer <token>
OpenAPI에서는 Security Scheme을 정의한다.
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
그리고 API에 인증이 필요하다고 지정할 수 있다.
security:
- bearerAuth: []
Swagger UI가 이를 인식하면 인증 정보를 입력하고 API를 호출하는 형태로 사용할 수 있다.
다만 여기서 구분해야 할 것이 있다.
Swagger가 JWT 인증을 구현해주는 것은 아니다.
JWT 발급, 검증, 사용자 인증 등의 실제 비즈니스 로직은 서버 애플리케이션에서 구현한다.
Swagger/OpenAPI는 그 인증 방식과 API의 요구사항을 명세하고 테스트할 수 있도록 표현하는 역할을 한다.
Swagger와 Backend Framework
Swagger는 특정 서버 언어에 종속된 기술이 아니다.
Node.js, Java, C#, Python 등 다양한 환경에서 사용할 수 있다.
예를 들어 Node.js 서버라면 OpenAPI 문서를 작성하고 Swagger UI를 연결할 수 있다.
Node.js -> REST API / OpenAPI Specification -> Swagger UI
Spring 환경에서도 마찬가지다.
Spring Boot -> Controller / OpenAPI -> Swagger UI
프레임워크에 따라 코드에서 OpenAPI 문서를 자동 생성하도록 구성할 수도 있다.
Code First와 Design First
OpenAPI를 사용하는 방식은 크게 두 가지 관점으로 생각할 수 있다.
Code First
먼저 서버 코드를 작성하고 API 정보를 기반으로 OpenAPI 문서를 생성한다.
Controller -> API Implementation -> OpenAPI Document -> Swagger UI
개발 속도가 빠르고 기존 코드에 적용하기 편한 경우가 많다.
Design First
API 명세를 먼저 작성하고 그 명세를 기준으로 서버와 클라이언트를 개발한다.
OpenAPI -> Backend / Frontend / Client SDK
여러 팀이 하나의 API를 기준으로 개발하는 환경에서 유용하다.
API 계약을 먼저 합의할 수 있기 때문이다.
Swagger와 REST API의 관계
Swagger가 REST API를 만드는 것은 아니다.
REST API는 서버의 인터페이스 설계 방식이고, Swagger/OpenAPI는 그 인터페이스를 설명하고 관리하기 위한 명세 및 도구다.
관계를 정리하면:
REST API -> OpenAPI Specification -> Swagger UI
예를 들어 실제 서버가:
GET /users/123
를 제공한다면 OpenAPI에서는 이 API가 어떤 요청을 받고 어떤 응답을 반환하는지를 표현한다.
Swagger UI는 그 명세를 사람이 보기 편한 화면으로 만들어준다.
Swagger를 테스트 도구로만 사용하면 생기는 문제
Swagger UI에서 Try it out을 사용할 수 있기 때문에 Swagger를 단순한 API 테스트 도구로 생각하기 쉽다.
하지만 본질적인 역할은 API 계약을 명확하게 만드는 것에 더 가깝다.
예를 들어 다음 API가 있다고 하자.
POST /users
문서가 없다면 개발자는 서버 코드를 찾아서 다음을 직접 확인해야 한다.
Request Body / Response / Status Code / Required Field / Authentication
OpenAPI가 제대로 작성되어 있다면 이 정보가 하나의 명세에 모인다.
그래서 Swagger UI는 그 명세를 확인하기 위한 가장 편한 인터페이스 중 하나라고 보는 편이 맞다.
API 개발에서 Swagger를 사용하는 흐름
실제 개발 흐름을 단순화하면 다음과 같다.
API 설계 -> OpenAPI 정의 -> Backend 구현 -> Swagger UI -> API 확인 / 테스트 -> 문서 공유
또는 Code First 방식이라면:
Backend 구현 -> OpenAPI 생성 -> Swagger UI -> API 문서 / 테스트
팀 규모가 커질수록 API 명세가 코드와 따로 놀지 않도록 관리하는 것이 중요하다.
실무에서 주의할 부분
Swagger를 붙였다고 API 문서가 자동으로 좋은 상태가 되는 것은 아니다.
자동 생성된 문서는 실제 구현 코드의 구조를 보여주는 데는 편하지만, API를 사용하는 입장에서 필요한 설명이 부족할 수 있다.
예를 들어 다음 정보는 명세에 제대로 표현해두는 것이 좋다.
Field Description / Required / Optional / Example / Enum / Validation Rule / Status Code / Authentication / Error Response
특히 Error Response를 빼먹기 쉽다.
정상 응답만 정의해 놓으면 실제 클라이언트 개발에서는 결국 서버 코드를 다시 확인해야 한다.
200 -> 정상 응답
400 -> 잘못된 요청
401 -> 인증 실패
404 -> 리소스 없음
409 -> 충돌
500 -> 서버 오류
이런 부분까지 명확하게 정의해두면 API 문서의 가치가 올라간다.
Swagger와 API Gateway는 다르다
Swagger와 API Gateway도 혼동하기 쉽다.
API Gateway는 실제 네트워크 요청을 받아서 인증, 라우팅, Rate Limiting, 로깅 등의 처리를 하는 서버 인프라 계층이다.
반면 Swagger/OpenAPI는 API의 구조와 계약을 정의하고 이를 문서화하는 역할이다.
Client -> API Gateway -> Backend API
Swagger는 이 API의 구조를 설명한다.
OpenAPI -> Endpoint / Request / Response / Authentication / Schema
둘은 서로 대체 관계가 아니다.
전체 구조
지금까지의 내용을 하나로 묶으면 다음과 같다.
OpenAPI -> API Specification -> Swagger UI / Code Generator
Swagger UI -> API Documentation -> Try it out -> API Server
OpenAPI가 API의 계약을 표현하고, Swagger UI가 그 명세를 사람이 사용할 수 있는 문서 및 테스트 화면으로 보여준다.
여기에 실제 서버 구현이 붙는다.
Frontend -> API Server -> REST API
API Server -> OpenAPI Specification -> Swagger UI
정리
Swagger를 공부하면서 가장 먼저 구분해야 할 것은 Swagger와 OpenAPI의 관계다.
OpenAPI는 API를 표현하는 표준 명세이고, Swagger는 OpenAPI를 활용하는 대표적인 도구 생태계다.
개발 과정에서는 보통 다음 관계로 이해하면 충분하다.
REST API -> OpenAPI -> Swagger UI
OpenAPI에는 Endpoint, HTTP Method, Parameter, Request Body, Response, Schema, Authentication 같은 API의 계약이 들어간다.
Swagger UI는 이 명세를 기반으로 API 문서를 만들고 Try it out을 통해 실제 요청을 보내볼 수 있게 한다.
그리고 Swagger가 인증이나 비즈니스 로직을 구현하는 것은 아니다. JWT 검증이나 권한 처리는 서버가 담당하고, Swagger/OpenAPI는 그 API가 어떤 인증 방식을 사용하는지를 설명하는 역할을 한다.
결국 Swagger를 API 테스트 페이지 정도로만 생각하면 범위를 너무 좁게 보는 셈이다.
OpenAPI를 중심으로 API의 계약을 명확하게 정의하고, Swagger UI를 이용해 그 계약을 사람이 확인하고 테스트하는 구조로 이해하면 된다.