diff --git a/build.gradle b/build.gradle index 020869c..ecf83e6 100644 --- a/build.gradle +++ b/build.gradle @@ -31,6 +31,9 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // validation + implementation 'org.springframework.boot:spring-boot-starter-validation' } tasks.named('test') { diff --git a/src/main/java/com/leets/team2/xclone/common/ApiData.java b/src/main/java/com/leets/team2/xclone/common/ApiData.java new file mode 100644 index 0000000..b04c74a --- /dev/null +++ b/src/main/java/com/leets/team2/xclone/common/ApiData.java @@ -0,0 +1,69 @@ +package com.leets.team2.xclone.common; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.leets.team2.xclone.exception.ErrorInfo; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; + +@JsonSerialize +@Builder +@Getter +public class ApiData { + + private Boolean success; + private T data; + private Integer errorCode; + private Object errorMessage; + + public static ResponseEntity> successFrom(HttpStatus httpStatus, T data) { + ApiData apiData = ApiData.builder() + .success(true) + .data(data) + .build(); + return ResponseEntity.status(httpStatus).body(apiData); + } + + public static ResponseEntity> ok(T data) { + ApiData apiData = ApiData.builder() + .success(true) + .data(data) + .build(); + return ResponseEntity.ok(apiData); + } + + public static ResponseEntity> created(T data) { + ApiData apiData = ApiData.builder() + .success(true) + .data(data) + .build(); + return ResponseEntity.status(HttpStatus.CREATED).body(apiData); + } + + public static ResponseEntity> validationFailure(List fieldErrors) { + Map errors = new HashMap<>(); + fieldErrors.forEach( + fieldError -> errors.put(fieldError.getField(), fieldError.getDefaultMessage())); + + ApiData apiData = ApiData.builder() + .success(false) + .errorCode(ErrorInfo.NOT_VALID_REQUEST.getCode()) + .errorMessage(errors) + .build(); + return ResponseEntity.ok(apiData); + } + + public static ResponseEntity> errorFrom(ErrorInfo error) { + ApiData apiData = ApiData.builder() + .success(false) + .errorCode(error.getCode()) + .errorMessage(error.getMessage()) + .build(); + return ResponseEntity.status(error.getStatusCode()).body(apiData); + } +} diff --git a/src/main/java/com/leets/team2/xclone/exception/ApplicationException.java b/src/main/java/com/leets/team2/xclone/exception/ApplicationException.java new file mode 100644 index 0000000..3de2524 --- /dev/null +++ b/src/main/java/com/leets/team2/xclone/exception/ApplicationException.java @@ -0,0 +1,14 @@ +package com.leets.team2.xclone.exception; + +import lombok.Getter; + +@Getter +public class ApplicationException extends RuntimeException { + + protected ErrorInfo errorInfo; + + protected ApplicationException(ErrorInfo errorInfo) { + super(errorInfo.getMessage()); + this.errorInfo = errorInfo; + } +} diff --git a/src/main/java/com/leets/team2/xclone/exception/ErrorInfo.java b/src/main/java/com/leets/team2/xclone/exception/ErrorInfo.java new file mode 100644 index 0000000..d4ff168 --- /dev/null +++ b/src/main/java/com/leets/team2/xclone/exception/ErrorInfo.java @@ -0,0 +1,20 @@ +package com.leets.team2.xclone.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorInfo { + + // validation 실패 + NOT_VALID_REQUEST(HttpStatus.BAD_REQUEST, "요청을 검증하는데 실패했습니다.", 9999), + + // Member 영역 + NO_SUCH_MEMBER(HttpStatus.NOT_FOUND, "해당 멤버를 찾을 수 없습니다.", 10001); + + private final HttpStatus statusCode; + private final String message; + private final int code; +} diff --git a/src/main/java/com/leets/team2/xclone/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/leets/team2/xclone/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..e7e5b44 --- /dev/null +++ b/src/main/java/com/leets/team2/xclone/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,44 @@ +package com.leets.team2.xclone.exception.handler; + +import com.leets.team2.xclone.common.ApiData; +import com.leets.team2.xclone.exception.ApplicationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + private static final String INTERNAL_SERVER_ERROR = "서버에 오류가 발생했습니다.\n잠시후 다시 시도해주세요."; + + @ExceptionHandler(ApplicationException.class) + public ResponseEntity> handleApplicationException(ApplicationException e) { + return ApiData.errorFrom(e.getErrorInfo()); + } + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(RuntimeException.class) + public String handleAnyRunTimeException(RuntimeException e) { + log.warn("Unexpected Error Occurred", e); + return INTERNAL_SERVER_ERROR; + } + + + + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + @ExceptionHandler(Exception.class) + public String handleAnyCheckedException(Exception e) { + log.error("Unexpected Error Occurred", e); + return INTERNAL_SERVER_ERROR; + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + return ApiData.validationFailure(e.getFieldErrors()); + } +}