본문으로 건너뛰기
Springdoc과 OpenAPI (어노테이션 활용법)

Springdoc과 OpenAPI (어노테이션 활용법)

· loading · loading ·
gunyoung.Park
작성자
gunyoung.Park
Always curious, always exploring new tech
목차
Swagger OAS(Open API Specification) - 이 글은 시리즈의 일부입니다.
부분 2: 이 글

QueryString 처리 방식 비교
#

Spring에서 QueryString을 처리하는 두 가지 방식을 비교해보겠습니다.

방식 1: Object로 받기 (ParameterObject)
#

@Operation(tags = {"swagger"})
@GetMapping("/hello/parameters1")
public ResponseEntity<List<ResponseTest>> parameterObjectTest(ParameterObjectReq req) {
    ResponseTest response = new ResponseTest(req.email(), req.password(), req.occupation());
    return ResponseEntity.ok(List.of(response));
}

방식 2: 개별 파라미터로 받기 (@RequestParam)
#

@Operation(tags = {"swagger"})
@GetMapping("/hello/parameters2")
public ResponseEntity<List<ResponseTest>> parameterObjectTest2(
        @RequestParam(value = "email") String email,
        @RequestParam(value = "pw") String password,
        @RequestParam(value = "oq") OccupationStatus status
) {
    ResponseTest response = new ResponseTest(email, password, status);
    return ResponseEntity.ok(List.of(response));
}

모델 정의
#

ParameterObjectReq (Request DTO)

public record ParameterObjectReq(
        String email,
        String password,
        OccupationStatus occupation
) {
}

OccupationStatus (Enum)

public enum OccupationStatus {
    STUDENT,
    EMPLOYEE,
    UNEMPLOYED
}

두 방식의 차이점
#

일반적으로 request를 QueryString으로 받을 경우 @RequestParam을 사용하지만, 받는 인자가 많을 경우 첫 번째 방식처럼 QueryString을 Object 형태로 받을 수 있습니다.

@RequestParam vs ParameterObject
#

  • @RequestParam: 기본적으로 required = true로 설정되어 있어 request value를 필수로 받습니다.
  • ParameterObject: Spring에서 별도의 어노테이션 없이도 QueryString을 객체의 필드값에 자동으로 바인딩합니다. 하지만 required가 기본 설정되지 않아 null 값이 들어올 수 있습니다.

Springdoc에서의 ParameterObject vs @RequestParam 변환 비교
#

ParameterObject 사용 시
#

ParameterObject 변환 결과

@RequestParam 사용 시
#

RequestParam 변환 결과


@ParameterObject 어노테이션 활용
#

ParameterObject를 Springdoc이 @RequestParam을 사용했을 때처럼 변환해주고 Required 여부를 표시하려면 다음과 같이 설정합니다.

코드 예제
#

@ParameterObject
public record ParameterObjectReq(
        @NotNull
        String email,

        @NotNull
        String password,

        OccupationStatus occupation
) {
}

@ParameterObject는 Springdoc 어노테이션으로, 여러 개의 QueryString을 Object 형태로 받을 경우 해당 클래스 위에 명시하면 @RequestParam처럼 인식하고 변환해줍니다.

Swagger UI에서의 변환 결과

JSR-303 지원
#

Springdoc은 JSR-303을 지원하며, 다음과 같은 validation 어노테이션을 사용할 수 있습니다

  • @NotNull
  • @Min, @Max
  • @Size
  • 기타 validation 어노테이션

Springdoc 공식 문서에 따르면

This library supports

  • OpenAPI 3
  • Spring-boot (v1, v2 and v3)
  • JSR-303, specifically for @NotNull, @Min, @Max, and @Size
  • Swagger-ui
  • OAuth 2
  • GraalVM native images

변환 결과
#

ParameterObject도 @RequestParam으로 인식되도록 spec 파일이 작성되었고, @NotNull을 붙이지 않은 occupation에는 Required가 optional 형태로 표시됩니다.

Spec 파일 변환 결과

Swagger UI 표시 결과

좌측 이미지: @ParameterObject를 명시했을 경우 Springdoc이 인식하고 정상적인 spec으로 변환


Swagger2 → Swagger3 Annotations
#

Swagger2Swagger3설명
@Api@Tag클래스단에 swagger 리소스 표시(그룹화 시켜줌)

name : 태그의 이름
description : 태그에 대한 설명
@ApiIgnore@Parameter(hidden = true)
@Operation(hidden = true)
@Hidden
해당 어노테이션을 통해 파라미터를 swagger-ui 에서 숨길 수 있음.

requestBody 나 ResponseBody 의 경우는
@JsonProperty(access = JsonProperty.Access.READ_ONLY) 를 사용
@ApiImplicitParam@Parameter단일 RequestParam 에 대한 설정 및 리소스 표시
@ApiImplicitParams@Parameters여러개의 RequestParam 을 설정
@ApiModel@Schemadescription : 한글명
defaultValue : 기본값
allowableValues : 허용가능한 값(열거형으로 정의가능할 경우 설정합니다)
@ApiModelProperty(hidden = true)@Schema(accessMode = READ_ONLY)
@ApiOperation(value = “foo”, notes = “bar”)@Operation(summary = “foo”, description = “bar”)summary : api에 대한 간략 설명
description : api에 대한 상세 설명
responses : api Response 리스트
parameters : api 파라미터 리스트
@ApiParam@Parametername : 파라미터 이름
description : 파라미터 설명
in : 파라미터 위치 (query, header, path, cookie)
@ApiResponse(code = 404, message = “foo”)@ApiResponse(responseCode = “404”, description = “foo”)responseCode : http 상태코드
description : response에 대한 설명
content : Response payload 구조
schema : payload에서 이용하는 Schema

hidden : Schema 숨김여부
implementation : Schema 대상 클래스
  • 여러 개의 request query params를 캡처하기 위해 객체를 사용하는 경우, 해당 메서드 인자에 @ParameterObject 어노테이션을 사용하세요
  • 이 단계는 선택사항입니다: 여러 개의 Docket 빈이 있는 경우에만 GroupedOpenApi 빈으로 교체하세요

@Tag 어노테이션 활용
#

@Tag 어노테이션을 사용하면 다음과 같은 그룹핑이 가능합니다

  • Controller 단위로 그룹핑
  • Controller 내부의 메서드 단위로 그룹핑
  • @Tag에 명명한 이름에 따라 spec 파일로 전환 시 그룹핑 수행
  • OpenAPI Generator를 이용한 client code 생성 시 해당 이름으로 파일 생성

@Tag 중복 사용 시 주의사항
#

질문: 최상위 레벨에 @Tag로 그룹핑하고, 하위 메서드의 @Operation에서 다른 이름으로 tag를 설정하면 어떻게 될까요?

테스트 결과
#

중복 그룹 생성 결과

최상위에 @Tag(name = "swagger")를 설정하고, postHello 메서드의 @Operation에서 tags = {"swagger123"}을 추가한 경우, 같은 엔드포인트가 다른 그룹으로 중복 생성됩니다.

문제점
#

이 상태에서 OpenAPI Generator를 사용하면 아래와 같이 중복된 client 코드가 생성되는 문제가 발생합니다.

중복 생성된 API 파일 1
중복 생성된 API 파일 2
중복 생성된 API 파일 3

권장사항: 특별한 경우가 아니라면 @Tag를 이용한 그룹핑은 Controller의 최상위에서만 사용하는 것을 권장합니다.

OpenAPI Generator Client Code 생성 시 파일명
#

Client 코드 생성 시 @Tag에서 명명한 이름 + -api가 postfix로 붙습니다. 이 부분을 커스터마이징하려면 Mustache 파일을 수정해야 합니다.

참고 자료
#


인증 관련 OpenAPI 스펙
#

OpenAPI는 다양한 인증 방식을 지원합니다. 주요 설정 항목은 다음과 같습니다.

type (인증 형식)
#

현재 API Key, HTTP, OAuth2, OpenID Connect 방식을 지원합니다. 참고: OpenAPI v2 스펙에서는 OpenID Connect 방식을 지원하지 않습니다.

지원되는 타입

  • http: Basic, Bearer 및 기타 HTTP 인증 체계
  • apiKey: API 키 및 쿠키 인증
  • oauth2: OAuth2 인증
  • openIdConnect: OpenID Connect 검색

주요 설정 항목
#

  • name: 인증 키 이름 (API Key 방식 사용 시 필요)
  • in: 인증 키의 위치 지정 (query, header, cookie 중 선택, API Key 방식 사용 시 필요)
  • scheme: 인증 방식 지정 (Basic 또는 Bearer, HTTP 인증 방식 사용 시 필요)
  • bearerFormat: Bearer 토큰 형식 (일반적으로 JWT 사용)
  • flows: OAuth2 플로우 타입 (implicit, password, clientCredentials, authorizationCode 중 선택)
  • openIdConnectUrl: OpenID Connect URL (OpenAPI v2 스펙에서는 OAuth2나 Bearer 토큰 방식으로 대체 권장)

@Deprecated 전략
#

API 버전 업데이트로 DTO 스펙에 변경이 있을 경우, 다음과 같은 단계적 전략을 사용합니다.

1단계: @Deprecated 표시
#

먼저 변경될 필드에 @Deprecated 어노테이션을 붙입니다.

public class UserDto {
    @Deprecated
    private String oldField;
    
    private String newField;
}

OpenAPI spec 상에도 해당 스키마의 필드에 deprecated가 표시되고, 프론트엔드에서 코드 생성 시 해당 필드에 deprecated 표시가 나타납니다. 이를 통해 프론트엔드 팀에게 곧 해당 필드가 제거될 것임을 미리 알립니다.

2단계: @Schema(hidden = true) 적용
#

프론트엔드에서 새로운 스펙으로 마이그레이션이 완료되면, 서버에서 해당 @Deprecated 필드에 @Schema(hidden = true)를 추가하여 더 이상 OpenAPI spec에 해당 필드가 생성되지 않도록 합니다.

public class UserDto {
    @Deprecated
    @Schema(hidden = true)
    private String oldField;  // spec에서 제외됨
    
    private String newField;
}

3단계: 필드 제거
#

충분한 시간이 지난 후 해당 필드를 완전히 제거합니다.

이러한 단계적 접근 방식을 통해 프론트엔드와 백엔드 간의 안전한 API 버전 관리가 가능합니다.

Swagger OAS(Open API Specification) - 이 글은 시리즈의 일부입니다.
부분 2: 이 글

관련 글