-
Notifications
You must be signed in to change notification settings - Fork 0
응답 표준화 및 에러 처리 방법 정의
LeeJongSeon edited this page Oct 28, 2024
·
1 revision
애플리케이션의 예외 처리와 응답 형식을 표준화하여 개발 효율성과 코드 일관성을 높이고자 합니다. 이를 위해 다음과 같은 클래스를 구현했습니다:
-
BaseResponse<T>
: 모든 API 응답의 기본 형태를 정의하는 클래스 -
BusinessException
: 비즈니스 로직에서 발생하는 예외를 처리하기 위한 사용자 정의 예외 클래스 -
GlobalExceptionHandler
: 전역 예외 처리를 담당하는 클래스 -
ErrorCode
열거형(enum): 애플리케이션에서 사용되는 에러 코드를 관리하는 열거형
BaseResponse<T>
클래스는 모든 API 응답의 기본 구조를 정의합니다.
@Getter
@NoArgsConstructor
public class BaseResponse<T> {
private boolean success;
private T data;
private Error error;
// 성공 응답 생성자
public BaseResponse(T data) {
this.success = true;
this.data = data;
this.error = null;
}
// 실패 응답 생성자
public BaseResponse(ErrorCode errorCode) {
this.success = false;
this.data = null;
this.error = new Error(errorCode);
}
@Getter
@NoArgsConstructor
private static class Error {
private String code;
private String message;
Error(ErrorCode errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
}
}
-
success
: 요청의 성공 여부를 나타냅니다. -
data
: 요청이 성공한 경우 반환되는 데이터입니다. -
error
: 요청이 실패한 경우 에러 정보를 담고 있습니다.
BaseResponse<UserDto> response = new BaseResponse<>(userDto);
BaseResponse<Object> response = new BaseResponse<>(ErrorCode.USER_NOT_FOUND);
비즈니스 로직에서 발생할 수 있는 예외를 처리하기 위한 사용자 정의 예외 클래스입니다.
@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
-
errorCode
: 발생한 예외에 대한 에러 코드입니다. - 생성자에서는
errorCode
를 받아 부모 클래스인RuntimeException
에 메시지를 전달합니다.
서비스나 비즈니스 로직에서 특정 조건에 따라 예외를 발생시킬 수 있습니다.
if (user == null) {
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
}
전역적으로 예외를 처리하여 일관된 응답을 제공하는 클래스입니다.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
protected ResponseEntity<BaseResponse<Object>> handleBusinessException(BusinessException e) {
log.error("Business Exception: {}", e.getMessage());
return ResponseEntity
.badRequest()
.body(new BaseResponse<>(e.getErrorCode()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<BaseResponse<Object>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("Validation Exception: {}", e.getMessage());
return ResponseEntity
.badRequest()
.body(new BaseResponse<>(ErrorCode.INVALID_INPUT_VALUE));
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<BaseResponse<Object>> handleException(Exception e) {
log.error("Internal Server Error: {}", e.getMessage());
return ResponseEntity
.internalServerError()
.body(new BaseResponse<>(ErrorCode.INTERNAL_SERVER_ERROR));
}
}
-
@RestControllerAdvice
: 전역 예외 처리를 위한 어노테이션입니다. -
@ExceptionHandler
: 특정 예외를 처리하는 메서드를 지정합니다.
-
BusinessException
- 비즈니스 로직에서 발생하는 예외를 처리합니다.
- HTTP 상태 코드 400(Bad Request)을 반환합니다.
-
MethodArgumentNotValidException
- 요청 데이터의 유효성 검증 실패 시 발생하는 예외를 처리합니다.
- HTTP 상태 코드 400(Bad Request)을 반환합니다.
-
Exception
- 그 외의 모든 예외를 처리합니다.
- HTTP 상태 코드 500(Internal Server Error)을 반환합니다.
애플리케이션에서 발생할 수 있는 에러 코드를 관리하는 열거형입니다.
@Getter
public enum ErrorCode {
// 공통 에러
INVALID_INPUT_VALUE("C001", "Invalid input value"),
INTERNAL_SERVER_ERROR("C002", "Internal server error"),
// 사용자 도메인 에러
USER_NOT_FOUND("U001", "User not found"),
DUPLICATE_EMAIL("U002", "Email already exists"),
// 바이크 도메인 에러
BIKE_NOT_FOUND("B001", "Bike not found"),
BIKE_ALREADY_IN_USE("B002", "Bike is already in use"),
// 대여 도메인 에러
RENTAL_NOT_FOUND("R001", "Rental not found"),
INVALID_RENTAL_STATUS("R002", "Invalid rental status"),
// 결제 도메인 에러
PAYMENT_FAILED("P001", "Payment processing failed"),
INSUFFICIENT_BALANCE("P002", "Insufficient balance");
private final String code;
private final String message;
ErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
}
- 각 에러 코드는 고유한
code
와message
를 가지고 있습니다. - 도메인별로 에러 코드를 구분하여 관리합니다.
에러가 발생할 경우 해당하는 ErrorCode
를 예외나 응답에 사용합니다.
throw new BusinessException(ErrorCode.BIKE_NOT_FOUND);
또는
return new BaseResponse<>(ErrorCode.PAYMENT_FAILED);
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public BaseResponse<UserDto> getUser(@PathVariable Long id) {
UserDto user = userService.findUserById(id);
if (user == null) {
throw new BusinessException(ErrorCode.USER_NOT_FOUND);
}
return new BaseResponse<>(user);
}
}
@Service
public class UserService {
public UserDto findUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
return new UserDto(user);
}
}
- 일관된 응답 형식: 클라이언트는 항상 동일한 형식의 응답을 받으므로 처리 로직이 간소화됩니다.
- 유지 보수성 향상: 에러 코드와 메시지를 중앙에서 관리하므로 변경 사항이 있을 때 쉽게 수정할 수 있습니다.
- 개발 효율성 증대: 개발자들이 예외 처리에 신경 쓰지 않고 비즈니스 로직에 집중할 수 있습니다.
🍎 말고 🍅