Exception

3 minute read

Exception

Q1. Error와 Exception의 차이점?

보기

모든 예외클래스는 Throwable 클래스를 상속받고 있으며, Throwable은 최상위 클래스 Object의 자식 클래스이다.

Trowable을 상속받는 클래스는 ErrorException이 있다.

오류(Error)는 시스템에 비정상적인 상황이 생겼을 때 발생한다. 이는 시스템 레벨에서 발생하기 때문에 심각한 수준의 오류이다. 따라서 개발자가 미리 예측하여 처리할 수 없기 때문에, 애플리케이션에서 오류에 대한 처리를 신경 쓰지 않아도 된다.

오류가 시스템 레벨에서 발생한다면, 예외(Exception)는 개발자가 구현한 로직에서 발생한다. 즉, 예외는 발생할 상황을 미리 예측하여 처리할 수 있다. 즉, 예외는 개발자가 처리할 수 있기 때문에 예외를 구분하고 그에 따른 처리 방법을 명확히 알고 적용하는 것이 중요하다.

Q2. Checked Exception과 Unchecked Exception의 차이점?

보기

RuntimeException은 Checked Exception과 Unchecked Exception을 구분하는 기준이다. Exception의 자식 클래스 중 RuntimeException을 제외한 모든 클래스는 Checked Exception이며, RuntimeException과 그의 자식 클래스들을 Unchecked Exception이라 부른다.

Checked Exception과 Unchecked Exception의 가장 명확한 구분 기준은 꼭 처리를 해야 하느냐이다.

Checked Exception이 발생할 수 있는 메소드를 사용할 경우, 복구가 가능한 예외들이기 때문에 반드시 예외를 처리하는 코드를 함께 작성해야 한다. catch문으로 예외를 잡든, throws로 예외를 자신을 호출한 클래스로 던지는 방법으로 해결해야 하는데, 이를 해결하지 않으면 컴파일 에러가 발생한다. 대표적으로는 IOException이나 SQLException 등이 존재한다.

반면에 RuntimeException을 상속한 예외들은 따로 Unchecked Exception이라고 부르는데, 명시적으로 예외처리를 강제하지 않기 때문이다. Unchecked Exception은 따로 catch문으로 잡거나, throws로 선언하지 않아도 된다. Unchecked Exception은 피할 수 있지만 개발자가 부주의해서 발생하는 경우가 대부분이고, 미리 예측하지 못했던 상황에서 발생하는 예외가 아니기 때문에 굳이 로직으로 처리를 할 필요가 없도록 만들어져 있다. 대표적으로는 NullPointerException이나 IllegalArgumentException 등이 존재한다.

또한 예외를 확인할 수 있는 시점에서도 구분할 수 있다. 일반적으로 컴파일 단계에서 명확하게 Exception 체크가 가능한 것을 Checked Exception이라 하며, 실행과정 중 어떠한 특정 논리에 의해 발견되는 Exception을 Unchecked Exception이라 한다. 따라서 컴파일 단계에서 확인할 수 없는 예외라 하여 Unchecked Exception이며, 실행과정 중 발견된다 하여서 Runtime Exception이라 하는 것이다.

그리고 한 가지 더 인지하고 있으면 좋은 것이 있다. 바로 예외발생시 트랜잭션의 롤백 여부이다. 기본적으로 Checked Exception은 예외가 발생하면 트랜잭션을 롤백하지 않고 예외를 던져준다. 하지만 Unchecked Exception은 예외 발생 시 트랜잭션을 롤백한다는 점에서 차이가 있다. 트랜잭션의 전파방식 즉, 어떻게 묶어놓느냐에 따라서 Checked Exception이냐 Unchecked Exception이냐의 영향도가 크다. 롤백이 되는 범위가 달라지기 때문에 개발자가 이를 인지하지 못하면, 실행결과가 맞지 않거나 예상치 못한 예외가 발생할 수 있다. 그러므로 이를 인지하고 트랜잭션을 적용시킬 때 전파방식(propagation behavior)과 롤백규칙 등을 적절히 사용하면 더욱 효율적인 애플리케이션을 구현할 수 있을 것이다.

Q3. 예외를 처리하는 방법

보기

예외 복구

예외 복구는 예외 상황을 파악하고, 문제를 해결해서 정상적인 상태로 돌려놓는 것을 말한다.

예를 들어 사용자가 요청한 파일을 읽으려고 했지만 파일이 없는 경우, IOException이 발생한다. 이때는 사용자에게 상황을 알리고 다른 파일을 이용하도록 안내하는 방법으로 해결할 수 있다.

이런 식으로, 예외처리 코드를 강제하는 체크 예외들은 어떤 식으로든 복구할 가능성이 있는 경우에 사용된다.

예외처리 회피

예외처리 회피는 말 그대로, 자신이 예외를 처리하지 않고 호출한 쪽으로 throws를 해버리는 것이다.

public void add() throws SQLException {  
    ... // 구현 로직
}

이런 방법은 자신을 호출한 쪽에서 예외를 처리하는게 맞다는 확신이 있을 경우에 사용하는 방법이다. 처리를 하는 부분이 없이 무책임하게 계속해서 throws를 하다보면 결국에는 서버에까지 예외가 던져지게되고, 문제가 발생할 수 있다.

예외 전환

위에서 설명한 예외처리 회피와 비슷하게, 예외를 복구해서 정상적인 상태로 만들 수 없기 때문에 예외를 발생한 메소드 밖으로 던지는 방법이지만, 적절한 예외로 전환해서 던지는 방법이다.

예외 전환은 아래의 두 가지 목적으로 사용된다.

1. 발생한 예외를 의미있는 예외로 바꾸어주기 위해서

catch (SQLException e) {  
...
throw DuplicateUserIdException();
}

예외의 이름을 보고 어떤 문제가 있는 예외인지 알 수 있게 해준다. 예를 들어, 사용자의 아이디가 겹치는 예외의 경우 일반적인 SQLException이 아니라, DuplicatedUserIDException으로 던지게 되면, 호출한 쪽에서는 단순히 SQLException이 아닌, 사용자의 아이디가 겹쳐서 발생하게된 예외인지를 인식할 수 있게 된다.

2. 예외를 처리하기 쉽고 단순하게 하기 위해서 포장하는 것

복구 가능한 예외가 아닌경우, RuntimeException으로 포장해서 던지는 방법이다. 이렇게 체크 예외를 언체크 예외인 RuntimeException으로 포장해서 던지는 경우, 불필요한 throws 구문이 줄게 된다.

1번과 2번의 경우, 생성한 새로운 런타임 예외에 기존의 예외정보를 담는 중첩 예외 방식으로 새로운 런타임 예외를 던지는 방법을 많이 사용한다. 기존의 예외를 담아서 던지게되면, 처리하는 곳에서는 getCause() 함수를 사용해서 기존에 발생한 예외를 확인할 수 있게 된다.

참고

Reference
  • http://www.nextree.co.kr/p3239/
  • https://wikidocs.net/229
  • 토비의 스프링

Tags:

Categories:

Updated: