개념
QA가 왜 붙이는 방법까지 알아야 하나
붙이는 일 자체는 대개 백엔드 개발자가 한다. 그런데 QA가 원리를 알면 세 가지가 달라진다. 문서가 부실할 때 "무엇을 어디에 적어달라"고 정확히 요청 할 수 있고, 문서가 없는 팀에 도입을 설득 할 수 있고, 운영 환경에 열려 있는 Swagger 같은 위험을 먼저 발견 할 수 있다. 세 번째는 실제로 보안 지적 사항으로 자주 올라온다.
자동 생성의 원리 — 코드가 곧 문서다
Swagger UI가 읽는 스펙은 사람이 YAML을 손으로 쓰는 것이 아니라 서버 코드에서 생성 되는 것이 일반적이다(코드 우선 방식).
컨트롤러 코드 + 어노테이션/타입 ──▶ /v3/api-docs (OpenAPI JSON)
│
▼
Swagger UI 화면 (/swagger-ui.html)여기서 QA가 기억할 원칙 하나가 나온다. 화면이 부실한 것은 도구
탓이 아니라 코드에 정보가 없기 때문이다. "설명을 좀 더 써주세요"는
막연하지만, "이 DTO 필드에 @Schema(description=..., example=...)을
달아주세요"는 바로 처리 가능한 요청이 된다.
반대 방향인 설계 우선(design-first) 도 있다. 스펙 YAML을 먼저 합의하고 그로부터 코드 뼈대를 생성하는 방식으로, 모듈 19(레슨 3)의 Apidog이 이 흐름에 가깝다. 어느 쪽이든 스펙이 하나뿐이어야 한다 는 원칙은 같다.
Spring Boot — springdoc-openapi
Spring Boot 3.x에서는 springdoc-openapi 2.x를 쓴다(구버전 Springfox는 Spring Boot 3과 호환되지 않아 마이그레이션 대상이다).
// build.gradle
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.0'의존성만 넣으면 기본 주소가 생긴다.
스펙(JSON) http://localhost:8080/v3/api-docs
화면 http://localhost:8080/swagger-ui.html (→ /swagger-ui/index.html 로 이동)레슨 6에서 본 Authorize 버튼은 보안 스킴을 등록해야 생긴다.
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI api() {
SecurityScheme bearer = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT");
return new OpenAPI()
.info(new Info().title("주문 API").version("v1"))
.components(new Components().addSecuritySchemes("bearerAuth", bearer))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"));
}
}엔드포인트·필드 설명은 어노테이션으로 붙인다.
@Operation(summary = "주문 단건 조회", description = "주문 ID로 주문 상세를 조회한다")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 주문"),
@ApiResponse(responseCode = "403", description = "다른 사용자의 주문")
})
@GetMapping("/orders/{id}")
public OrderResponse get(@Parameter(description = "주문 ID", example = "1001") @PathVariable Long id) { ... }QA가 리뷰에서 가장 자주 요청하게 되는 것이 @ApiResponses다.
성공 응답만 적힌 문서는 절반짜리다 — 실패 경로가 문서에 없으면
프런트엔드는 에러 처리를 상상으로 만든다.
FastAPI — 설정이 아예 없다
FastAPI는 타입 힌트와 Pydantic 모델에서 스펙을 만들어내므로 추가 라이브러리가 필요 없다. 기본으로 세 주소가 열린다.
/openapi.json 스펙
/docs Swagger UI
/redoc Redoc (읽기 전용 문서)class OrderResponse(BaseModel):
id: int
status: Literal["PAID", "SHIPPED", "CANCELED"]
total_price: int = Field(..., example=9000, description="쿠폰 적용 후 결제 금액")
@app.get("/orders/{order_id}", response_model=OrderResponse, summary="주문 단건 조회")
def get_order(order_id: int): ...Literal이 스펙의 enum으로 그대로 나간다는 점을 눈여겨보자.
화면의 enum 목록은 곧 상태 전이 테스트의 입력 목록 이다(모듈 3
레슨 3의 상태 전이 기법과 바로 이어진다).
NestJS — 데코레이터와 CLI 플러그인
const config = new DocumentBuilder()
.setTitle('주문 API')
.setVersion('v1')
.addBearerAuth() // ← Authorize 버튼
.build();
SwaggerModule.setup('api-docs', app, SwaggerModule.createDocument(app, config));@ApiProperty({ example: 9000 }) 를 DTO에 붙이면 필드 설명이 채워지고,
nest-cli.json에 "plugins": ["@nestjs/swagger"] 를 넣으면 타입에서
상당 부분을 자동으로 뽑아준다.
붙였다고 끝이 아니다 — 문서 품질 점검표
도구를 켠 팀과 문서가 쓸 만한 팀은 다르다. QA가 스펙 리뷰에서 볼 항목은 다음과 같다.
- 엔드포인트마다 한 줄 설명(summary) 이 있는가 —
getOrder1같은 메서드명이 그대로 노출되고 있지 않은가 - 요청 필드의 필수 여부 가 표시돼 있는가
- example 값 이
string,0자리표시자가 아니라 현실적인가 -
enum값이 코드와 일치하는가(빠진 상태값이 없는가) - 에러 응답(400/401/403/404/409/500) 이 정의돼 있고 본문 형식이 일관적인가
- 인증 방식이 정의돼
Authorize버튼이 동작하는가 - 서버 목록에 개발·스테이징이 구분돼 있는가
- 더 이상 쓰지 않는 API가 남아 있지 않은가(
@Deprecated표시) - 페이지네이션·정렬 파라미터의 기본값과 최댓값이 적혀 있는가
이 점검표는 모듈 12(레슨 4)의 계약 테스트와 짝이다. 계약 테스트는 "스펙과 실제가 같은가"를 보고, 이 점검표는 "스펙 자체가 쓸 만한가" 를 본다. 스펙이 비어 있으면 계약 테스트도 검증할 것이 없다.
스펙 변화를 CI에서 감지하기
문서가 자동 생성된다는 말은 아무도 모르게 계약이 바뀔 수 있다 는 뜻이기도 하다. 개발자가 필드 하나를 지우면 문서도 조용히 따라 바뀐다.
그래서 스펙 파일을 저장소에 커밋해두고, 빌드 때 생성한 스펙과 비교해 차이를 리뷰에 노출시키는 방식을 쓴다.
# 실행 중인 앱에서 현재 스펙을 받아 저장소 사본과 비교
curl -s http://localhost:8080/v3/api-docs | jq -S . > build/openapi.json
diff <(jq -S . api/openapi.json) build/openapi.json || echo "스펙이 변경됨 — 리뷰 필요"여기서 한 걸음 더 가면 oasdiff 같은 도구로 호환성을 깨는 변경
(필수 필드 추가, 응답 필드 삭제, enum 값 제거)만 골라 파이프라인을
실패시킬 수 있다. 모듈 14에서 배운 "변경을 자동으로 알아채는 장치"를
API 계약에 적용한 형태다.
운영 환경에는 열어두지 않는다
Swagger UI는 모든 엔드포인트·파라미터·내부 필드명을 한 화면에 정리해주는 도구 다. 공격자에게도 그렇다. 그래서 운영 배포에서는 끄거나 인증 뒤에 두는 것이 기본이다.
# application-prod.yml
springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false주의할 점은 UI만 끄고 스펙 경로를 열어두면 의미가 없다 는 것이다.
/swagger-ui.html이 404여도 /v3/api-docs가 열려 있으면 정보는 그대로
새어 나간다. QA 점검 항목으로 넣어둘 만하다 — 운영 도메인에서 스펙
주소를 직접 호출해 보는 것까지가 한 세트다.
FastAPI는 FastAPI(docs_url=None, redoc_url=None, openapi_url=None) 로
같은 일을 한다. 완전히 닫는 대신 사내망·VPN·관리자 인증 뒤에 두는
절충도 흔하다.
Swagger UI 말고 다른 선택지
같은 스펙 파일을 읽는 다른 화면들도 있다. 팀이 이미 다른 것을 쓰고 있어도 당황할 필요가 없다 — 원본은 똑같은 OpenAPI 스펙이다.
| 도구 | 성격 | 언제 |
|---|---|---|
| Swagger UI | 실행 가능(Try it out), 가장 널리 쓰임 | 표준. QA가 직접 두드려야 할 때 |
| Redoc | 읽기 전용, 3단 레이아웃·검색이 강함 | 외부 공개 레퍼런스 문서 |
| Scalar | 최신 UI + 내장 API 클라이언트 | 새 프로젝트에서 점점 기본값이 되는 추세 |
| RapiDoc | 가벼운 웹 컴포넌트 | 사내 포털에 끼워 넣을 때 |
OpenAPI 3.1 을 쓰는 프로젝트라면 화면 지원 여부를 먼저 확인한다. Swagger UI는 3.1에서 일부 표현이 어색하게 나오는 경우가 보고돼 있어, 3.1 기반 신규 프로젝트가 Scalar·Redoc을 고르는 이유가 되기도 한다.
실무에서 왜 필요한가
문서가 없는 팀에서 QA가 겪는 손실은 눈에 잘 안 띄지만 크다. API 하나 확인할 때마다 개발자에게 물어보고, 답을 기다리고, 그 답이 슬랙 어딘가에 묻힌다. Swagger를 붙이는 일은 그 왕복을 없애는 인프라 작업 이고, QA는 그 효용을 가장 크게 체감하는 당사자이므로 도입을 제안하기에 가장 좋은 위치에 있다.
또한 이 레슨의 점검표는 QA가 코드를 못 읽어도 개발 품질에 개입할 수
있는 통로 다. "에러 응답이 문서에 없습니다", "enum에 EXPIRED가
빠졌습니다" 같은 지적은 구현 이전에 나올 수 있고, 그 시점이 가장 싸다.
운영 노출 점검은 그 자체로 보안 점검 항목이라 모듈 23(보안 테스트
도구)의 정보 노출 점검과도 이어진다.