package com.takensoft.common.exception; import com.takensoft.common.message.MessageCode; import com.takensoft.common.util.ResponseUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException; import org.springframework.dao.DataAccessException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.servlet.resource.NoResourceFoundException; import java.net.UnknownHostException; /** * @author takensoft * @since 2025.01.22 * @modification * since | author | description * 2025.01.22 | takensoft | 최초 등록 * 2025.03.12 | 하석형 | handleCustomCodeDuplicationException, handleCustomDataDuplicationException 추가 * 2025.03.21 | 하석형 | handleFileSizeLimitExceededException, handleCustomFileUploadFailException, handleCustomPrhibtWordException 추가 * * 스프링 MVC 컨트롤러에서 발생하는 예외를 처리하는 공통 클래스 */ @RestControllerAdvice @Slf4j @RequiredArgsConstructor public class GlobalExceptionHandler { private final ResponseUtil resUtil; /** * @param e - 처리할 예외 객체 * * 예외를 로그로 출력 */ private void logError(Exception e) { StackTraceElement[] stackTrace = e.getStackTrace(); if(stackTrace.length > 0) { StackTraceElement origin = stackTrace[0]; // 예외가 발생한 첫 번째 위치 log.error("[ {} ] - {} ({} [{}]번째 행)", e.getClass().getSimpleName(), e.getMessage(), origin.getFileName(), origin.getLineNumber() ); } else { log.error("[ {} ] - {}", e.getClass().getSimpleName(), e.getMessage()); } e.printStackTrace(); } /** * @param dae - DataAccessException 예외 객체 * @return 유효성 검사 실패에 대한 HTTP 응답 * * SQL 예외 처리 */ @ExceptionHandler(DataAccessException.class) public ResponseEntity handleDataAccessException(DataAccessException dae) { logError(dae); // String msg = dae.getMessage().toLowerCase(); // if(msg.contains("null value")) { // return resUtil.errorRes(MessageCode.SQL_NULL_VALUE); // } else if(msg.contains("duplicate key")) { // return resUtil.errorRes(MessageCode.SQL_DUPLICATE_KEY); // } return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR); } /** * @param mave - MethodArgumentNotValidException 예외 객체 * @return 유효성 검사 실패에 대한 HTTP 응답 * * 유효성 검증에 실패한 경우 */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity handleValidationException(MethodArgumentNotValidException mave) { logError(mave); String message = mave.getBindingResult().getFieldErrors().stream() .findFirst() .map(error -> error.getDefaultMessage()) .orElse("유효성 검증에 실패했습니다."); return resUtil.errorRes(HttpStatus.BAD_REQUEST, message); } /** * @param ne - NullPointerException 예외 객체 * @return NullPointerException에 대한 HTTP 응답 * * NullPointerException이 발생한 경우 */ @ExceptionHandler(NullPointerException.class) public ResponseEntity handleNullPointerException(NullPointerException ne) { logError(ne); return resUtil.errorRes(MessageCode.COMMON_NULL_POINT); } /** * @param ie - IllegalArgumentException 예외 객체 * @return IllegalArgumentException에 대한 HTTP 응답 * * IllegalArgumentException이 발생한 경우 */ @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ie) { logError(ie); return resUtil.errorRes(MessageCode.COMMON_ILLEGAL_ARGUMENT); } /** * @param nrfe - NoResourceFoundException 예외 객체 * @return NoResourceFoundException 대한 HTTP 응답 * * NoResourceFoundException이 발생한 경우 */ @ExceptionHandler(NoResourceFoundException.class) public ResponseEntity handleNoResourceFoundException(NoResourceFoundException nrfe) { logError(nrfe); return resUtil.errorRes(MessageCode.COMMON_BAD_REQUEST); } /** * @param hrmnse - HttpRequestMethodNotSupportedException 예외 객체 * @return HttpRequestMethodNotSupportedException 대한 HTTP 응답 * * HttpRequestMethodNotSupportedException이 발생한 경우 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseEntity handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException hrmnse) { logError(hrmnse); return resUtil.errorRes(MessageCode.COMMON_METHOD_NOT_ALLOWED); } /** * @param cite - CustomIdTakenException 예외 객체 * @return CustomIdTakenException에 대한 HTTP 응답 * * CustomIdTakenException이 발생한 경우 */ @ExceptionHandler(CustomIdTakenException.class) public ResponseEntity handleCustomIdTakenException(CustomIdTakenException cite) { logError(cite); return resUtil.errorRes(MessageCode.SIGNUP_ID_TAKEN); } /** * @param cbre - CustomBadRequestException 예외 객체 * @return CustomBadRequestException에 대한 HTTP 응답 * * CustomBadRequestException이 발생한 경우 */ @ExceptionHandler(CustomBadRequestException.class) public ResponseEntity handleCustomBadRequestException(CustomBadRequestException cbre) { logError(cbre); return resUtil.errorRes(MessageCode.COMMON_BAD_REQUEST); } /** * @param cade - CustomAccessDeniedException 예외 객체 * @return CustomAccessDeniedException에 대한 HTTP 응답 * * CustomAccessDeniedException이 발생한 경우 */ @ExceptionHandler(CustomAccessDeniedException.class) public ResponseEntity handleCustomAccessDeniedException(CustomAccessDeniedException cade) { logError(cade); return resUtil.errorRes(MessageCode.ACCESS_DENIED); } /** * @param cpce - CustomPasswordComparisonException 예외 객체 * @return CustomPasswordComparisonException에 대한 HTTP 응답 * * CustomPasswordComparisonException이 발생한 경우 */ @ExceptionHandler(CustomPasswordComparisonException.class) public ResponseEntity handleCustomPasswordComparisonException(CustomPasswordComparisonException cpce) { logError(cpce); return resUtil.errorRes(MessageCode.LOGIN_INVALID_CREDENTIALS); } /** * @param cfe - CustomNotFoundException 예외 객체 * @return CustomNotFoundException에 대한 HTTP 응답 * * CustomNotFoundException 발생한 경우 */ @ExceptionHandler(CustomNotFoundException.class) public ResponseEntity handleNotFoundException(CustomNotFoundException cfe) { logError(cfe); return resUtil.errorRes(MessageCode.COMMON_NOT_FOUND); } /** * @param cife - CustomInsertFailException 예외 객체 * @return CustomInsertFailException에 대한 HTTP 응답 * * CustomInsertFailException이 발생한 경우 */ @ExceptionHandler(CustomInsertFailException.class) public ResponseEntity handleCustomInsertFailException(CustomInsertFailException cife) { logError(cife); return resUtil.errorRes(MessageCode.COMMON_INSERT_FAIL); } /** * @param cufe - CustomUpdateFailException 예외 객체 * @return CustomUpdateFailException에 대한 HTTP 응답 * * CustomUpdateFailException이 발생한 경우 */ @ExceptionHandler(CustomUpdateFailException.class) public ResponseEntity handleCustomUpdateFailException(CustomUpdateFailException cufe) { logError(cufe); return resUtil.errorRes(MessageCode.COMMON_UPDATE_FAIL); } /** * @param cdfe - CustomDeleteFailException 예외 객체 * @return CustomDeleteFailException에 대한 HTTP 응답 * * CustomDeleteFailException 발생한 경우 */ @ExceptionHandler(CustomDeleteFailException.class) public ResponseEntity handleCustomDeleteFailException(CustomDeleteFailException cdfe) { logError(cdfe); return resUtil.errorRes(MessageCode.COMMON_DELETE_FAIL); } /** * @param ukhe - UnknownHostException 예외 객체 * @return UnknownHostException에 대한 HTTP 응답 * * UnknownHostException 발생한 경우 */ @ExceptionHandler(UnknownHostException.class) public ResponseEntity handleUnknownHostException(UnknownHostException ukhe) { logError(ukhe); return resUtil.errorRes(MessageCode.NETWORK_UNKNOWN_HOST); } /** * @param ccde - CustomCodeDuplicationException 예외 객체 * @return CustomCodeDuplicationException 대한 HTTP 응답 * * CustomCodeDuplicationException 발생한 경우 */ @ExceptionHandler(CustomCodeDuplicationException.class) public ResponseEntity handleCustomCodeDuplicationException(CustomCodeDuplicationException ccde) { logError(ccde); return resUtil.errorRes(MessageCode.COMMON_DUPLICATION_CODE); } /** * @param cdde - CustomDataDuplicationException 예외 객체 * @return CustomDataDuplicationException 대한 HTTP 응답 * * CustomDataDuplicationException 발생한 경우 */ @ExceptionHandler(CustomDataDuplicationException.class) public ResponseEntity handleCustomDataDuplicationException(CustomDataDuplicationException cdde) { logError(cdde); return resUtil.errorRes(MessageCode.COMMON_DUPLICATION_DATA); } /** * @param fsle - FileSizeLimitExceededException 예외 객체 * @return FileSizeLimitExceededException 대한 HTTP 응답 * * FileSizeLimitExceededException 발생한 경우 */ @ExceptionHandler(FileSizeLimitExceededException.class) public ResponseEntity handleFileSizeLimitExceededException(FileSizeLimitExceededException fsle) { logError(fsle); return resUtil.errorRes(MessageCode.COMMON_PAYLOAD_TOO_LARGE); } /** * @param cfufe - CustomFileUploadFailException 예외 객체 * @return CustomFileUploadFailException 대한 HTTP 응답 * * CustomFileUploadFailException 발생한 경우 */ @ExceptionHandler(CustomFileUploadFailException.class) public ResponseEntity handleCustomFileUploadFailException(CustomFileUploadFailException cfufe) { logError(cfufe); return resUtil.errorRes(MessageCode.FILE_UPLOAD_FAIL); } /** * @param cpwe - CustomPrhibtWordException 예외 객체 * @return CustomPrhibtWordException 대한 HTTP 응답 * * CustomPrhibtWordException 발생한 경우 */ @ExceptionHandler(CustomPrhibtWordException.class) public ResponseEntity handleCustomPrhibtWordException(CustomPrhibtWordException cpwe) { logError(cpwe); return resUtil.errorRes(MessageCode.COMMON_PROHIBITION_WORD, "\n* " + cpwe.getWord()); } /** * @param e - Exception 예외 객체 * @return 기타 예외에 대한 HTTP 응답 * * 그 외 모든 예외가 발생한 경우 */ @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception e) { logError(e); return resUtil.errorRes(MessageCode.COMMON_UNKNOWN_ERROR); } }