
API 문서화: 엑셀로 정리하다 싸움 난다
백엔드: 'API 다 만들었어요.' 프론트엔드: '어떻게 써요?' 이 지겨운 대화를 끝내주는 Swagger(OpenAPI)의 마법.

백엔드: 'API 다 만들었어요.' 프론트엔드: '어떻게 써요?' 이 지겨운 대화를 끝내주는 Swagger(OpenAPI)의 마법.
내 서버는 왜 걸핏하면 뻗을까? OS가 한정된 메모리를 쪼개 쓰는 처절한 사투. 단편화(Fragmentation)와의 전쟁.

미로를 탈출하는 두 가지 방법. 넓게 퍼져나갈 것인가(BFS), 한 우물만 팔 것인가(DFS). 최단 경로는 누가 찾을까?

프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

이름부터 빠릅니다. 피벗(Pivot)을 기준으로 나누고 또 나누는 분할 정복 알고리즘. 왜 최악엔 느린데도 가장 많이 쓰일까요?

처음 백엔드를 혼자 만들었을 때의 일이다. 3주 동안 밤을 새워가며 REST API를 완성했다. 자랑스러운 마음으로 프론트엔드 개발자(동료)에게 슬랙 메시지를 보냈다.
"API 다 만들었어요! 테스트 서버:
https://api.example.com"
30분 후 슬랙 알림:
"어떻게 쓰는 건데요? 엔드포인트가 뭐고, 파라미터는 뭐예요?"
당황한 나는 급하게 엑셀 파일을 만들었다.
| 엔드포인트 | 메서드 | 파라미터 | 설명 |
|---|---|---|---|
/users | GET | - | 유저 목록 |
/users/{id} | GET | id (int) | 유저 상세 |
이 파일을 슬랙에 올렸다. "이거 보고 써주세요!"
2주 뒤, 또 슬랙 알림:
"문서에는
userId라고 되어 있는데 왜user_id에러가 나요?"
나는 일주일 전에 API 스펙을 수정했는데, 엑셀 문서는 업데이트를 깜빡했던 거였다. 프론트엔드 개발자는 2시간 동안 "왜 안 되지?" 하며 디버깅했다.
그날 나는 깨달았다. "엑셀 문서는 거짓말쟁이다."
백엔드 개발자의 일상은 이렇다:
왜 깜빡할까? 나는 이렇게 이해했다. 문서와 코드가 분리되어 있기 때문이다.
UserController.javaAPI명세서_v2.xlsx (다른 폴더)코드를 수정할 때 문서 파일이 눈에 안 보인다. 심지어 문서가 어디 있는지도 까먹는다.
내가 경험한 최악의 상황:
API명세서_최종.xlsx
API명세서_최종_진짜최종.xlsx
API명세서_최종_진짜최종_이번꺼.xlsx
API명세서_6월버전.xlsx
폴더에 이런 파일이 5개 있었다. 어떤 게 진짜 최신인지 아무도 모른다.
이 문제를 해결하려면 문서와 코드를 한 곳에 두어야 한다. 그게 바로 Swagger (OpenAPI) 방식이었다.
// UserController.java (코드)
@GetMapping("/users/{id}")
public User getUser(@PathVariable String id) {
return userService.findById(id);
}
// API명세서.xlsx (별도 파일)
엔드포인트: /users/{id}
메서드: GET
파라미터: id (String)
문제: 코드 수정 → 문서 수정 깜빡 → 거짓말 문서였다.
// UserController.java (코드 + 문서)
@Operation(summary = "유저 조회", description = "ID로 유저 정보를 가져옵니다")
@ApiResponse(responseCode = "200", description = "성공")
@ApiResponse(responseCode = "404", description = "유저 없음")
@GetMapping("/users/{id}")
public User getUser(@PathVariable String id) {
return userService.findById(id);
}
어노테이션(@Operation)이 문서다.
코드를 수정하면 문서도 자동으로 바뀐다.
Swagger는 이 어노테이션을 읽어서 예쁜 웹사이트를 자동 생성한다:
https://api.example.com/swagger-ui
Swagger UI에 접속하면 다음을 볼 수 있다:
GET /users/{id} - 유저 조회
POST /users - 유저 생성
DELETE /users/{id} - 유저 삭제
코드에서 @GetMapping, @PostMapping을 찾아서 자동으로 목록을 만들어준다.
Path Parameters:
- id (String, required): 유저 ID
Response:
{
"id": "user123",
"name": "홍길동",
"email": "hong@example.com"
}
User 클래스의 필드를 읽어서 자동으로 예시 응답을 보여준다.
이게 제일 강력했다. 웹 브라우저에서 바로 API를 실행할 수 있다.
id 입력란에 "user123" 입력{
"id": "user123",
"name": "홍길동",
"email": "hong@example.com"
}
포스트맨(Postman) 필요 없다.
내 블로그 백엔드(Next.js API Routes)에 Swagger를 붙였을 때:
프론트엔드 개발자(내 동료):
"POST /posts 엔드포인트 body 형식이 뭐예요?"
나 (백엔드):
"잠깐만요... (코드 뒤적뒤적)
title,content,tags필드 보내주세요."
30분 후:
"tags가 string이에요 array예요?"
나:
"아... array입니다.
["tag1", "tag2"]이렇게요."
이런 대화가 하루에 5번이었다.
/**
* @swagger
* /api/posts:
* post:
* summary: 블로그 글 생성
* requestBody:
* content:
* application/json:
* schema:
* type: object
* properties:
* title:
* type: string
* content:
* type: string
* tags:
* type: array
* items:
* type: string
*/
export default function handler(req, res) { ... }
프론트엔드 개발자:
"Swagger 문서 보고 작업했어요. 바로 됐습니다."
질문 0개였다.
| 항목 | Swagger 없음 | Swagger 있음 |
|---|---|---|
| 문서 작성 시간 | 1시간 (엑셀 정리) | 10분 (어노테이션) |
| 문서 업데이트 | 수동 (자주 깜빡) | 자동 (코드와 동기화) |
| 테스트 방법 | Postman 켜기 | 웹 브라우저에서 바로 |
| 프론트-백 커뮤니케이션 | 슬랙 질문 폭탄 | 문서 보고 끝 |
| 신규 개발자 온보딩 | 1주일 (API 설명) | 1일 (Swagger 링크) |
실제 내가 회사에서 쓴 설정이다.
build.gradle)implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
@RestController
@RequestMapping("/api/users")
@Tag(name = "User API", description = "유저 관리 API")
public class UserController {
@Operation(
summary = "유저 목록 조회",
description = "모든 유저의 목록을 페이지네이션으로 반환합니다"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "500", description = "서버 오류")
})
@GetMapping
public List<User> getUsers(
@Parameter(description = "페이지 번호 (0부터 시작)")
@RequestParam(defaultValue = "0") int page
) {
return userService.findAll(page);
}
}
http://localhost:8080/swagger-ui/index.html
끝이다. 문서가 자동으로 생성된다.
@Schema(description = "유저 생성 요청")
public class CreateUserRequest {
@Schema(description = "유저 이름", example = "홍길동")
private String name;
@Schema(description = "이메일", example = "hong@example.com")
private String email;
}
Swagger UI에서 "Try it out"을 누르면 예시 값이 자동으로 채워진다.
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
)
);
}
}
이제 Swagger UI 상단에 "Authorize" 버튼이 생긴다. JWT 토큰을 한 번 입력하면 모든 API 테스트에 자동으로 헤더가 붙는다.
운영 환경(Production)에서는 Swagger를 끄고 싶을 때:
# application-prod.yml
springdoc:
swagger-ui:
enabled: false
보안상 API 스펙을 외부에 공개하고 싶지 않을 때 사용한다.
API가 복잡하면 어노테이션이 코드보다 길어진다.
@Operation(...)
@ApiResponse(...)
@ApiResponse(...)
@Parameter(...)
@Parameter(...)
@GetMapping("/users")
public List<User> getUsers(...) {
// 실제 로직 3줄
}
해결책: 별도 OpenAPI YAML 파일 작성 (Code-First가 아닌 Contract-First).
Swagger는 서버를 띄워야 문서를 볼 수 있다. 컴파일 타임에는 확인 불가였다.
해결책: Postman Collection / Insomnia 사용 (파일로 관리 가능).
처음 내가 엑셀로 API 문서를 만들었을 때는 "이게 최선"이라고 생각했다. 하지만 코드와 문서가 따로 놀면 문서는 항상 거짓말을 한다는 걸 배웠다.
Swagger를 도입한 후:
아직도 엑셀/워드로 API 명세서를 작성하고 계신가요? 오늘 당장 Swagger를 도입하세요. 평화가 찾아옵니다.
REST API만 문서화하면 좋겠지만, 세상은 더 복잡하다.
"결제 완료되면 제가 웹훅(Webhook) 쏴드릴게요"라고 말만 하면 안 된다. 받는 사람 입장에서 어떤 JSON이 날아올지 모르면 서버를 못 만든다.
GraphQL은 스키마(Schema) 자체가 강력한 문서다. 하지만 "어떤 필드를 써야 하는지" 설명이 부족하다.
.proto 파일 자체가 IDL(Interface Definition Language)이라 문서 역할을 하지만, 이걸 읽을 줄 아는 사람은 드물다.
protoc-gen-doc 플러그인을 쓰면 .proto 파일을 예쁜 HTML이나 마크다운으로 변환해준다.백엔드 개발자 사이의 영원한 난제다. 실무에서도 자주 논의되는 주제다.
| 특징 | Swagger (OpenAPI) | Spring REST Docs |
|---|---|---|
| 작성 위치 | 프로덕션 코드 (Controller) | 테스트 코드 (Test Code) |
| 장점 | 적용이 쉽고, UI가 화려하며 테스트 가능 | 제품 코드에 영향 없음(Zero Pollution). 테스트 통과해야 문서 나옴(신뢰성 100%). |
| 단점 | 어노테이션 도배로 코드가 지저분해짐. | 작성이 어렵고 귀찮음(Asciidoc 문법). |
| 추천 | 스타트업, 초기 개발, 내부 API | 대규모 시스템, 외부 공개용 API, 품질 중요 |
내 결론: 초기에는 Swagger로 빠르게 치고 나가고, 시스템이 안정화되고 외부 공개가 필요해지면 REST Docs로 전환하거나 병행하는 전략이 좋다고 생각한다. 최근에는 Swagger UI + REST Docs를 합쳐서 쓰는 라이브러리(com.epages.restdocs-api-spec)도 유행이다.
문서가 "예쁘게" 나오는 것보다 중요한 원칙들을 정리해본다.
API는 살아있는 생물이라 계속 바뀐다.
/api/v1/users (가장 직관적, 추천)Accept: application/vnd.company.v1+json (깔끔하지만 테스트하기 귀찮다)"성공하면 뭐 주나요?"는 다들 잘 쓴다. 하지만 프론트엔드 개발자가 진짜 궁금한 건 "망했을 때 뭐 주나요?"였다.
모든 API의 응답 껍데기(Envelope)를 통일하자.
{
"code": "SUCCESS",
"message": "요청 성공",
"data": { ...실제 데이터... }
}
어떤 API는 배열을 바로 주고([]), 어떤 건 객체({})를 주면 프론트엔드에서 공통 처리를 못 한다.
최근 테크 기업들은 "문서도 코드다"라는 철학을 실천하고 있다. 단순히 Swagger를 쓰는 것을 넘어서, 전체 기술 문서를 GitHub에서 관리하는 것이다.
git blame으로 범인을 찾을 수 있다.push)하면 Vercel이나 Netlify가 알아서 정적 사이트로 빌드해서 배포해준다.결국 "개발자가 가장 익숙한 도구(IDE, Git, Markdown)로 문서를 쓰게 하자"가 핵심이다.
워드나 컨플루언스(Confluence)는 개발자에게 너무 무겁고, 흐름을 끊는다. 게다가 마크다운으로 작성하면 코드 하이라이팅이 예쁘게 들어가서 읽기도 좋다. 나는 예쁜 코드를 보면 기분이 좋아지고, 기분이 좋아지면 문서를 더 잘 쓰게 되는 선순환을 경험했다. (진짜다.)