Skip to content

Commit

Permalink
✨ FEAT. 공통 응답 및 예외 핸들러 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
junhaa committed Apr 8, 2024
1 parent 3a89ab9 commit f369364
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/main/java/fairytale/tbd/global/entity/BaseEntity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fairytale.tbd.global.entity;

import java.time.LocalDateTime;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public abstract class BaseEntity {
@CreatedDate
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;
}
13 changes: 13 additions & 0 deletions src/main/java/fairytale/tbd/global/enums/statuscode/BaseCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fairytale.tbd.global.enums.statuscode;

import org.springframework.http.HttpStatus;

public interface BaseCode {
String getCode();

String getMessage();

HttpStatus getHttpStatus();

Integer getStatusValue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package fairytale.tbd.global.enums.statuscode;

import org.springframework.http.HttpStatus;

public enum ErrorStatus implements BaseCode{
// common
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."),
_BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청입니다."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "인증이 필요합니다."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),


// ElevenLabs
_FILE_CONVERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "FILE5001", "파일 변환에 실패했습니다.");


private final HttpStatus httpStatus;
private final String code;
private final String message;

ErrorStatus(HttpStatus httpStatus, String code, String message) {
this.httpStatus = httpStatus;
this.code = code;
this.message = message;
}

@Override
public String getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}

@Override
public HttpStatus getHttpStatus() {
return httpStatus;
}

@Override
public Integer getStatusValue() {
return httpStatus.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package fairytale.tbd.global.enums.statuscode;

import org.springframework.http.HttpStatus;

public enum SuccessStatus implements BaseCode{
_OK(HttpStatus.OK, "COMMON200", "성공입니다."),
_ACCEPTED(HttpStatus.ACCEPTED, "COMMON204", "별도의 응답 데이터가 없으며, 정상 처리되었습니다.");

private final HttpStatus httpStatus;
private final String code;
private final String message;

SuccessStatus(HttpStatus httpStatus, String code, String message) {
this.httpStatus = httpStatus;
this.code = code;
this.message = message;
}

@Override
public String getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}

@Override
public HttpStatus getHttpStatus() {
return httpStatus;
}

@Override
public Integer getStatusValue() {
return httpStatus.value();
}
}
84 changes: 84 additions & 0 deletions src/main/java/fairytale/tbd/global/exception/ExceptionAdvice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package fairytale.tbd.global.exception;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
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.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import fairytale.tbd.global.enums.statuscode.ErrorStatus;
import fairytale.tbd.global.response.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class ExceptionAdvice extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = GeneralException.class)
public ResponseEntity<Object> handleGeneralException(GeneralException exception, HttpServletRequest request) {
return handleExceptionInternal(exception, HttpHeaders.EMPTY, request);
}

@Override
public ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatusCode status, WebRequest request) {

log.info("handleGeneralException 발생 ={}", exception);
Map<String, String> errors = new LinkedHashMap<>();
exception.getBindingResult().getFieldErrors().stream()
.forEach(fieldError -> {

String fieldName = fieldError.getField();
String errorMessage = Optional.ofNullable(fieldError.getDefaultMessage()).orElse("");
errors.merge(fieldName, errorMessage,
(existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage);
});

exception.getBindingResult().getGlobalErrors().stream()
.forEach(globalError -> {
log.info("globalError = {}", globalError);
String objectName = globalError.getObjectName();
String errorMessage = Optional.ofNullable(globalError.getDefaultMessage()).orElse("");
errors.merge(objectName, errorMessage,
(existingErrorMessage, newErrorMessage) -> existingErrorMessage + ", " + newErrorMessage);
});

return handleExceptionInternalArgs(exception, HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"), request,
errors);
}

@ExceptionHandler
public ResponseEntity<Object> handlingException(Exception exception, WebRequest request) {
return handleExceptionInternal(exception, ErrorStatus._INTERNAL_SERVER_ERROR, HttpHeaders.EMPTY,
ErrorStatus._INTERNAL_SERVER_ERROR.getHttpStatus(), request, exception.getMessage());
}

private ResponseEntity<Object> handleExceptionInternal(GeneralException exception, HttpHeaders headers,
HttpServletRequest request) {
ApiResponse<Object> body = ApiResponse.onFailure(exception.getErrorCode(), exception.getErrorReason(), null);
WebRequest webRequest = new ServletWebRequest(request);
return super.handleExceptionInternal(exception, body, headers, exception.getHttpStatus(), webRequest);
}

private ResponseEntity<Object> handleExceptionInternal(Exception exception, ErrorStatus errorStatus,
HttpHeaders headers, HttpStatus status, WebRequest request, String errorPoint) {
ApiResponse<String> body = ApiResponse.onFailure(errorStatus.getCode(), errorStatus.getMessage(), errorPoint);
return super.handleExceptionInternal(exception, body, headers, status, request);
}

private ResponseEntity<Object> handleExceptionInternalArgs(Exception e, HttpHeaders headers,
ErrorStatus errorCommonStatus, WebRequest request, Map<String, String> errorArgs) {
ApiResponse<Object> body = ApiResponse.onFailure(errorCommonStatus.getCode(), errorCommonStatus.getMessage(),
errorArgs);
return super.handleExceptionInternal(e, body, headers, errorCommonStatus.getHttpStatus(), request);
}
}
23 changes: 23 additions & 0 deletions src/main/java/fairytale/tbd/global/exception/GeneralException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package fairytale.tbd.global.exception;

import org.springframework.http.HttpStatus;

import fairytale.tbd.global.enums.statuscode.BaseCode;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class GeneralException extends RuntimeException{
private final BaseCode errorStatus;

public String getErrorCode() {
return errorStatus.getCode();
}

public String getErrorReason() {
return errorStatus.getMessage();
}

public HttpStatus getHttpStatus() {
return errorStatus.getHttpStatus();
}
}
35 changes: 35 additions & 0 deletions src/main/java/fairytale/tbd/global/response/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package fairytale.tbd.global.response;

import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import fairytale.tbd.global.enums.statuscode.BaseCode;
import fairytale.tbd.global.enums.statuscode.SuccessStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"isSuccess", "code", "message", "result"})
public class ApiResponse<T> {

private final Boolean isSuccess;
private final String code;

private final String message;
private T result;

// 성공한 경우 응답 생성

public static <T> ApiResponse<T> onSuccess(T result) {
return new ApiResponse<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), result);
}

public static <T> ApiResponse<T> of(boolean isSuccess, BaseCode code, T result) {
return new ApiResponse<>(isSuccess, code.getCode(), code.getMessage(), result);
}

// 실패한 경우 응답 생성
public static <T> ApiResponse<T> onFailure(String code, String message, T data) {
return new ApiResponse<>(false, code, message, data);
}
}

0 comments on commit f369364

Please sign in to comment.