Skip to content

Commit

Permalink
[feat] 표준 응답객체 구현 및 표준 에러 정의 (#7)
Browse files Browse the repository at this point in the history
* feat: 에러를 표준화할 enum 정의

* feat: 에러 응답을 표준화하기위한 예외 클래스 구현

* feat: 표준 응답객체 구현 및 GlobalExceptionHandler구현

* feat: 표준 응답 객체에 created 편의 메서드 추가

* chore: spring starter validation 의존성 추가
  • Loading branch information
soyesenna authored Oct 7, 2024
1 parent f830c93 commit 01c4f48
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/com/leets/team2/xclone/common/ApiData.java
Original file line number Diff line number Diff line change
@@ -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<T> {

private Boolean success;
private T data;
private Integer errorCode;
private Object errorMessage;

public static <T> ResponseEntity<ApiData<T>> successFrom(HttpStatus httpStatus, T data) {
ApiData<T> apiData = ApiData.<T>builder()
.success(true)
.data(data)
.build();
return ResponseEntity.status(httpStatus).body(apiData);
}

public static <T> ResponseEntity<ApiData<T>> ok(T data) {
ApiData<T> apiData = ApiData.<T>builder()
.success(true)
.data(data)
.build();
return ResponseEntity.ok(apiData);
}

public static <T> ResponseEntity<ApiData<T>> created(T data) {
ApiData<T> apiData = ApiData.<T>builder()
.success(true)
.data(data)
.build();
return ResponseEntity.status(HttpStatus.CREATED).body(apiData);
}

public static <T> ResponseEntity<ApiData<T>> validationFailure(List<FieldError> fieldErrors) {
Map<String, String> errors = new HashMap<>();
fieldErrors.forEach(
fieldError -> errors.put(fieldError.getField(), fieldError.getDefaultMessage()));

ApiData<T> apiData = ApiData.<T>builder()
.success(false)
.errorCode(ErrorInfo.NOT_VALID_REQUEST.getCode())
.errorMessage(errors)
.build();
return ResponseEntity.ok(apiData);
}

public static ResponseEntity<ApiData<String>> errorFrom(ErrorInfo error) {
ApiData<String> apiData = ApiData.<String>builder()
.success(false)
.errorCode(error.getCode())
.errorMessage(error.getMessage())
.build();
return ResponseEntity.status(error.getStatusCode()).body(apiData);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
20 changes: 20 additions & 0 deletions src/main/java/com/leets/team2/xclone/exception/ErrorInfo.java
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<ApiData<String>> 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<ApiData<String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
return ApiData.validationFailure(e.getFieldErrors());
}
}

0 comments on commit 01c4f48

Please sign in to comment.