728x90
오늘의 키워드
swagger
API 작성하기
class 에 적용
@Tag(name = "지역", description = "Region API")
api 메서드에 적용
@Operation(summary = "상품 등록", description = "상품을 등록합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "상품 등록 성공"),
})
@ApiErrorCodeAnnotationList({ApiErrorCode.INVALID_REQUEST, ApiErrorCode.UNAUTHORIZED})
@ApiErrorCodeAnnotationList
해당 어노테이션을 팀장님이 만드셨다! 여기서 해당 어노테이션은 아래과 같이 구현이 되어있다.
import com.toyland.global.exception.type.ApiErrorCode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiErrorCodeAnnotationList {
ApiErrorCode[] value();
}
import com.toyland.global.config.swagger.annotation.ApiErrorCodeAnnotation;
import com.toyland.global.config.swagger.annotation.ApiErrorCodeAnnotationList;
import com.toyland.global.config.swagger.response.ErrorResponseDto;
import com.toyland.global.config.swagger.response.ExampleHolder;
import com.toyland.global.exception.type.ApiErrorCode;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author : hanjihoon
* @Date : 2025. 02. 20.
*/
@Configuration
public class SwaggerConfig implements WebMvcConfigurer {
@Bean
public OpenAPI OpenApi() {
Info info = new Info()
.title("Toyland API")
.description("API docs")
.version("1.0.0");
SecurityScheme securityScheme = new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name(HttpHeaders.AUTHORIZATION);
SecurityRequirement securityRequirement = new SecurityRequirement().addList("JWT");
return new OpenAPI()
.components(new Components().addSecuritySchemes("JWT", securityScheme))
.addSecurityItem(securityRequirement)
.info(info);
}
@Bean
public OperationCustomizer customize() {
return (Operation operation, HandlerMethod handlerMethod) -> {
ApiErrorCodeAnnotationList apiErrorCodeAnnotationList = handlerMethod.getMethodAnnotation(
ApiErrorCodeAnnotationList.class);
// @apiErrorCodeAnnotationList 어노테이션이 붙어있다면
if (apiErrorCodeAnnotationList != null) {
generateErrorCodeResponseExample(operation, apiErrorCodeAnnotationList.value());
} else {
ApiErrorCodeAnnotation apiErrorCodeAnnotation = handlerMethod.getMethodAnnotation(
ApiErrorCodeAnnotation.class);
// @ApiErrorCodeAnnotationList 어노테이션이 붙어있지 않고
// @ApiErrorCodeAnnotation 어노테이션이 붙어있다면
if (apiErrorCodeAnnotation != null) {
generateErrorCodeResponseExample(operation, apiErrorCodeAnnotation.value());
}
}
return operation;
};
}
// 여러 개의 에러 응답값 추가
private void generateErrorCodeResponseExample(Operation operation, ApiErrorCode[] errorCodes) {
ApiResponses responses = operation.getResponses();
// ExampleHolder(에러 응답값) 객체를 만들고 에러 코드별로 그룹화
Map<Integer, List<ExampleHolder>> statusWithExampleHolders = Arrays.stream(errorCodes)
.map(
errorCode -> ExampleHolder.builder()
.holder(getSwaggerExample(errorCode))
.code(errorCode.getHttpStatus())
.name(errorCode.name())
.build()
)
.collect(Collectors.groupingBy(ExampleHolder::getCode));
// ExampleHolders를 ApiResponses에 추가
addExamplesToResponses(responses, statusWithExampleHolders);
}
// 단일 에러 응답값 예시 추가
private void generateErrorCodeResponseExample(Operation operation, ApiErrorCode errorCode) {
ApiResponses responses = operation.getResponses();
// ExampleHolder 객체 생성 및 ApiResponses에 추가
ExampleHolder exampleHolder = ExampleHolder.builder()
.holder(getSwaggerExample(errorCode))
.name(errorCode.name())
.code(errorCode.getHttpStatus())
.build();
addExamplesToResponses(responses, exampleHolder);
}
// ErrorResponseDto 형태의 예시 객체 생성
private Example getSwaggerExample(ApiErrorCode errorCode) {
ErrorResponseDto errorResponseDto = ErrorResponseDto.from(errorCode);
Example example = new Example();
example.setValue(errorResponseDto);
return example;
}
// exampleHolder를 ApiResponses에 추가
private void addExamplesToResponses(ApiResponses responses,
Map<Integer, List<ExampleHolder>> statusWithExampleHolders) {
statusWithExampleHolders.forEach(
(status, v) -> {
Content content = new Content();
MediaType mediaType = new MediaType();
ApiResponse apiResponse = new ApiResponse();
v.forEach(
exampleHolder -> mediaType.addExamples(
exampleHolder.getName(),
exampleHolder.getHolder()
)
);
content.addMediaType("application/json", mediaType);
apiResponse.setContent(content);
responses.addApiResponse(String.valueOf(status), apiResponse);
}
);
}
// exampleHolder를 ApiResponses에 추가
private void addExamplesToResponses(ApiResponses responses, ExampleHolder exampleHolder) {
Content content = new Content();
MediaType mediaType = new MediaType();
ApiResponse apiResponse = new ApiResponse();
mediaType.addExamples(exampleHolder.getName(), exampleHolder.getHolder());
content.addMediaType("application/json", mediaType);
apiResponse.content(content);
responses.addApiResponse(String.valueOf(exampleHolder.getCode()), apiResponse);
}
}
여기서 ApiErrorCodeAnnotationList 에 ApiErrorCode 대신
public enum ApiErrorCode implements ErrorCode {
구현한 인터페이스로 쓸순없을까.. 고민했다.
그래서 아래와 같이 바꿔보았으나
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiErrorCodeAnnotationList {
Class<? extends ErrorCode>[] value();
}
이렇게 하니 아래와 같이 리스트로 받는 부분이 컴파일 에러가 떴다.
@ApiErrorCodeAnnotationList({ApiErrorCode.INVALID_REQUEST, ApiErrorCode.UNAUTHORIZED})
어노테이션의 value()가 기대하는 타입이 Class<? extends ErrorCode>[]이기 때문에
ApiErrorCode.INVALID_REQUEST는 Class<?>가 아닌 enum의 인스턴스 값이므로 타입이 맞지 않는다고 한다...
그래서 결국 원래대로 돌렸다..
728x90
'TIL' 카테고리의 다른 글
[TIL] 2025.02.26 프로젝트 회고 (0) | 2025.02.26 |
---|---|
[TIL] 2025.02.25 controller 분기처리 (0) | 2025.02.26 |
[TIL] 2025.02.21 queryParameter dto로 받아보기 (0) | 2025.02.22 |
[TIL] 2025.02.21 sort 값 dto로 받아보기 (0) | 2025.02.21 |
[TIL] 2025.02.19 점화식 DP (0) | 2025.02.19 |