exception(ApiExceptionAdivice, CustomException, ErrorCode)
ApiExceptionAdivice
package com.zerobase.cms.order.exception;
import javax.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class ApiExceptionAdvice {
@ExceptionHandler({CustomException.class})
public ResponseEntity<CustomException.CustomExceptionResponse> exceptionHandler (HttpServletRequest request,
final CustomException e) {
return ResponseEntity
.status(e.getStatus())
.body(CustomException.CustomExceptionResponse.builder()
.message(e.getMessage())
.code(e.getErrorCode().name())
.status(e.getStatus())
.build());
}
@Getter
@ToString
@AllArgsConstructor
public static class ExceptionResponse {
private String message;
private ErrorCode errorCode;
}
}
1. 클래스 선언
- @RestControllerAdvice: Spring 프레임워크에게 이 클래스가 전역 예외 처리기(Global Exception Handler)임을 알려줍니다. 즉, 애플리케이션 전체에서 발생하는 예외를 이 클래스에서 처리할 수 있습니다.
- public class ApiExceptionAdvice: ApiExceptionAdvice라는 이름의 클래스를 정의합니다. public은 이 클래스가 어디서든 접근 가능하다는 의미입니다.
2. 예외 처리 메서드
- @ExceptionHandler({CustomException.class}): 이 메서드가 CustomException 타입의 예외를 처리한다는 것을 Spring에게 알려줍니다. 즉, 애플리케이션에서 CustomException이 발생하면 이 메서드가 자동으로 실행됩니다. 여러 개의 예외를 처리하고 싶다면 @ExceptionHandler({ExceptionA.class, ExceptionB.class})와 같이 작성할 수 있습니다.
- public ResponseEntity<CustomException.CustomExceptionResponse> exceptionHandler (HttpServletRequest request, final CustomException e): exceptionHandler라는 이름의 메서드를 정의합니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- ResponseEntity<CustomException.CustomExceptionResponse>: 이 메서드는 ResponseEntity 객체를 반환합니다. ResponseEntity는 HTTP 응답을 나타내는 클래스로, 상태 코드, 헤더, 본문을 포함할 수 있습니다. 여기서는 CustomException.CustomExceptionResponse 객체를 본문에 담아 반환합니다.
- HttpServletRequest request: HTTP 요청 정보를 담고 있는 객체입니다. 예외 처리 시 요청 정보를 활용할 수 있습니다.
- final CustomException e: 발생한 CustomException 객체입니다. final은 이 변수가 메서드 내에서 변경되지 않음을 나타냅니다.
- return ResponseEntity ...: 예외 정보를 담은 HTTP 응답을 생성하여 반환합니다.
- ResponseEntity.status(e.getStatus()): 응답의 HTTP 상태 코드를 설정합니다. e.getStatus()는 CustomException 객체에 정의된 상태 코드를 가져오는 메서드입니다. 예를 들어, 400 (Bad Request), 404 (Not Found), 500 (Internal Server Error) 등이 있습니다.
- body(CustomException.CustomExceptionResponse.builder() ... build()): 응답의 본문(body)을 설정합니다.
- CustomException.CustomExceptionResponse.builder(): CustomExceptionResponse 클래스의 빌더 패턴을 사용하여 객체를 생성합니다. 빌더 패턴은 객체 생성을 더 유연하고 가독성 좋게 만들어줍니다.
- .message(e.getMessage()): 응답 본문에 예외 메시지를 설정합니다. e.getMessage()는 CustomException 객체에 정의된 메시지를 가져오는 메서드입니다.
- .code(e.getErrorCode().name()): 응답 본문에 예외 코드를 설정합니다. e.getErrorCode().name()은 CustomException 객체에 정의된 에러 코드의 이름을 가져오는 메서드입니다. name() 메서드는 enum 타입의 이름을 문자열로 반환합니다.
- .status(e.getStatus()): 응답 본문에 HTTP 상태 코드를 설정합니다.
- .build(): 빌더 패턴으로 설정한 값들을 사용하여 CustomExceptionResponse 객체를 생성합니다.
3. ExceptionResponse 클래스 (내부 클래스)
- @Getter: Lombok 어노테이션으로, message와 errorCode 필드에 대한 getter 메서드를 자동으로 생성합니다. getter 메서드는 필드의 값을 읽어오는 데 사용됩니다.
- @ToString: Lombok 어노테이션으로, toString() 메서드를 자동으로 생성합니다. toString() 메서드는 객체의 내용을 문자열로 표현하는 데 사용됩니다.
- @AllArgsConstructor: Lombok 어노테이션으로, message와 errorCode 필드를 모두 매개변수로 받는 생성자를 자동으로 생성합니다.
- public static class ExceptionResponse: ExceptionResponse라는 이름의 내부 클래스를 정의합니다.
- public static: 이 클래스는 어디서든 접근 가능하며, ApiExceptionAdvice 클래스의 인스턴스 없이도 사용할 수 있습니다.
- class: 클래스를 정의하는 키워드입니다.
- private String message: 예외 메시지를 저장하는 message 필드를 정의합니다. private은 이 필드가 ExceptionResponse 클래스 내에서만 접근 가능하다는 의미입니다.
- private ErrorCode errorCode: 예외 코드를 저장하는 errorCode 필드를 정의합니다. ErrorCode는 enum 타입으로, 예외 코드를 열거형으로 관리하는 데 사용됩니다.
요약
이 코드는 애플리케이션에서 발생하는 CustomException을 처리하고, 예외 정보를 일관된 형식으로 응답하는 기능을 제공합니다. @RestControllerAdvice 어노테이션을 사용하여 전역 예외 처리기로 등록하고, @ExceptionHandler 어노테이션을 사용하여 CustomException을 처리하는 메서드를 정의합니다. 예외 발생 시 HTTP 상태 코드, 메시지, 에러 코드를 포함한 ResponseEntity를 반환하여 사용자에게 유용한 오류 정보를 제공합니다. Lombok 라이브러리를 사용하여 getter 메서드, toString() 메서드, 생성자를 자동으로 생성하여 코드의 간결성을 높였습니다.
CustomException
package com.zerobase.cms.order.exception;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
public class CustomException extends RuntimeException {
private final ErrorCode errorCode;
private final int status;
private static final ObjectMapper mapper = new ObjectMapper();
public CustomException(ErrorCode errorCode) {
super(errorCode.getDetail());
this.errorCode = errorCode;
this.status = errorCode.getHttpStatus().value();
}
@AllArgsConstructor
@Builder
@NoArgsConstructor
@Getter
public static class CustomExceptionResponse {
private int status;
private String code;
private String message;
}
}
1. 클래스 선언
- @Getter: Lombok 어노테이션으로, 이 클래스의 모든 필드에 대한 getter 메서드를 자동으로 생성합니다.
- public class CustomException extends RuntimeException: CustomException이라는 이름의 클래스를 정의합니다.
- public: 이 클래스는 어디서든 접근 가능합니다.
- extends RuntimeException: CustomException 클래스가 RuntimeException 클래스를 상속받는다는 의미입니다. RuntimeException은 자바에서 예외를 나타내는 클래스이며, RuntimeException을 상속받는 클래스는 unchecked exception으로 분류됩니다. unchecked exception은 컴파일러가 예외 처리 여부를 강제하지 않습니다.
2. 멤버 변수 선언
- private final ErrorCode errorCode: ErrorCode 타입의 errorCode라는 멤버 변수를 선언합니다.
- private: 이 변수는 현재 클래스 내에서만 접근 가능합니다.
- final: 이 변수는 한 번 값이 할당되면 변경할 수 없습니다.
- ErrorCode: 예외 코드를 나타내는 enum 타입입니다. (예: NOT_FOUND, INVALID_INPUT 등)
- private final int status: HTTP 상태 코드를 저장하는 status 필드를 정의합니다.
- private: 이 변수는 현재 클래스 내에서만 접근 가능합니다.
- final: 이 변수는 한 번 값이 할당되면 변경할 수 없습니다.
- int: 정수형 데이터를 저장하는 변수입니다.
- private static final ObjectMapper mapper = new ObjectMapper(): ObjectMapper 타입의 mapper라는 멤버 변수를 선언합니다.
- private: 이 변수는 현재 클래스 내에서만 접근 가능합니다.
- static: 이 변수는 클래스의 모든 인스턴스가 공유합니다. 즉, CustomException 클래스의 모든 객체는 동일한 mapper 객체를 사용합니다.
- final: 이 변수는 한 번 값이 할당되면 변경할 수 없습니다.
- ObjectMapper: Jackson 라이브러리의 클래스로, 자바 객체를 JSON 형식으로 변환하거나 JSON 형식을 자바 객체로 변환하는 데 사용됩니다.
3. 생성자
- public CustomException(ErrorCode errorCode): CustomException 클래스의 생성자를 정의합니다.
- public: 이 생성자는 어디서든 호출 가능합니다.
- ErrorCode errorCode: ErrorCode 타입의 errorCode라는 매개변수를 받습니다. 이 매개변수는 예외 코드입니다.
- super(errorCode.getDetail()): 부모 클래스인 RuntimeException의 생성자를 호출합니다.
- super(): 부모 클래스의 생성자를 호출하는 키워드입니다.
- errorCode.getDetail(): ErrorCode enum에 정의된 상세 메시지를 가져와서 예외 메시지로 설정합니다.
- this.errorCode = errorCode: errorCode 멤버 변수에 매개변수로 받은 errorCode 값을 할당합니다.
- this: 현재 객체를 가리키는 키워드입니다.
- this.status = errorCode.getHttpStatus().value(): status 멤버 변수에 errorCode에 정의된 HTTP 상태 코드를 할당합니다.
- errorCode.getHttpStatus().value(): ErrorCode enum에 정의된 HTTP 상태 코드를 가져와서 정수 값으로 변환합니다.
6. CustomExceptionResponse 클래스 (내부 클래스)
- @AllArgsConstructor: Lombok 어노테이션으로, 모든 필드(status, code, message)를 매개변수로 받는 생성자를 자동으로 생성합니다.
- @Builder: Lombok 어노테이션으로, 빌더 패턴을 자동으로 생성합니다. 빌더 패턴은 객체 생성을 더 유연하고 가독성 좋게 만들어줍니다.
- @NoArgsConstructor: Lombok 어노테이션으로, 매개변수가 없는 기본 생성자를 자동으로 생성합니다.
- @Getter: Lombok 어노테이션으로, 각 필드(status, code, message)에 대한 getter 메서드를 자동으로 생성합니다.
- public static class CustomExceptionResponse: CustomExceptionResponse라는 이름의 내부 클래스를 정의합니다.
- public static: 이 클래스는 어디서든 접근 가능하며, CustomException 클래스의 인스턴스 없이도 사용할 수 있습니다.
- class: 클래스를 정의하는 키워드입니다.
- private int status: HTTP 상태 코드를 저장하는 status 필드를 정의합니다.
- private: 이 필드는 CustomExceptionResponse 클래스 내에서만 접근 가능합니다.
- int: 정수형 데이터를 저장하는 변수입니다.
- private String code: 예외 코드를 저장하는 code 필드를 정의합니다.
- private: 이 필드는 CustomExceptionResponse 클래스 내에서만 접근 가능합니다.
- String: 문자열 데이터를 저장하는 변수입니다.
- private String message: 예외 메시지를 저장하는 message 필드를 정의합니다.
- private: 이 필드는 CustomExceptionResponse 클래스 내에서만 접근 가능합니다.
- String: 문자열 데이터를 저장하는 변수입니다.
요약
이 코드는 애플리케이션에서 발생하는 특정 오류 상황을 나타내기 위해 CustomException이라는 사용자 정의 예외 클래스를 정의합니다. CustomException은 ErrorCode와 HTTP 상태 코드를 가지며, 예외 발생 시 해당 정보를 함께 전달할 수 있습니다. 또한, CustomExceptionResponse 내부 클래스를 통해 예외 정보를 일관된 형식으로 표현하고, Lombok 라이브러리를 사용하여 코드의 간결성을 높였습니다. 이러한 방식으로 애플리케이션의 예외 처리를 더 명확하고 효율적으로 관리할 수 있습니다.
ErrorCode
package com.zerobase.cms.order.exception;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
@RequiredArgsConstructor
@Getter
public enum ErrorCode {
NOT_FOUND_PRODUCT(HttpStatus.NOT_FOUND, "상품을 찾을 수 없습니다."),
NOT_FOUND_ITEM(HttpStatus.NOT_FOUND, "아이템을 찾을 수 없습니다."),
SAME_ITEM_NAME(HttpStatus.BAD_REQUEST, "아이템 명 중복 입니다."),
CART_CHANGE_FAIL(HttpStatus.BAD_REQUEST, "장바구니에 추가할 수 없습니다."),
ITEM_COUNT_NOT_ENOUGH(HttpStatus.BAD_REQUEST, "상품의 수량이 부족합니다."),
ORDER_FAIL_CHECK_CART(HttpStatus.BAD_REQUEST, "주문 불가! 장바구니를 확인해 주세요."),
ORDER_FAIL_NO_MONEY(HttpStatus.BAD_REQUEST, "주문 불가! 잔액 부족입니다.")
;
private final HttpStatus httpStatus;
private final String detail;
}
1. 열거형 선언
- @RequiredArgsConstructor: Lombok 어노테이션입니다. 이 열거형의 모든 final 필드(여기서는 httpStatus와 detail)를 매개변수로 받는 생성자를 자동으로 생성해줍니다.
- @Getter: Lombok 어노테이션입니다. 이 열거형의 모든 필드에 대한 getter 메서드를 자동으로 생성해줍니다. 즉, getHttpStatus()와 getDetail() 메서드가 자동으로 생성됩니다.
- public enum ErrorCode: ErrorCode라는 이름의 열거형을 정의합니다.
- public: 이 열거형은 어디서든 접근 가능합니다.
- enum: 열거형을 정의하는 키워드입니다.
- ErrorCode: 열거형의 이름입니다.
2. 열거형 상수 정의
- 이 부분은 ErrorCode 열거형의 상수들을 정의합니다. 각 상수는 애플리케이션에서 발생할 수 있는 특정 오류 상황을 나타냅니다.
- 각 상수는 NOT_FOUND_PRODUCT, NOT_FOUND_ITEM 등과 같이 대문자와 언더스코어(_)로 이루어진 이름을 가집니다. 이는 상수 이름을 짓는 일반적인 관례입니다.
- 각 상수는 괄호 안에 두 개의 값을 가집니다. 첫 번째 값은 HttpStatus 타입의 HTTP 상태 코드이고, 두 번째 값은 오류에 대한 상세 메시지를 나타내는 문자열입니다.
- 예를 들어, NOT_FOUND_PRODUCT(HttpStatus.NOT_FOUND, "상품을 찾을 수 없습니다.")는 "상품을 찾을 수 없음" 오류가 발생했을 때 HTTP 상태 코드 404 Not Found를 사용하고, 사용자에게 "상품을 찾을 수 없습니다."라는 메시지를 보여주기 위해 정의되었습니다.
3. 필드 선언
- 이 부분은 ErrorCode 열거형의 필드를 선언합니다. 각 필드는 열거형 상수가 가질 수 있는 데이터를 정의합니다.
- private final HttpStatus httpStatus: HTTP 상태 코드를 저장하는 httpStatus 필드를 정의합니다.
- private: 이 필드는 ErrorCode 열거형 내에서만 접근 가능합니다.
- final: 이 필드는 한 번 값이 할당되면 변경할 수 없습니다.
- HttpStatus: Spring 프레임워크에서 제공하는 열거형으로, HTTP 상태 코드를 정의합니다.
- private final String detail: 오류에 대한 상세 메시지를 저장하는 detail 필드를 정의합니다.
- private: 이 필드는 ErrorCode 열거형 내에서만 접근 가능합니다.
- final: 이 필드는 한 번 값이 할당되면 변경할 수 없습니다.
- String: 문자열 데이터를 저장하는 변수입니다.
요약
이 코드는 애플리케이션에서 발생할 수 있는 다양한 오류 상황을 ErrorCode라는 열거형으로 정의하고 관리합니다. 각 오류 상황은 HTTP 상태 코드와 상세 메시지를 가지며, Lombok 라이브러리를 사용하여 getter 메서드와 생성자를 자동으로 생성하여 코드의 간결성을 높였습니다. 이렇게 정의된 ErrorCode는 예외 처리 과정에서 유용하게 사용될 수 있으며, 코드의 가독성과 유지보수성을 향상시키는 데 기여합니다.