1. 도입 배경
프로젝트를 진행하면서 API가 점점 많아지다 보니 이런 문제가 생겼다.
- API 요청/응답 형식이 매번 헷갈림
- 프론트와 협업할 때 명세 전달이 불편함
- 테스트를 Postman으로 계속 날리는 게 번거로움
그래서 Swagger(OpenAPI)를 도입했다.
2. Swagger 적용 과정
이번 프로젝트에서는 아래 라이브러리를 사용했다.
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
설치 후 서버를 실행하면
http://localhost:8080/swagger-ui/index.html
로 접속 가능하다.
여기까지는 정말 쉽다.
문제는 여기서부터 시작이었다.
3. 가장 큰 문제: 403 에러 (Undocumented)
Swagger에서 API를 실행하면 이런 에러가 발생했다.
403 Forbidden
Undocumented
처음엔 원인을 몰라서 한참 헤맸다.
4. 원인 분석
문제의 핵심은 Spring Security + JWT 필터였다.
내 구조는 이런 흐름이었다:
Swagger → API 요청 → JwtAuthenticationFilter → 인증 검사
근데 Swagger에서는 토큰을 안 넣고 요청을 보내기 때문에 JWT 필터에서 걸려버린다.
즉, Swagger 요청도 “인증 필요 요청”으로 처리된 것
5. 해결 방법
1) Swagger 경로 허용
SecurityConfig에서 Swagger 관련 경로를 풀어줘야 한다.
http
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/swagger-ui/**",
"/v3/api-docs/**"
).permitAll()
.anyRequest().authenticated()
);
2) Swagger에서 JWT 사용 설정
단순히 열어주는 것만으로 끝이 아니다.
실제로 로그인 후 토큰을 넣어서 테스트해야 한다.
@SecurityRequirement(name = "bearerAuth")
그리고 Swagger 설정에 Bearer 인증 추가
@OpenAPIDefinition(
security = @SecurityRequirement(name = "BearerAuth")
)
@SecurityScheme(
name = "BearerAuth",
type = SecuritySchemeType.HTTP,
scheme = "bearer",
bearerFormat = "JWT"
)
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("BenePicker API")
.description("BenePicker 백엔드 API 문서")
.version("v1.0.0")
.contact(new Contact()
.name("BenePicker Team")
.email("test@example.com"))
.license(new License()
.name("Apache 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0")))
.externalDocs(new ExternalDocumentation()
.description("프로젝트 문서")
.url("https://example.com"));
}
}
이후 Swagger UI에서 "Authorize" 버튼으로 토큰 입력 가능
3) JwtFilter 디버깅 로그 추가
처음엔 왜 막히는지 몰랐는데 로그 찍으니까 바로 보였다.
System.out.println("Authorization: " + authorization);
여기서 알게 된 사실
- Swagger 요청에는 Authorization 헤더 없음
- 그래서 필터에서 바로 탈락
6. 동작 화면

7. 깨달은 점
이번 경험에서 느낀 핵심은 다음과 같다.
1) Swagger는 그냥 “문서 도구”가 아니다
→ 실제 요청을 보내는 클라이언트다
2) Security와 붙으면 반드시 충돌한다
→ 인증/인가 흐름을 이해해야 한다
3) 로그는 무조건 찍어야 한다
→ 감으로 디버깅하면 시간만 날린다
8. 다음 개선 방향
- 공통 응답 구조 (CommonResponse) Swagger에 명세 추가
- 에러 코드 문서화
- API 태그 정리 (@Tag, @Operation)
'백엔드 > 회고' 카테고리의 다른 글
| [Spring] JWT 로그아웃 기본 코드 분석 (0) | 2026.04.03 |
|---|---|
| [Spring] MyBatis vs JPA + XML vs Annotation, 직접 써보며 느낀 차이 (0) | 2026.04.03 |
| [GitHub] 협업 효율을 높여준 PR & Issue 템플릿 도입 (0) | 2026.03.31 |