/src/main/resources/application.yaml
spring:
mvc:
problemdetails:
enabled: true
messages:
basename: messages/errors
web:
resources:
add-mappings: false
problemdetails.enabled: true
Spring MVC가 ProblemDetail을 활용할 것을 지정
messages.basename: messages/errors
메시지소스(properties) 파일의 경로와 이름을 지정
web.resources.add-mappings: false
Spring가 자동처리하는 정적 자원 파일 mapping을 비활성화
/src/main/resources/messages/errors.properties
# AsyncRequestTimeoutException problemDetail.title.org.springframework.web.context.request.async.AsyncRequestTimeoutException=요청 시간 초과 problemDetail.org.springframework.web.context.request.async.AsyncRequestTimeoutException=요청 처리 시간이 제한 시간을 초과했습니다. # ConversionNotSupportedException problemDetail.title.org.springframework.beans.ConversionNotSupportedException=형식 변환 실패 problemDetail.org.springframework.beans.ConversionNotSupportedException=프로퍼티 {0}의 값을 {1} 형식으로 변환할 수 없습니다. # HandlerMethodValidationException problemDetail.title.org.springframework.web.bind.support.HandlerMethodValidationException=데이터 검증 실패 problemDetail.org.springframework.web.bind.support.HandlerMethodValidationException=데이터 검증 중 오류 발생: {0} # HttpMediaTypeNotAcceptableException problemDetail.title.org.springframework.web.HttpMediaTypeNotAcceptableException=지원되지 않는 응답 타입 problemDetail.org.springframework.web.HttpMediaTypeNotAcceptableException=지원 가능한 미디어 타입: {0} problemDetail.org.springframework.web.HttpMediaTypeNotAcceptableException.parseError=미디어 타입 파싱 중 오류가 발생했습니다. # HttpMediaTypeNotSupportedException problemDetail.title.org.springframework.web.HttpMediaTypeNotSupportedException=지원되지 않는 요청 타입 problemDetail.org.springframework.web.HttpMediaTypeNotSupportedException=미디어 타입 {0}은(는) 지원되지 않습니다. 지원 타입: {1} problemDetail.org.springframework.web.HttpMediaTypeNotSupportedException.parseError=미디어 타입 파싱 중 오류가 발생했습니다. # HttpMessageNotReadableException problemDetail.title.org.springframework.http.converter.HttpMessageNotReadableException=요청 본문 처리 오류 problemDetail.org.springframework.http.converter.HttpMessageNotReadableException=요청 본문을 읽을 수 없습니다. 데이터 형식을 확인하세요. # HttpMessageNotWritableException problemDetail.title.org.springframework.http.converter.HttpMessageNotWritableException=응답 생성 오류 problemDetail.org.springframework.http.converter.HttpMessageNotWritableException=응답 본문 생성 중 오류가 발생했습니다. # HttpRequestMethodNotSupportedException problemDetail.title.org.springframework.web.HttpRequestMethodNotSupportedException=지원되지 않는 HTTP 메서드 problemDetail.org.springframework.web.HttpRequestMethodNotSupportedException=HTTP 메서드 {0}은(는) 지원되지 않습니다. 사용 가능 메서드: {1} # MethodArgumentNotValidException problemDetail.title.org.springframework.web.bind.MethodArgumentNotValidException=유효성 검증 실패 problemDetail.org.springframework.web.bind.MethodArgumentNotValidException=요청 데이터가 유효하지 않습니다. # MissingRequestHeaderException problemDetail.title.org.springframework.web.bind.MissingRequestHeaderException=헤더 누락 problemDetail.org.springframework.web.bind.MissingRequestHeaderException=필수 헤더 {0}이(가) 누락되었습니다. # MissingServletRequestParameterException problemDetail.title.org.springframework.web.bind.MissingServletRequestParameterException=파라미터 누락 problemDetail.org.springframework.web.bind.MissingServletRequestParameterException=필수 파라미터 {0}이(가) 누락되었습니다. # MissingMatrixVariableException problemDetail.title.org.springframework.web.bind.MissingMatrixVariableException=매트릭스 변수 누락 problemDetail.org.springframework.web.bind.MissingMatrixVariableException=필수 매트릭스 변수 {0}이(가) 누락되었습니다. # MissingPathVariableException problemDetail.title.org.springframework.web.bind.MissingPathVariableException=경로 변수 누락 problemDetail.org.springframework.web.bind.MissingPathVariableException=경로 변수 {0}을(를) 찾을 수 없습니다. # MissingRequestCookieException problemDetail.title.org.springframework.web.bind.MissingRequestCookieException=쿠키 누락 problemDetail.org.springframework.web.bind.MissingRequestCookieException=필수 쿠키 {0}이(가) 누락되었습니다. # MissingServletRequestPartException problemDetail.title.org.springframework.web.multipart.support.MissingServletRequestPartException=파일 데이터 누락 problemDetail.org.springframework.web.multipart.support.MissingServletRequestPartException=필수 파일 또는 폼 데이터 {0}이(가) 누락되었습니다. # NoHandlerFoundException problemDetail.title.org.springframework.web.servlet.NoHandlerFoundException=잘못된 접근 problemDetail.org.springframework.web.servlet.NoHandlerFoundException=요청한 API 엔드포인트가 존재하지 않습니다. # NoResourceFoundException problemDetail.title.org.springframework.web.NoResourceFoundException=리소스 없음 problemDetail.org.springframework.web.NoResourceFoundException=요청한 리소스를 찾을 수 없습니다. # TypeMismatchException problemDetail.title.org.springframework.beans.TypeMismatchException=타입 불일치 problemDetail.org.springframework.beans.TypeMismatchException=프로퍼티 {0}의 값 {1}이(가) 요구되는 타입과 일치하지 않습니다. # UnsatisfiedServletRequestParameterException problemDetail.title.org.springframework.web.bind.UnsatisfiedServletRequestParameterException=파라미터 조건 미충족 problemDetail.org.springframework.web.bind.UnsatisfiedServletRequestParameterException=다음 조건을 충족하지 않았습니다: {0}
@RestControllerAdvice
class ResponseEntityExceptionHandlerAdapter : ResponseEntityExceptionHandler() {
override fun handleMethodArgumentNotValid(
ex: MethodArgumentNotValidException,
headers: HttpHeaders,
status: HttpStatusCode,
request: WebRequest,
): ResponseEntity<in Any>? {
val body: ProblemDetail = ex.updateAndGetBody(messageSource, LocaleContextHolder.getLocale())
val fieldErrors =
ex.fieldErrors.map { fieldError ->
mapOf(
"field" to fieldError.field,
"value" to fieldError.rejectedValue,
"message" to fieldError.defaultMessage,
)
}
body.setProperty("errors", fieldErrors)
return handleExceptionInternal(
ex,
body,
headers,
status,
request,
)
}
}
이렇게 구성하면 Spring MVC 예외 처리 구조에서 ProblemDetail
로 각종 예외를 형식화하고 한국어로 출력할 수 있다.
{
"type": "about:blank",
"title": "유효성 검증 실패",
"status": 400,
"detail": "요청 데이터가 유효하지 않습니다.",
"instance": "/",
"errors": [
{
"field": "reason",
"value": null,
"message": "사유는 비어있을 수 없습니다."
}
]
}
고민 해볼만한 점