DS's TechBlog
[Spring] Swagger 공통 응답 코드 처리 및 Enum으로 정의한 응답 코드 사용하기 본문
Swagger로 API 문서를 작성하는 데 몇 가지 문제가 있었습니다.
1. 공통 응답 코드는 Swagger-ui에 표시되지 않습니다.
2. Enum으로 정의한 응답 코드와 메시지를 @ApiResponse에서 사용할 수 없습니다.
문제 코드

문제 상황

위와 같이, 중복 응답 코드에 대해서는 먼저 작성한 구문에 대해서만 동작하는 것을 볼 수 있습니다.
해결 방법
https://leeeeeyeon-dev.tistory.com/92#google_vignette
[Packy] Swagger @ApiResponses를 커스텀 어노테이션으로 대체하기
1. @ApiResponses의 한계 Swagger에서 정상 응답값과 함께 에러 응답값도 명시해주어야 클라이언트 단에서도 에러 응답값에 대한 예외 처리를 할 수 있다. Swagger에서는 기본적으로 아래와 같이 @ApiRespon
leeeeeyeon-dev.tistory.com
위 블로그에서 이와 같은 고민들을 다루고 있었습니다.
기본적인 뼈대는 똑같습니다. 에러 코드 뿐 아니라, 성공 코드도 반환할 수 있도록 코드를 추가하였습니다.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSuccessCodeExample {
SuccessCode value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSuccessCodeExamples {
SuccessCode[] value();
}
note) SuccessCode는 HttpStatus와 성공여부, 메시지를 변수로 가지는 enum 클래스입니다.
@Configuration
public class SwaggerConfig {
/* 생략 */
@Bean
public OperationCustomizer customize() {
return (Operation operation, HandlerMethod handlerMethod) -> {
/* 생략 */
ApiSuccessCodeExamples apiSuccessCodeExamples = handlerMethod.getMethodAnnotation(
ApiSuccessCodeExamples.class);
if (apiSuccessCodeExamples != null) {
generateResponseCodeResponseExample(operation, apiSuccessCodeExamples.value());
} else {
ApiSuccessCodeExample apiSuccessCodeExample = handlerMethod.getMethodAnnotation(
ApiSuccessCodeExample.class);
if (apiSuccessCodeExample != null) {
generateResponseCodeResponseExample(operation, apiSuccessCodeExample.value());
}
}
return operation;
};
}
// ResponseCode는 SuccessCode와 ErrorCode의 인터페이스입니다.
private void generateResponseCodeResponseExample(Operation operation, ResponseCode[] responseCodes) {
ApiResponses responses = operation.getResponses();
Map<Integer, List<ExampleHolder>> statusWithExampleHolders = Arrays.stream(responseCodes)
.map(
responseCode -> ExampleHolder.builder()
.holder(getSwaggerExample(responseCode))
.httpStatus(responseCode.getHttpStatus().value())
.name(responseCode.name())
.build()
)
.collect(Collectors.groupingBy(ExampleHolder::getHttpStatus));
addExamplesToResponses(responses, statusWithExampleHolders);
}
private void generateResponseCodeResponseExample(Operation operation, ResponseCode responseCode) {
ApiResponses responses = operation.getResponses();
// ExampleHolder 객체 생성 및 ApiResponses에 추가
ExampleHolder exampleHolder = ExampleHolder.builder()
.holder(getSwaggerExample(responseCode))
.name(responseCode.name())
.httpStatus(responseCode.getHttpStatus().value())
.build();
addExamplesToResponses(responses, exampleHolder);
}
private Example getSwaggerExample(ResponseCode responseCode) {
Example example = new Example();
if (responseCode instanceof ErrorCode) {
List<ErrorResponse.ValidationError> validationErrorList = new ArrayList<>();
// Contoller의 @Valid에서 생긴 에러는 ErrorResponse에 에러 세부 필드를 추가해줍니다.
if (responseCode == ErrorCode.INVALID_PARAMETER) {
FieldError fieldError = new FieldError("objectName", "field", "defaultMessage");
validationErrorList.add(ErrorResponse.ValidationError.of(fieldError));
}
ErrorResponse errorResponse = ErrorResponse.builder()
.code(((ErrorCode) responseCode).getCode())
.message(responseCode.getMessage())
.errors(validationErrorList)
.build();
example.setValue(errorResponse);
return example;
}
SuccessResponse successResponse = SuccessResponse.builder()
.success(((SuccessCode) responseCode).getSuccess())
.message(responseCode.getMessage())
.build();
example.setValue(successResponse);
return example;
}
/* 생략 */
}
전체적인 코드와 흐름에 대한 설명은 위 블로그에 자세하게 작성되어 있어서, 추가하거나 변경한 부분만 작성했습니다.
코드 변경점
- 에러 코드와 성공 코드를 같이 처리하기 위해서 ErrorCode와 SuccessCode를 구현체로 가지는 ResponseCode 인터페이스를 작성해야 합니다.
- 성공 코드를 반환할 때 사용할 ApiSuccessCodeExample과 ApiSuccessCodeExamples 를 작성해줍니다.
- @Valid에서 발생하는 에러에 추가 되는 errors 필드를 응답에 포함하기 위해서, FiledError 를 추가했습니다. (errors 필드는 @JsonInclude(JsonInclude.Include.NON_EMPTY) 이 추가되어 있어서, 값이 없을 때는 응답에 포함되지 않습니다.)
적용 코드
@ApiSuccessCodeExample(SuccessCode.JOIN_SUCCESS)
@ApiErrorCodeExamples({ErrorCode.DUPLICATED_USER_ID, ErrorCode.DUPLICATED_STUDENT_NUMBER, ErrorCode.INVALID_PARAMETER})
@PostMapping
public ResponseEntity<SuccessResponse> resisterUser(@RequestBody @Valid JoinDTO joinDTO) {
joinService.createUserAccount(joinDTO);
return ResponseUtil.buildSuccessResponseEntity(SuccessCode.JOIN_SUCCESS);
}
적용 결과

이렇게 해서, Swagger-UI에 공통 응답 코드를 처리하는 방법과 Enum으로 정의한 응답 코드와 메시지를 바로 사용할 수 있습니다.
'Java & Spring' 카테고리의 다른 글
[Spring Security] authorizeHttpRequests 우선순위 적용하기 (1) | 2024.05.17 |
---|---|
[Spring] Swagger default 200 response 삭제하기 (0) | 2024.05.03 |
[Spring Security] SecurityFilterChain에서 발생하는 예외를 처리하기 (0) | 2024.04.24 |
[Spring Security] 로그인 입력 값을 Json 형태 + userId로 받기 (1) | 2024.04.19 |
[Java] Optional과 findById (0) | 2024.04.13 |