-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/main/java/com/unit/daybook/global/common/response/GlobalResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.unit.daybook.global.common.response; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import com.unit.daybook.global.error.ErrorResponse; | ||
|
||
public record GlobalResponse(int status, Object data, LocalDateTime timestamp) { | ||
public static GlobalResponse success(int status, Object data) { | ||
return new GlobalResponse(status, data, LocalDateTime.now()); | ||
} | ||
|
||
public static GlobalResponse fail(int status, ErrorResponse errorResponse) { | ||
return new GlobalResponse(status, errorResponse, LocalDateTime.now()); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/com/unit/daybook/global/common/response/GlobalResponseAdvice.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.unit.daybook.global.common.response; | ||
|
||
import org.springframework.core.MethodParameter; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.server.ServerHttpRequest; | ||
import org.springframework.http.server.ServerHttpResponse; | ||
import org.springframework.http.server.ServletServerHttpResponse; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | ||
|
||
import jakarta.servlet.http.HttpServletResponse; | ||
|
||
@RestControllerAdvice(basePackages = "com.unit.daybook") | ||
public class GlobalResponseAdvice implements ResponseBodyAdvice { | ||
@Override | ||
public boolean supports(MethodParameter returnType, Class converterType) { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Object beforeBodyWrite( | ||
Object body, | ||
MethodParameter returnType, | ||
MediaType selectedContentType, | ||
Class selectedConverterType, | ||
ServerHttpRequest request, | ||
ServerHttpResponse response) { | ||
HttpServletResponse servletResponse = | ||
((ServletServerHttpResponse) response).getServletResponse(); | ||
int status = servletResponse.getStatus(); | ||
HttpStatus resolve = HttpStatus.resolve(status); | ||
if (resolve == null || body instanceof String) { | ||
return body; | ||
} | ||
if (resolve.is2xxSuccessful()) { | ||
return GlobalResponse.success(status, body); | ||
} | ||
return body; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/com/unit/daybook/global/error/ErrorResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.unit.daybook.global.error; | ||
|
||
public record ErrorResponse(String errorClassName, String message) { | ||
|
||
public static ErrorResponse of(String errorClassName, String message) { | ||
return new ErrorResponse(errorClassName, message); | ||
} | ||
} |
147 changes: 147 additions & 0 deletions
147
src/main/java/com/unit/daybook/global/error/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package com.unit.daybook.global.error; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.HttpStatusCode; | ||
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.context.request.WebRequest; | ||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||
|
||
import com.unit.daybook.global.common.response.GlobalResponse; | ||
import com.unit.daybook.global.error.exception.CustomException; | ||
import com.unit.daybook.global.error.exception.ErrorCode; | ||
|
||
import jakarta.validation.ConstraintViolationException; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.SneakyThrows; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
@RestControllerAdvice | ||
@RequiredArgsConstructor | ||
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { | ||
|
||
@Override | ||
protected ResponseEntity<Object> handleExceptionInternal( | ||
Exception ex, | ||
Object body, | ||
HttpHeaders headers, | ||
HttpStatusCode statusCode, | ||
WebRequest request) { | ||
ErrorResponse errorResponse = | ||
ErrorResponse.of(ex.getClass().getSimpleName(), ex.getMessage()); | ||
return super.handleExceptionInternal(ex, errorResponse, headers, statusCode, request); | ||
} | ||
|
||
/** | ||
* javax.validation.Valid or @Validated 으로 binding error 발생시 발생한다. HttpMessageConverter 에서 등록한 | ||
* HttpMessageConverter binding 못할경우 발생 주로 @RequestBody, @RequestPart 어노테이션에서 발생 | ||
*/ | ||
@SneakyThrows | ||
@Override | ||
protected ResponseEntity<Object> handleMethodArgumentNotValid( | ||
MethodArgumentNotValidException e, | ||
HttpHeaders headers, | ||
HttpStatusCode status, | ||
WebRequest request) { | ||
log.error("MethodArgumentNotValidException : {}", e.getMessage(), e); | ||
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage(); | ||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(e.getClass().getSimpleName(), errorMessage); | ||
GlobalResponse response = GlobalResponse.fail(status.value(), errorResponse); | ||
return ResponseEntity.status(status).body(response); | ||
} | ||
|
||
/** Request Param Validation 예외 처리 */ | ||
@ExceptionHandler(ConstraintViolationException.class) | ||
public ResponseEntity<GlobalResponse> handleConstraintViolationException( | ||
ConstraintViolationException e) { | ||
log.error("ConstraintViolationException : {}", e.getMessage(), e); | ||
|
||
Map<String, Object> bindingErrors = new HashMap<>(); | ||
e.getConstraintViolations() | ||
.forEach( | ||
constraintViolation -> { | ||
List<String> propertyPath = | ||
List.of( | ||
constraintViolation | ||
.getPropertyPath() | ||
.toString() | ||
.split("\\.")); | ||
String path = | ||
propertyPath.stream() | ||
.skip(propertyPath.size() - 1L) | ||
.findFirst() | ||
.orElse(null); | ||
bindingErrors.put(path, constraintViolation.getMessage()); | ||
}); | ||
|
||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(e.getClass().getSimpleName(), bindingErrors.toString()); | ||
final GlobalResponse response = | ||
GlobalResponse.fail(HttpStatus.BAD_REQUEST.value(), errorResponse); | ||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); | ||
} | ||
|
||
/** PathVariable, RequestParam, RequestHeader, RequestBody 에서 타입이 일치하지 않을 경우 발생 */ | ||
@ExceptionHandler(MethodArgumentTypeMismatchException.class) | ||
protected ResponseEntity<GlobalResponse> handleMethodArgumentTypeMismatchException( | ||
MethodArgumentTypeMismatchException e) { | ||
log.error("MethodArgumentTypeMismatchException : {}", e.getMessage(), e); | ||
final ErrorCode errorCode = ErrorCode.METHOD_ARGUMENT_TYPE_MISMATCH; | ||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage()); | ||
final GlobalResponse response = | ||
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse); | ||
return ResponseEntity.status(errorCode.getStatus()).body(response); | ||
} | ||
|
||
/** 지원하지 않은 HTTP method 호출 할 경우 발생 */ | ||
@Override | ||
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported( | ||
HttpRequestMethodNotSupportedException e, | ||
HttpHeaders headers, | ||
HttpStatusCode status, | ||
WebRequest request) { | ||
log.error("HttpRequestMethodNotSupportedException : {}", e.getMessage(), e); | ||
final ErrorCode errorCode = ErrorCode.METHOD_NOT_ALLOWED; | ||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(e.getClass().getSimpleName(), errorCode.getMessage()); | ||
final GlobalResponse response = | ||
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse); | ||
return ResponseEntity.status(errorCode.getStatus()).body(response); | ||
} | ||
|
||
/** CustomException 예외 처리 */ | ||
@ExceptionHandler(CustomException.class) | ||
public ResponseEntity<GlobalResponse> handleCustomException(CustomException e) { | ||
log.error("CustomException : {}", e.getMessage(), e); | ||
final ErrorCode errorCode = e.getErrorCode(); | ||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(errorCode.name(), errorCode.getMessage()); | ||
final GlobalResponse response = | ||
GlobalResponse.fail(errorCode.getStatus().value(), errorResponse); | ||
return ResponseEntity.status(errorCode.getStatus()).body(response); | ||
} | ||
|
||
/** 500번대 에러 처리 */ | ||
@ExceptionHandler(Exception.class) | ||
protected ResponseEntity<GlobalResponse> handleException(Exception e) { | ||
log.error("Internal Server Error : {}", e.getMessage(), e); | ||
final ErrorCode internalServerError = ErrorCode.INTERNAL_SERVER_ERROR; | ||
final ErrorResponse errorResponse = | ||
ErrorResponse.of(e.getClass().getSimpleName(), internalServerError.getMessage()); | ||
final GlobalResponse response = | ||
GlobalResponse.fail(internalServerError.getStatus().value(), errorResponse); | ||
return ResponseEntity.status(internalServerError.getStatus()).body(response); | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/unit/daybook/global/error/exception/CustomException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.unit.daybook.global.error.exception; | ||
|
||
import lombok.Getter; | ||
|
||
@Getter | ||
public class CustomException extends RuntimeException { | ||
|
||
private final ErrorCode errorCode; | ||
|
||
public CustomException(ErrorCode errorCode) { | ||
super(errorCode.getMessage()); | ||
this.errorCode = errorCode; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/com/unit/daybook/global/error/exception/ErrorCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.unit.daybook.global.error.exception; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
@AllArgsConstructor | ||
public enum ErrorCode { | ||
SAMPLE_ERROR(HttpStatus.BAD_REQUEST, "Sample Error Message"), | ||
|
||
// Common | ||
METHOD_ARGUMENT_TYPE_MISMATCH(HttpStatus.BAD_REQUEST, "요청 한 값 타입이 잘못되어 binding에 실패하였습니다."), | ||
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않는 HTTP method 입니다."), | ||
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 오류, 관리자에게 문의하세요"), | ||
|
||
; | ||
private final HttpStatus status; | ||
private final String message; | ||
} |