
백엔드 개발자가 API를 만들 때 가장 귀찮지만, 가장 중요한 일이 무엇일까? 바로 API 문서화다.
엑셀이나 노션에 "POST /api/login은 무슨 파라미터를 받는다..."라고 열심히 적어놔도, 코드를 한 줄 고치는 순간 그 문서까지도 고쳐야 한다. 그렇지 않으면 정보가 달라, 프론트엔드 개발자에게 혼란만 줄 뿐이다.
그래서 나는 이번 프로젝트에서 "코드가 바뀌면 문서도 알아서 바뀌는" 살아있는 문서를 만들기 위해 Swagger UI (Springdoc OpenAPI)를 도입했다.
Swagger(현재는 OpenAPI Specification)는 RESTful API를 위한 표준 인터페이스 명세다.
쉽게 말해 "우리 서버에는 이런 API가 있고, 이렇게 요청하면 이런 응답을 줘" 라는 설명서를 컴퓨터가 이해할 수 있는 JSON 형태로 만들어주고, 그걸 사람이 보기 편한 웹페이지로 보여주는 도구다.
예전에는 springfox를 많이 썼지만, Spring Boot 3.x 버전부터는 springdoc-openapi가 사실상의 표준이다.
dependencies {
// Spring Boot 3.x용 Swagger 라이브러리
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
}
의존성을 넣고 서버를 띄우면 바로 문서가 나온다. 하지만 실무 레벨에서는 부족하다.
특히 우리 프로젝트처럼 JWT 토큰 인증을 쓰는 경우, "로그인이 필요한 API"를 Swagger에서 테스트하려면 헤더에 토큰을 넣을 수 있는 버튼이 필요하다.
그래서 별도의 설정 파일(SwaggerConfig)을 만들었다.
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
// JWT 인증 버튼(Authorize) 설정
String jwtSchemeName = "JWT Authentication";
SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName);
Components components = new Components()
.addSecuritySchemes(jwtSchemeName, new SecurityScheme()
.name(jwtSchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")); // 토큰 형식을 지정
return new OpenAPI()
.addSecurityItem(securityRequirement) // 이 설정을 전역에 적용
.components(components)
.info(new Info()
.title("드림쇼콰이어 API 명세서")
.description("실시간으로 업데이트되는 문서입니다.")
.version("v1.0"));
}
}
이렇게 하면 문서 페이지 오른쪽 위에 초록색 [Authorize] 버튼이 생긴다. 거기에 로그인해서 받은 토큰을 넣으면 모든 API 요청 헤더에 자동으로 실려서 나간다.
Swagger가 사용하는 리소스 경로들을 permitAll()(전체 허용) 목록에 추가해줘야 한다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// ... 다른 설정들 ...
.requestMatchers(
"/swagger-ui/**", // UI 관련 정적 리소스 (HTML, JS, CSS)
"/v3/api-docs/**", // API 명세 데이터 (JSON)
"/swagger-ui.html" // 리다이렉트 페이지
).permitAll()
// ...
);
return http.build();
}
특히 /v3/api-docs/**를 빼먹는 경우가 많은데, 이게 없으니, 다른 이상한 Petshop api를 보여주더라.(경험담)
자동으로 만들어진 문서는 userController, createPost 같이 개발자만 아는 영어로 되어 있다.
협업을 위해 어노테이션(Annotation)으로 친절한 설명을 달아주는 것이 매너다.
@Tag(name = "Sheet (악보)", description = "정단원 전용 악보 공유 API") // 그룹 설명
@RestController
@RequestMapping("/api/sheets")
public class SheetController {
@Operation(summary = "악보 업로드", description = "PDF나 MP3 파일을 업로드합니다.") // API 설명
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<SheetResponseDto> uploadSheet(
@Parameter(description = "업로드할 파일", required = true) // 파라미터 설명
@RequestPart(value = "file") MultipartFile file
) {
// ...
}
}
이렇게 달아두면 문서에 "악보 업로드"라고 한글로 예쁘게 표시된다. 프론트엔드 개발자가 "이 API 뭐 하는 거예요?"라고 물어볼 일이 없어진다.
API 문서를 엑셀로 관리하던 시절에는 코드를 고칠 때마다 "아, 문서도 고쳐야지" 하는 압박감이 있었다. 그리고 꼭 하나씩 빼먹어서 프론트엔드 팀과 "문서랑 코드가 다른데요?" 하며 얼굴을 붉히기도 했다. 사실 이번 프로젝트를 진행할 때까지만 해도, 이런 게 있다는 사실을 몰라서, API 명세서를 따로 작성하고 있었다. 하나하나 확인해가면서 글로 썼는데... 백엔드 개발이 거의 끝났을 때 알게 되어서, 끝마치자마자 적용을 시켰다.
다음 프로젝트에서는 처음부터 Swagger를 통해서 API를 문서화시켜야겠다.
혹시 아직도 API 명세서를 손으로 쓰고 있다면, 당장 Swagger를 도입해 보길 권한다. 신세계가 열릴 것이다.