From 5e0a52cb477f9213f9221daf0a158bec68c33e4d Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 29 Jul 2025 14:04:35 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20ExceptionHandler=EC=97=90=EC=84=9C?= =?UTF-8?q?=20API=20=EC=97=90=EB=9F=AC=20=EC=9D=91=EB=8B=B5=EC=9D=84=20?= =?UTF-8?q?=EB=A1=9C=EA=B9=85=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionControllerAdvice.kt | 99 ++++++++++++++----- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt b/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt index 5037d01d..25b83831 100644 --- a/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt +++ b/src/main/kotlin/roomescape/common/exception/ExceptionControllerAdvice.kt @@ -2,55 +2,110 @@ package roomescape.common.exception import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging +import org.slf4j.MDC +import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.http.converter.HttpMessageNotReadableException import org.springframework.web.bind.MethodArgumentNotValidException import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.RestControllerAdvice +import roomescape.auth.exception.AuthException import roomescape.common.dto.response.CommonErrorResponse +import roomescape.common.log.ApiLogMessageConverter +import roomescape.common.log.ConvertResponseMessageRequest +import roomescape.common.log.LogType + +private val log: KLogger = KotlinLogging.logger {} @RestControllerAdvice class ExceptionControllerAdvice( - private val log: KLogger = KotlinLogging.logger {} + private val messageConverter: ApiLogMessageConverter ) { @ExceptionHandler(value = [RoomescapeException::class]) fun handleRoomException(e: RoomescapeException): ResponseEntity { val errorCode: ErrorCode = e.errorCode + val httpStatus: HttpStatus = errorCode.httpStatus + val errorResponse = CommonErrorResponse(errorCode) + + val type = if (e is AuthException) LogType.AUTHENTICATION_FAILURE else LogType.APPLICATION_FAILURE + logException( + type = type, + httpStatus = httpStatus.value(), + errorResponse = errorResponse, + exception = e + ) + return ResponseEntity - .status(errorCode.httpStatus) - .body(CommonErrorResponse(errorCode, e.message)) + .status(httpStatus) + .body(errorResponse) } - @ExceptionHandler(value = [HttpMessageNotReadableException::class]) - fun handleHttpMessageNotReadableException(e: HttpMessageNotReadableException): ResponseEntity { - log.debug { "message: ${e.message}" } + @ExceptionHandler(value = [MethodArgumentNotValidException::class, HttpMessageNotReadableException::class]) + fun handleInvalidRequestValueException(e: Exception): ResponseEntity { + val message: String = if (e is MethodArgumentNotValidException) { + e.bindingResult.allErrors + .mapNotNull { it.defaultMessage } + .joinToString(", ") + } else { + e.message!! + } + log.debug { "[ExceptionControllerAdvice] Invalid Request Value Exception occurred: $message" } val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE - return ResponseEntity - .status(errorCode.httpStatus) - .body(CommonErrorResponse(errorCode)) - } + val httpStatus: HttpStatus = errorCode.httpStatus + val errorResponse = CommonErrorResponse(errorCode) - @ExceptionHandler(value = [MethodArgumentNotValidException::class]) - fun handleMethodArgumentNotValidException(e: MethodArgumentNotValidException): ResponseEntity { - val message: String = e.bindingResult.allErrors - .mapNotNull { it.defaultMessage } - .joinToString(", ") - log.debug { "message: $message" } + logException( + type = LogType.APPLICATION_FAILURE, + httpStatus = httpStatus.value(), + errorResponse = errorResponse, + exception = e + ) - val errorCode: ErrorCode = CommonErrorCode.INVALID_INPUT_VALUE return ResponseEntity - .status(errorCode.httpStatus) - .body(CommonErrorResponse(errorCode)) + .status(httpStatus) + .body(errorResponse) } @ExceptionHandler(value = [Exception::class]) fun handleException(e: Exception): ResponseEntity { - log.error(e) { "message: ${e.message}" } + log.error(e) { "[ExceptionControllerAdvice] Unexpected exception occurred: ${e.message}" } val errorCode: ErrorCode = CommonErrorCode.UNEXPECTED_SERVER_ERROR + val httpStatus: HttpStatus = errorCode.httpStatus + val errorResponse = CommonErrorResponse(errorCode) + + logException( + type = LogType.UNHANDLED_EXCEPTION, + httpStatus = httpStatus.value(), + errorResponse = errorResponse, + exception = e + ) + return ResponseEntity - .status(errorCode.httpStatus) - .body(CommonErrorResponse(errorCode)) + .status(httpStatus) + .body(errorResponse) + } + + private fun logException( + type: LogType, + httpStatus: Int, + errorResponse: CommonErrorResponse, + exception: Exception + ) { + val commonRequest = ConvertResponseMessageRequest( + type = type, + httpStatus = httpStatus, + startTime = MDC.get("startTime")?.toLongOrNull(), + body = errorResponse, + ) + + val logMessage = if (errorResponse.message == exception.message) { + messageConverter.convertToResponseMessage(commonRequest) + } else { + messageConverter.convertToResponseMessage(commonRequest.copy(exception = exception)) + } + + log.warn { logMessage } } }