Spring Boot 3.x.x 부터는 JavaEE → Jakarta EE로 교체되었습니다.
그렇기 때문에 이전에 사용했던 Springfox는 현재 Spring Boot 3.x.x에 대한 업데이트가 이뤄지지 않고 현시점에는 사용할 수 없었습니다. Springdoc 공식문서에서 어떻게 적용할 수 있는지에 대한 자세한 방법이 나와있으니 참고하시면 좋을 것 같습니다.
의존성 추가
Spring Boot 3.x.x부터는 아래 의존성을 추가해주면 Swagger-ui 설정은 끝입니다.
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2"
- springdoc-openapi-ui → springdoc-openapi-starter-webmvc-ui 로 교체되었습니다.
- springdoc-openapi-webflux-ui → springdoc-openapi-starter-webflux-ui 로 교체되었습니다.
해당 의존성을 추가해주는 것만으로도 /swagger-ui.html 접근이 가능합니다.
설정 파일
현재 API 문서의 설명이나, 버전 등등 더 세부적으로 설정해줄 순 없을까요?
바로 OpenApi를 빈으로 등록하는 방법이 있습니다.
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class SpringdocConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.addOpenApiCustomizer(openApi -> openApi
.info(new Info().title("TEST API DOCS")
.description("테스트 API 문서입니다.")
.version("v1.0"))
.group("Test Group")
.packagesToScan("org.example.controller")
.displayName("This is Test API")
.build();
}
}
application.yml 설정
springdoc:
packages-to-scan: org.example.controller
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
swagger-ui:
path: /docs/test.html
tags-sorter: alpha
operations-sorter: alpha
syntax-highlight:
activated: true
api-docs:
path: /v3/docs
groups:
enabled: true
cache:
disabled: true
- springdoc.swagger-ui.tags-sorter: URI를 다음 기준으로 정렬
- springdoc.swagger-ui.operations-sorter : HTTP 메소드를 다음 기준으로 정렬
- alpha: 알파벳 순으로 정렬
- method: HTTP 메소드 순으로 정렬
더 많은 properties 설정은 공식문서에서 확인하시길 바랍니다.
Spring Security를 사용하고 있다면?
implementation 'org.springdoc:springdoc-openapi-security:1.6.14'
이미 springdoc-openapi-starter-webmvc-ui 의존성을 사용중이라면, 따로 의존성을 추가해줄 필요는 없습니다. 우리가 추가해줬던 의존성으로 인해 @AuthenticationPrincipal 어노테이션은 자동으로 swagger-ui에 ignore 되므로 따로 처리해주지 않아도 됩니다.
Javadoc 으로 문서를 꾸며보자
springdoc-openapi-starter-webmvc-ui 의존성을 사용중이라면, 아래 의존성들을 추가해줘서 javadoc을 활성화 시켜줘야 합니다.
annotationProcessor 'com.github.therapi:therapi-runtime-javadoc-scribe:0.13.0'
implementation 'com.github.therapi:therapi-runtime-javadoc:0.13.0'
이제 작성한 javadoc은 다음과 같이 처리됩니다.
- method comment: @Operation(description={}) 으로 치환됩니다.
- @return: @Operation(response={}) 으로 치환됩니다.
- attribute comment: @Schema(description={}) 으로 치환됩니다.
💡 만약 Swagger Annotation과 javadoc이 동시에 존재한다면, Swagger Annotation이 우선으로 적용됩니다.
Authorization 추가
JWT를 사용해 인가를 하고있다면 다음과 같이 설정을 추가하면 Swagger에서 전역 인증정보를 설정하여 인증이 필요한 API를 호출할 수 있습니다.
@Configuration
public class SpringdocConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.addSecurityItem(new SecurityRequirement().addList("Authorization"))
.components(new Components().addSecuritySchemes("Authorization", new SecurityScheme()
.name("Authorization")
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")))
.addOpenApiCustomizer(openApi -> openApi
.info(new Info().title("TEST API DOCS")
.description("테스트 API 문서입니다.")
.version("v1.0"))
.group("Test Group")
.packagesToScan("org.example.controller")
.displayName("This is Test API")
.build();
}
}
공통 Pageable 응답 어노테이션 개발
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Parameters({
@Parameter(name = "page", description = "페이지 번호 (0..N) [기본값: 0]", schema = @Schema(type = "integer", defaultValue = "0", nullable = true)),
@Parameter(name = "size", description = "페이지 번호 (0..100) [기본값: 10]", schema = @Schema(type = "integer", defaultValue = "10")),
@Parameter(name = "sort", description = "정렬 (컬럼,asc|desc) [예시] 이름을 내림차순 정렬 ex) 'name,desc'", schema = @Schema(type = "array", name = "정렬 (컬럼,asc|desc) [예시] 이름을 내림차순 정렬 ex)")),
@Parameter(name = "pageable", hidden = true)
})
public @interface ApiPageable {
}
공통 API 응답 어노테이션 개발
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiResponse(responseCode = "401", description = "UnAuthorized", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example")))
@ApiResponse(responseCode = "403", description = "Forbidden", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class),mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example")))
@ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = YourExample.class)))
public @interface ApiCommonResponse {
}
Controller 명세
GET 방식
@Operation(
summary = "회원 조회 API",
description = "회원을 조회할 수 있습니다.",
responses = {
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true, headers = @Header(name = "Authorization", required = true), content = @Content(schema = @Schema(implementation = YourExample.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example"))),
@ApiResponse(responseCode = "404", description = "NotFound", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class), examples = @ExampleObject(value = "example"))),})
@Parameters({
@Parameter(name = "username", description = "사용자 이름", example = "john", required = true),
@Parameter(name = "age", description = "사용자 나이", example = "20", required = true)
})
@ApiPageable
@ApiCommonResponse
@GetMapping("/members")
ResponseEntity<MemberDto> searchMembers(
@RequestParam("username") String username,
@RequestParam("age") int age,
@PageableDefault Pageable pageable
){
...
}
POST 방식
@Operation(summary = "회원을 생성하는 API",
requestBody = @RequestBody(description = "회원 생성을 위한 DTO"),
responses = {
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true, headers = @Header(name = "Authorization", required = true), content = @Content(schema = @Schema(implementation = CreateProjectResponse.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "{\\"success\\":true,\\"error\\":null,\\"message\\":{\\"projectId\\":1}"))),
...
})
@ApiCommonResponse
ResponseEntity<Long> createProject(
@Valid @RequestBody CreateMemberDto request
) {
...
}
REFERENCES
springdoc-openapi v2.1.0
springdoc-openapi java library helps to automate the generation of API documentation using spring boot projects. springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and vario
springdoc.org
Set JWT with Spring Boot and Swagger UI | Baeldung
Learn how to set a JSON Web Token on requests to Swagger UI running in Spring Boot.
www.baeldung.com
Spring Boot 3.x.x 부터는 JavaEE → Jakarta EE로 교체되었습니다.
그렇기 때문에 이전에 사용했던 Springfox는 현재 Spring Boot 3.x.x에 대한 업데이트가 이뤄지지 않고 현시점에는 사용할 수 없었습니다. Springdoc 공식문서에서 어떻게 적용할 수 있는지에 대한 자세한 방법이 나와있으니 참고하시면 좋을 것 같습니다.
의존성 추가
Spring Boot 3.x.x부터는 아래 의존성을 추가해주면 Swagger-ui 설정은 끝입니다.
implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2"
- springdoc-openapi-ui → springdoc-openapi-starter-webmvc-ui 로 교체되었습니다.
- springdoc-openapi-webflux-ui → springdoc-openapi-starter-webflux-ui 로 교체되었습니다.
해당 의존성을 추가해주는 것만으로도 /swagger-ui.html 접근이 가능합니다.
설정 파일
현재 API 문서의 설명이나, 버전 등등 더 세부적으로 설정해줄 순 없을까요?
바로 OpenApi를 빈으로 등록하는 방법이 있습니다.
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class SpringdocConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.addOpenApiCustomizer(openApi -> openApi
.info(new Info().title("TEST API DOCS")
.description("테스트 API 문서입니다.")
.version("v1.0"))
.group("Test Group")
.packagesToScan("org.example.controller")
.displayName("This is Test API")
.build();
}
}
application.yml 설정
springdoc:
packages-to-scan: org.example.controller
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
swagger-ui:
path: /docs/test.html
tags-sorter: alpha
operations-sorter: alpha
syntax-highlight:
activated: true
api-docs:
path: /v3/docs
groups:
enabled: true
cache:
disabled: true
- springdoc.swagger-ui.tags-sorter: URI를 다음 기준으로 정렬
- springdoc.swagger-ui.operations-sorter : HTTP 메소드를 다음 기준으로 정렬
- alpha: 알파벳 순으로 정렬
- method: HTTP 메소드 순으로 정렬
더 많은 properties 설정은 공식문서에서 확인하시길 바랍니다.
Spring Security를 사용하고 있다면?
implementation 'org.springdoc:springdoc-openapi-security:1.6.14'
이미 springdoc-openapi-starter-webmvc-ui 의존성을 사용중이라면, 따로 의존성을 추가해줄 필요는 없습니다. 우리가 추가해줬던 의존성으로 인해 @AuthenticationPrincipal 어노테이션은 자동으로 swagger-ui에 ignore 되므로 따로 처리해주지 않아도 됩니다.
Javadoc 으로 문서를 꾸며보자
springdoc-openapi-starter-webmvc-ui 의존성을 사용중이라면, 아래 의존성들을 추가해줘서 javadoc을 활성화 시켜줘야 합니다.
annotationProcessor 'com.github.therapi:therapi-runtime-javadoc-scribe:0.13.0'
implementation 'com.github.therapi:therapi-runtime-javadoc:0.13.0'
이제 작성한 javadoc은 다음과 같이 처리됩니다.
- method comment: @Operation(description={}) 으로 치환됩니다.
- @return: @Operation(response={}) 으로 치환됩니다.
- attribute comment: @Schema(description={}) 으로 치환됩니다.
💡 만약 Swagger Annotation과 javadoc이 동시에 존재한다면, Swagger Annotation이 우선으로 적용됩니다.
Authorization 추가
JWT를 사용해 인가를 하고있다면 다음과 같이 설정을 추가하면 Swagger에서 전역 인증정보를 설정하여 인증이 필요한 API를 호출할 수 있습니다.
@Configuration
public class SpringdocConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.addSecurityItem(new SecurityRequirement().addList("Authorization"))
.components(new Components().addSecuritySchemes("Authorization", new SecurityScheme()
.name("Authorization")
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")))
.addOpenApiCustomizer(openApi -> openApi
.info(new Info().title("TEST API DOCS")
.description("테스트 API 문서입니다.")
.version("v1.0"))
.group("Test Group")
.packagesToScan("org.example.controller")
.displayName("This is Test API")
.build();
}
}
공통 Pageable 응답 어노테이션 개발
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Parameters({
@Parameter(name = "page", description = "페이지 번호 (0..N) [기본값: 0]", schema = @Schema(type = "integer", defaultValue = "0", nullable = true)),
@Parameter(name = "size", description = "페이지 번호 (0..100) [기본값: 10]", schema = @Schema(type = "integer", defaultValue = "10")),
@Parameter(name = "sort", description = "정렬 (컬럼,asc|desc) [예시] 이름을 내림차순 정렬 ex) 'name,desc'", schema = @Schema(type = "array", name = "정렬 (컬럼,asc|desc) [예시] 이름을 내림차순 정렬 ex)")),
@Parameter(name = "pageable", hidden = true)
})
public @interface ApiPageable {
}
공통 API 응답 어노테이션 개발
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiResponse(responseCode = "401", description = "UnAuthorized", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example")))
@ApiResponse(responseCode = "403", description = "Forbidden", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class),mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example")))
@ApiResponse(responseCode = "500", description = "Internal Server Error", content = @Content(schema = @Schema(implementation = YourExample.class)))
public @interface ApiCommonResponse {
}
Controller 명세
GET 방식
@Operation(
summary = "회원 조회 API",
description = "회원을 조회할 수 있습니다.",
responses = {
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true, headers = @Header(name = "Authorization", required = true), content = @Content(schema = @Schema(implementation = YourExample.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "example"))),
@ApiResponse(responseCode = "404", description = "NotFound", useReturnTypeSchema = true, content = @Content(schema = @Schema(implementation = YourExample.class), examples = @ExampleObject(value = "example"))),})
@Parameters({
@Parameter(name = "username", description = "사용자 이름", example = "john", required = true),
@Parameter(name = "age", description = "사용자 나이", example = "20", required = true)
})
@ApiPageable
@ApiCommonResponse
@GetMapping("/members")
ResponseEntity<MemberDto> searchMembers(
@RequestParam("username") String username,
@RequestParam("age") int age,
@PageableDefault Pageable pageable
){
...
}
POST 방식
@Operation(summary = "회원을 생성하는 API",
requestBody = @RequestBody(description = "회원 생성을 위한 DTO"),
responses = {
@ApiResponse(responseCode = "200", description = "Success", useReturnTypeSchema = true, headers = @Header(name = "Authorization", required = true), content = @Content(schema = @Schema(implementation = CreateProjectResponse.class), mediaType = APPLICATION_JSON_VALUE, examples = @ExampleObject(value = "{\\"success\\":true,\\"error\\":null,\\"message\\":{\\"projectId\\":1}"))),
...
})
@ApiCommonResponse
ResponseEntity<Long> createProject(
@Valid @RequestBody CreateMemberDto request
) {
...
}
REFERENCES
springdoc-openapi v2.1.0
springdoc-openapi java library helps to automate the generation of API documentation using spring boot projects. springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and vario
springdoc.org
Set JWT with Spring Boot and Swagger UI | Baeldung
Learn how to set a JSON Web Token on requests to Swagger UI running in Spring Boot.
www.baeldung.com