자바 예외 구분: Checked Exception, Unchecked Exception

자바에서 예외는 어떻게 구분할까? Checked Exception과 Unchecked Exception의 차이는 무엇일까?

#java #exception


예외(Exception)란?

Checked ExceptionUnchecked Exception의 차이를 알아보기 전에 먼저 예외와 에러가 무엇인지 알아볼 필요가 있다. 프로그래밍에서 예외(Exception)란 입력 값에 대한 처리가 불가능하거나, 프로그램 실행 중에 참조된 값이 잘못된 경우 등 정상적인 프로그램의 흐름을 어긋나는 것을 말한다. 그리고 자바에서 예외는 개발자가 직접 처리할 수 있기 때문에 예외 상황을 미리 예측하여 핸들링할 수 있다.

한편, 에러(Error)는 시스템에 무엇인가 비정상적인 상황이 발생한 경우에 사용된다. 주로 자바 가상머신에서 발생시키는 것이며 예외와 반대로 이를 애플리케이션 코드에서 잡으려고 하면 안 된다. (사실 잡아도 방법이 없다.) 에러의 예로는 OutOfMemoryError, ThreadDeath, StackOverflowError 등이 있다.

/**
 * StackOverflowError Example.
 * @author kimtaeng
 */
public class SomeTest {

    public static void test() {
        test();
    }

    public static void main(String[] args) {
        try {
            test();
        } catch (StackOverflowError e) {
            // ...!?
        }
    }
}


예외 구분

이번 글의 주제처럼 Exception은 Checked Exception과 Unchecked Exception으로 구분할 수 있는데, 간단하게 RuntimeException을 상속하지 않는 클래스는 Checked Exception, 반대로 상속한 클래스는 Unchecked Exception으로 분류할 수 있다.

exceptions


여기서 RuntimeExceptionException 클래스의 서브 클래스이기 때문에 Exception의 일종이기도 하지만 자바에서는 RuntimeException과 이를 상속한 클래스를 조금 특별하게 취급한다. 명시적으로 예외 처리를 하지 않아도 되기 때문이다.

(2022.02.24 내용 추가) 기존에 이 위치에 트랜잭션이 포함된 관련 내용과 맺음말에 "트랜잭션 처리 옵션을 설정할 수 있기 때문에 트랜잭션 처리 방식은 예외에 따라 고정적이지 않을 수 있다."라는 내용이 있었습니다. 지금 시점에 다시 읽어보니 본문에는 스프링에서의 기본 동작과 같은 추가적인 설명도 없고, 특히나 현재 글의 카테고리에서는 해당 내용이 적합하지 않은 것 같아서 제외했습니다. 내용이 많이 퍼가진 것 같은... 몇 년 된 글인데요. 혼란을 드린 점 사과드리고(제 불찰이니 싸우지 마세요😭) 이곳저곳에서 제보해 주셔서 감사합니다. (_ _)


예외 처리

예외를 처리하는 방법에는 예외 복구, 예외 처리 회피, 예외 전환 방법이 있다.

예외 복구

  • 예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법
  • 예외를 잡아서 일정 시간, 조건만큼 대기하고 다시 재시도를 반복한다.
  • 최대 재시도 횟수를 넘기게 되는 경우 예외를 발생시킨다.
final int MAX_RETRY = 100;
public Object someMethod() {
    int maxRetry = MAX_RETRY;
    while(maxRetry > 0) {
        try {
            ...
        } catch(SomeException e) {
            // 로그 출력. 정해진 시간만큼 대기한다.
        } finally {
            // 리소스 반납 및 정리 작업
        }
    }
    // 최대 재시도 횟수를 넘기면 직접 예외를 발생시킨다.
    throw new RetryFailedException();
}

예외처리 회피

  • 예외 처리를 직접 담당하지 않고 호출한 쪽으로 던져 회피하는 방법
  • 그래도 예외 처리의 필요성이 있다면 어느 정도는 처리하고 던지는 것이 좋다.
  • 긴밀하게 역할을 분담하고 있는 관가 아니라면 예외를 그냥 던지는 것은 무책임하다.
// 예시 1
public void add() throws SQLException {
    // ...생략
}

// 예시 2 
public void add() throws SQLException {
    try {
        // ... 생략
    } catch(SQLException e) {
        // 로그를 출력하고 다시 날린다!
        throw e;
    }
}

예외 전환

  • 예외 회피와 비슷하게 메서드 밖으로 예외를 던지지만, 그냥 던지지 않고 적절한 예외로 전환해서 넘기는 방법
  • 조금 더 명확한 의미로 전달되기 위해 적합한 의미를 가진 예외로 변경한다.
  • 예외 처리를 단순하게 만들기 위해 포장(wrap) 할 수도 있다.
// 조금 더 명확한 예외로 던진다.
public void add(User user) throws DuplicateUserIdException, SQLException {
    try {
        // ...생략
    } catch(SQLException e) {
        if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
            throw DuplicateUserIdException();
        }
        else throw e;
    }
}

// 예외를 단순하게 포장한다.
public void someMethod() {
    try {
        // ...생략
    }
    catch(NamingException ne) {
        throw new EJBException(ne);
        }
    catch(SQLException se) {
        throw new EJBException(se);
        }
    catch(RemoteException re) {
        throw new EJBException(re);
        }
}


정리하면

자바에서 예외는 RuntimeException을 상속하지 않고 꼭 처리해야 하는 Checked Exception과 반대로 명시적으로 처리하지 않아도 되는 Unchecked Exception로 기본적으로 구분할 수 있다.





댓글을 남기시려면 Github 로그인을 해주세요 :D