Error vs Exception
Error: 메모리 부족 처럼 어플리케이션에서 복구 불가능한 예외 계층
Exception: 개발자가 복구 가능한 예외가 포함된 계층
먼저 예외에 대한 규칙은 예외를 밖으로 던지거나 처리한다 이다. 체크 예외는 예외를 밖으로 필수적으로 던져야 하며, 반대로 언체크(런타임) 예외는 예외를 밖으로 던질 필요가 없다. (언체크(런타임) 예외는 throws 명시를 안해주면 자동으로 밖으로 던져준다) 코드로 하나씩 알아보자
/**
* Exception을 상속받은 예외는 체크 예외가 된다.
*/
static class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
static class Repository {
public void call() throws MyCheckedException {
throw new MyCheckedException("ex");
}
}
MyCheckedException
클래스는 Exception
클래스를 상속받기 때문에 체크 예외이다MyCheckedException
예외 클래스는 던질려면 필수적으로 명시해줘야 한다. ex) method() thorws MyCheckedException
static class Service {
Repository repository = new Repository();
/**
* 예외를 잡아서 처리하는 코드
*/
public void callCatch() {
try {
repository.call();
} catch (MyCheckedException e) {
log.info("예외 처리, message={}", e.getMessage(), e);
}
}
/**
* 체크 예외를 밖으로 던지는 코드
* 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 필수로 선언해야 한다
*/
public void callThrow() throws MyCheckedException {
throw new MyCheckedException("ex");
}
}
/**
* RuntimeException을 상속받은 예외는 언체크 예외가 된다
*/
static class MyUncheckedException extends RuntimeException {
public MyUncheckedException(String message) {
super(message);
}
}
static class Repository {
public void call() {
throw new MyUncheckedException("ex");
}
}
RuntimeException
클래스를 상속받은 MyUncheckedException
클래스는 언체크 예외이다. 따라서 throws 를 필수적으로 명시할 필요가 없다.결론부터 말하자면 RuntimeException
을 기본적으로 사용하고, 정말 중요한 비즈니스 로직만 체크 예외를 사용하면 된다. (Id, Password 불일치, 계좌이체 실패 등..) 이러한 이유에는 총 2가지가 있다.
1) 넘겨받은 예외를 처리하지 못한다.
sqlException
이 발생했다고 해보자. 그래서 throws 로 service, controller 계층이 sqlException
처리할 수 있을까? sqlException
은 공통 예외 계층까지 가야지 해결이 가능할 것이다. 즉, 체크 예외를 사용하면 예외 처리도 불가능한데 계속해서 throws 를 사용해 예외만 던지는 상황이 반복되는 것이다.2) 다른 계층에서 의존관계 문제가 생긴다
sqlException
에서 jpaException
으로 예외가 바뀌었다고 생각해보자. 그러면 Service, Controller 계층은 모든 throws 내용들을 변경해줘야 한다. 따라서 위의 2가지 이유로 기본적으로 RuntimeException
을 사용하되, 중요한 로직만 CheckExcetion
을 사용하도록 하자. 그리고 서블릿 오류 페이지에서 공통적으로 예외를 다루자.