위의 자바 예외 계층 그림을 보면 가장 최상위에는 Object가 있다. 여기서 중요한 점은 예외도 객체이기 때문에 예외의 최상위 부모도 'Object'다.
예외 중에서 가장 최상위 예외는 Throwable이고 그 하위에 'Exception'과 'Error'가 있다. 'Error' 는 메모리 부족이나 심각한 시스템 오류로 애플리케이션의 복구가 불가능할 때 발생하는 예외로 이것은 해결할 필요가 없고 'Exception'부터 필요한 예외로 생각하고 잡으면 된다.
'Exception'과 그 하위의 예외인 SQLException, IOException모두 컴파일러가 체크하는 체크 예외다. 단 RuntimeException은 언체크 예외다.
예외를 처리하는 방식은 잡아서 처리하거나 처리할 수 없으면 밖으로 던지는 방법 두가지다. 예외를 처리하지 못하면 호출한 곳으로 예외를 계속 던지게된다. 만약 'Exception'으로 처리했다가 'catch'로 잡으면 그 하위의 예외도 모두 잡을 수 있으며 'throws'로 던지면 그 하위 예외도 모두 던질 수 있다.
'Exception'과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외다. 체크 예외는 기본적으로 잡아서 처리하거나 밖으로 던지지 않으면 컴파일 오류가 발생한다. 코드로 살펴본다.
public class MyCheckedException extends Exception {
public MyCheckedException(String message){
super(message);
}
}
Exception을 상속받은 MyCheckedException클래스를 만든다. MyCheckedException 클래스는 부모인 Exception 클래스의 메서드를 그대로 상속받는다.
//service
public class Service{
Repository repository = new Repository();
public void catchException() {
try {
repository.callException();
} catch(MyCheckedException e) {
log.info("예외 처리, message={}", e.getMessage(), e);
}
}
}
//repository
public class Repository{
public void callException() throws MyCheckedException {
throw new MyCheckedException("exception 발생")
}
}
테스트를 돌려 로그를 찍어보면 전달한 exception 메시지와 함께 exception 관련 정보들을 확인할 수 있다.
Exception을 상속받은 MyCheckedException은 체크 예외가 된다. 하지만 'RuntimeException'을 상속받으면 언체크 예외가 된다는 점을 유의해야한다. 체크예외의 경우에는 예외를 잡아서 처리할 수 없는 경우에는 밖으로 던지는 'throw'를 필수로 선언해야 한다. 만약 'throw'로 예외를 밖으로 던지지 않으면 컴파일 오류가 발생한다.
이 글은 김영한님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 듣고 정리한 내용입니다.