Swagger Springboot 3.x Response 설정 방법
Swagger Springboot 3.x Response 설정 방법입니다.
Swagger 의 다양한 설정 방법은 아래의 Link를 확인하세요!
Link : https://aljjabaegi.tistory.com/713
Link : https://aljjabaegi.tistory.com/714
Link : https://aljjabaegi.tistory.com/716
Link : https://aljjabaegi.tistory.com/717
Link : https://aljjabaegi.tistory.com/718
Responses 설정
Operation 하단에는 해당 API 의 응답 정보가 표출됩니다.
기본적으로는 ResponseEntity 의 Generic type 이 추가 됩니다.
{key 변수명, value type}
정상 Response 자동 추가
200 code에 대한 정보는 record에서 변경할 수 있습니다.
클래스와 속성 위치에 둘 다 @Schema 를 사용하여 설정합니다.
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
@Schema(description = "멤버 조회 응답")
public record MemberResponse(
@Schema(description = "멤버 ID", example = "geonlee")
@NotNull
@Size(min = 2, max = 50)
String memberId,
@Schema(description = "멤버 이름", example = "이건")
@Size(min = 2, max = 100)
String memberName) {
}
코드를 보시면 아시겠지만, Swagger에서는 Validation annotation 과 연계하여 동작합니다.
오류 Response 자동 추가
Exception 에 대한 응답 처리는 @ControllerAdvice와 연계하여 동작합니다.
확인을 위해 GlobalExceptionHandler 를 구현해 보겠습니다.
GlobalExceptionHandler.java
import com.aljjabaegi.swagger.api.config.exception.record.ErrorResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.io.IOException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = {IOException.class, NoSuchMethodError.class})
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ErrorResponse> handleInternalServerError(Exception e) {
LOGGER.error(e.getMessage(), e);
return ResponseEntity.ok()
.header("Content-type", String.valueOf(MediaType.APPLICATION_JSON))
.body(new ErrorResponse(500, "서버 오류입니다."));
}
@ExceptionHandler(value = NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<ErrorResponse> handleNotFoundError(Exception e) {
LOGGER.error(e.getMessage(), e);
return ResponseEntity.ok()
.header("Content-type", String.valueOf(MediaType.APPLICATION_JSON))
.body(new ErrorResponse(400, "요청하신 페이지를 찾을 수 없습니다."));
}
}
@ControllerAdvice 의 @ResponseStatus 와 연계하여 자동으로 Respones 가 추가되게 됩니다.
하지만 ErrorResponse의 example로 표출되기 때문에 정확한 정보를 전달하기 어렵죠.
Response 수동 추가
자동으로 추가하지 않고, 따로 설정하고 싶을 때에는 메서드에 @ApiResponses, @ApiResponse 를 사용합니다.
@RestController
@Tag(name = "[API-001] 멤버 정보 편집", description = "[담당자 : GEONLEE]")
public class MemberController {
@GetMapping(value = "/members/{memberId}", produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "멤버 정보 조회", description = """
# Parameters
- memberId [멤버 ID] <font color='red'>*</font>
- memberName [멤버 이름]
"""
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = """
### OK
- 정상 응답.
"""
, content = @Content(schema = @Schema(implementation = MemberResponse.class))),
@ApiResponse(responseCode = "404", description = """
### Not Found
- 요청한 'URI' 를 찾을 수 없을 경우 발생.
- 'URI' 가 올바른지 확인.
"""
, content = @Content(schema = @Schema(implementation = ErrorResponse.class)
, examples = @ExampleObject(value = """
{
"status" : 404,
"message" : "요청하신 페이지를 찾을 수 없습니다."
}
"""))),
@ApiResponse(responseCode = "500", description = """
### Internal Server Error
- 서버 오류.
- API 담당자에게 로그 확인을 요청.
"""
, content = @Content(schema = @Schema(implementation = ErrorResponse.class)
, examples = @ExampleObject(value = """
{
"status" : 500,
"message" : "서버 오류가 발생했습니다."
}
""")))
})
public ResponseEntity<MemberResponse> getMember(@PathVariable("memberId")
@Parameter(name = "memberId", description = "멤버 ID", example = "geonlee")
String memberId) {
return ResponseEntity.ok(new MemberResponse("geonlee", "이건"));
}
}
모든 Operation에 공통 Response 추가
@ApiResponses 를 사용하여 모든 Operation 에 추가하는 것은 무리가 있습니다. @ApiResponse 는 특정 Operation 에서 발생하는 Response를 추가할 때 사용하고 모든 Operation 공통 Response는 SwaggerConfig 에 아래와 같이 OpeartionCustomizer 를 등록하여 사용하도록 합니다.
@Configuration
public class SwaggerConfig {
/**
* Operation 의 기존 ApiResponse 에 공통 응답 추가
*/
@Bean
public OperationCustomizer operationCustomizer() {
return (operation, handlerMethod) -> {
ApiResponses apiResponses = operation.getResponses();
if (apiResponses == null) {
apiResponses = new ApiResponses();
operation.setResponses(apiResponses);
}
apiResponses.putAll(getCommonResponses());
return operation;
};
}
/**
* 공통 응답 정보를 생성하여 맵으로 리턴한다.
*
* @return LinkedHashMap<String, ApiResponse> ApiResponse Map
*/
private Map<String, ApiResponse> getCommonResponses() {
LinkedHashMap<String, ApiResponse> responses = new LinkedHashMap<>();
responses.put("404", notFoundResponse());
responses.put("500", internalServerResponse());
return responses;
}
/**
* 404 Response 를 생성하여 리턴
*
* @return ApiResponse 404 응답 객체
*/
private ApiResponse notFoundResponse() {
ApiResponse apiResponse = new ApiResponse();
apiResponse.setDescription("""
Not Found
- 요청한 URI 가 올바른지 확인한다.
""");
addContent(apiResponse, 404, "Not Found");
return apiResponse;
}
/**
* 500 Response 를 생성하여 리턴
*
* @return ApiResponse 500 응답 객체
*/
private ApiResponse internalServerResponse() {
ApiResponse apiResponse = new ApiResponse();
apiResponse.setDescription("""
Internal Server Error (Unchecked Exception)
- API 담당자에게 오류 확인을 요청한다.
""");
addContent(apiResponse, 500, "Internal Server Error");
return apiResponse;
}
/**
* ApiResponse 의 Content 정보를 추가
*
* @param apiResponse Api 응답 객체
* @param status 응답 상태 코드
* @param message 응답 메시지
*/
@SuppressWarnings("rawtypes")
private void addContent(ApiResponse apiResponse, int status, String message) {
Content content = new Content();
MediaType mediaType = new MediaType();
Schema schema = new Schema<>();
schema.$ref("#/components/schemas/ErrorResponse");
mediaType.schema(schema).example(new ErrorResponse(status, message));
content.addMediaType("application/json", mediaType);
apiResponse.setContent(content);
}
}
swagger에 등록되는 Schema 는 $ref("/components/chemas/스키마명") 으로 접근 할 수 있습니다.
※참고로 Schema 는 @Operation의 요청/응답 객체만 등록이 되기 때문에, GlobalExceptionHandler 에서 하나를 등록하고 (ErrorResponse가 schema 에 등록) OperationCustomizer에서 override 되도록 합니다. (ApiResponses는 Map)
등록이 되지 않은 Schema에 접근 할 경우 아래와 같은 에러를 보게됩니다.
참고로 2.3.0, 2.4.0 버전에서 OperationCustomizer 로 추가 할 경우 기존 exmaple 이 깨져 "string" 으로 표출되는 문제가 발견되었습니다. 2.2.0 버전에서는 발견되지 않은 문제이니 참고 바랍니다.
Github Swagger Project
https://github.com/aljjabaegiProgrammer/swagger-api