티스토리 뷰
💡 ORM: Object-Relational Mapping
- Object: **"객체"**지향 언어 (자바, 파이썬) Relational: "관계형" 데이터베이스 (H2, MySQL)
https://jeong-pro.tistory.com/195
스프링에서 예외처리는 크게 3가지로 나눌 수 있다.
- 컨트롤러단에서 처리 Controller Level - @ExceptionHandler
- 전역 처리 Global Level - @ControllerAdvice
- 메서드단위 처리 Method Level - try/catch
(@ExceptionHandler, @ControllerAdvice)
- @ControllerAdvice 를 통한 모든 Controller에서 발생한 예외를 처리
- @Exceptionhandler를 통한 특정 Controller의 예외처리
- @ControllerAdvice로 모든 컨트롤러에서 발생할 예외를 정의하고, @ExceptionHandler를 통해 발생하는 예외마다 처리할 몌소드를 정의
Checked Exception | Unchecked Exception | |
처리여부 | 반드시 예외처리 필요 | 명시적 처리 강제하지 않음 |
확인시점 | 컴파일 단계 | 실행중 단계 |
예외발생시 트랜잭션 | 롤백하지 않음 | 롤백함 |
대표 예외 | IOException SQLException |
NullPointException IlligalArgumentException |
@ControllerAdvice / RestControllerAdvice
@Controller나 @RestController에서 발생하는 예외를 한 곳에서 관리하고 처리할 수 있게 하는 어노테이션
: 설정을 통해 범위 지정이 가능하며, Default 값으로는 모든 Controller에 대해 예외처리 관리
- ex) @RestControllerAdvice(basePackages= "com.example.board")
예외 발생시 json 형태로 결과를 반환하기 위해서는 @RestControllerAdvice를 사용
@ExceptionHandler
예외 처리 상황이 발생하면 해당 Handler로 처리하겠다고 명시
어노테이션 뒤에 괄호를 붙여 어떤 ExceptionClass를 처리할지 설정
- @ExceptionHandler(00Exception.class)
전역설정(ControllerAdvice)보다 지역 설정(Controller)로 정의한 Handler가 우선순위를 가
Custom Exception
Custom Exception 을 발생시키는 기준은 해당 Request 가 과연 Server Application의 잘못으로 일어난 잘못인가 사용자의 잘못된 요청으로 발생하는 문제인가를 잘 판단해야 한다.
HttpStatus
HttpStatus는 Enum Class
EnumClass란?: 클래스처럼 보이는 상수, 서로 관련있는 상수들을 모아 심볼릭한 명칭의 집합으로 정의 한 것
error type: HttpStatus의 reasonPrase
error code: HttpStatus의 Value
message: 상황별 디테일 메세지
@ExceptionHandler 같은 경우는 @Controller, @RestController가 적용된 Bean내에서 발생하는 예외를 잡아서 하나의 메서드에서 처리해주는 기능을 한다.
외래키 설정
OneToMany
ManyToOne
OneToOne
ManyToMany
일대다(1:N) 단방향 연관 관계 매핑이 필요한 경우는 그냥 다대일(N:1) 양방향 연관 관계를 매핑해버리는게 추후에 유지보수에 훨씬 수월하기 때문에 이 방식을 채택하는 것을 추천
Mock Object
각 테스트 케이스는 서로 분리되어야 한다.
설정한 Custom Exception
Constants.java
public class Constants {
public enum ExceptionClass {
MIN_ORDER_PRICE("최소 주문 금액"), DELIVERY_FEE("배달비");
private String exceptionClass;
ExceptionClass(String exceptionClass) {
this.exceptionClass = exceptionClass;
}
public String getExceptionClass() {
return exceptionClass;
}
@Override
public String toString() {
return getExceptionClass() + " 오류 발생. ";
}
}
}
exception 패키지 내: RestaurantRegisterException
public class RestaurantRegisterException extends Exception{
private static final long serialVersionUID = 827419284719293L;
private Constants.ExceptionClass exceptionClass;
private HttpStatus httpStatus;
public RestaurantRegisterException(Constants.ExceptionClass exceptionClass, HttpStatus httpStatus, String message) {
super(exceptionClass.toString() + message);
this.exceptionClass = exceptionClass;
this.httpStatus = httpStatus;
}
public Constants.ExceptionClass getExceptionClass(){
return exceptionClass;
}
public int getHttpStatusCode() {
return httpStatus.value();
}
public String getHttpStatusType() {
return httpStatus.getReasonPhrase();
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
}
exception 패키지 내: RestaurantRegisterExceptionHandler
@ExceptionHandler(value = RestaurantRegisterException.class)
public ResponseEntity<Map<String, String>> ExceptionHandler(RestaurantRegisterException e) {
HttpHeaders responseHeaders = new HttpHeaders();
Map<String, String> map = new HashMap<>();
map.put("error type", e.getHttpStatusType());
map.put("code", Integer.toString(e.getHttpStatusCode()));
map.put("message", e.getMessage());
return new ResponseEntity<>(map, responseHeaders, e.getHttpStatus());
}
validator 패키지 내
@Component
public class RestaurantValidator {
public static void isValidPrice(int minOrderPrice, int deliveryFee) throws RestaurantRegisterException {
if(minOrderPrice < 1000 || minOrderPrice > 100000){
throw new RestaurantRegisterException(Constants.ExceptionClass.MIN_ORDER_PRICE, HttpStatus.BAD_REQUEST, "최소주문 가격은 1,000 ~ 100,000원 사이의 값으로 입력해주세요.");
}
if(minOrderPrice % 100 != 0){
throw new RestaurantRegisterException(Constants.ExceptionClass.MIN_ORDER_PRICE, HttpStatus.BAD_REQUEST, "최소주문 가격은 100원 단위로 입력해주세요.");
}
if(deliveryFee < 0 || deliveryFee > 10000){
throw new RestaurantRegisterException(Constants.ExceptionClass.DELIVERY_FEE, HttpStatus.BAD_REQUEST, "기본 배달비는 0 ~ 10,000원 사이의 값으로 입력해주세요.");
}
if(deliveryFee % 500 != 0){
throw new RestaurantRegisterException(Constants.ExceptionClass.DELIVERY_FEE, HttpStatus.BAD_REQUEST, "기본 배달비는 500원 단위로 입력해주세요.");
}
}
}
테스트 코드
class RestaurantControllerTest {
private String name;
private int minOrderPrice;
private int deliveryFee;
@BeforeEach
void setup() {
name = "동대문 엽기 떡볶이";
minOrderPrice = 14000;
deliveryFee = 1000;
}
@Nested
@DisplayName("최소주문 가격")
class CheckMinOrderPrice{
@Test
@DisplayName("1000원 미만 에러")
void fail1(){
//given
minOrderPrice = 500;
RestaurantDto restaurantDto = new RestaurantDto(name, minOrderPrice, deliveryFee);
//when
RestaurantRegisterException exception = assertThrows(RestaurantRegisterException.class, () -> {
new Restaurant(restaurantDto);
});
//then
assertEquals(exception.getExceptionClass(), Constants.ExceptionClass.MIN_ORDER_PRICE);
}
}
}
'TIL(Today I Learn)' 카테고리의 다른 글
TIL 220609 (0) | 2022.06.09 |
---|---|
TIL 220606 (0) | 2022.06.06 |
TIL 220603 (0) | 2022.06.03 |
TIL 220602 (0) | 2022.06.02 |
TIL 220531 (0) | 2022.06.01 |