Exception

raccoonback·2020년 7월 23일
3

Java

목록 보기
1/1

해당 글에서는 Java에서 지원하는 예외처리 방식에 대해 살펴볼 것이다.

Java에서는 예상치 못한 상황을 처리하고자 에러 처리에 대한 공통 조상인Throwable 클래스를 제공한다.
따라서, Throwable 타입을 상속받은 구체화된 객체들은 특정한 예외적인 상황을 의미하고 특정 상황에 맞는 기능들을 제공한다.
아래 그림을 보다시피, 예상치 못한 상황에 대해서 크게 Error, Exception 타입으로 구분하고 있다.
그럼, ErrorException 타입은 무엇을 의미하고 차이점이 무엇인지 살펴보자.

Error

Error는 JVM 같이 시스템 레벨에서 비정상적인 상황이 생겼을 때 발생한다.
즉, 애플리케이션 레벨에서는 처리할 수 없는 예상치 못한 상황인 것이다.
모든 Java 애플리케이션은 JVM 위에서 구동되는데, JVM 단에서 발생하는 에러를 개발 단계에서 예상할 수 없을 뿐만 아니라 애플리케이션에서 제어할 방법이 없다.
따라서, 시스템 레벨의 문제를 애플리케이션 레벨에서 처리할 수 없기 때문에 Error 발생시 프로그램이 종료되는 것은 자연스러운 흐름이다.
결과적으로 Error는 JVM 같은 시스템 레벨에서 발생하는 에러를 표현하기 위한 타입이고, 시스템에 변화를 주어 문제를 처리해야 하는 경우가 일반적이다.

시스템 레벨의 에러는 Error 타입으로 나타내는 것을 알았다.
그럼 애플리케이션에서 발생하는 에러는 어떻게 표현할까?

맞다. Exception 이다.

Exception

Exception은 애플리케이션에서 핸들링 가능한 예외를 의미하는데, 내부적으로 Checked Exception, Unchecked Exception으로 구분한다.

아래 그림을 보면 알 수 있듯이, Unchecked ExceptionRuntimeException으로 나타내고 RuntimeException 아닌 예외는 Checked Exception이다.

그럼 Checked Exception, Unchecked Exception 차이점은 무엇일까?

Checked Exception, Unchecked Exception 차이점은 위와 같이 크게 네 가지로 구분하는데, 가장 기본적인 구분 기준은 예외 처리의 강제성 여부이다.

Checked Exception

Checked Exception은 API 사용자측에게 반드시 예외 처리를 하도록 강제한다.
즉, Checked Exception는 애플리케이션 종료시키지 않고 복구 가능한 예외라는 것을 의미한다.
예를 들어, 만약 개발 과정에서 라이브러리 사용하는데 특정 함수에서 Checked Exception을 던진다고 가정해보자.
라이브러리는 해당 함수 실행 과정에서 발생 가능한 예외를 반드시 호출자(사용자)측에서 처리하도록 강제하는데, 이를 통해서 호출자는 특정한 상황에 대한 예외를 복구할 수 있는 기회를 얻게 되는 것이다.
뿐만 아니라, 함수에서 Checked Exception을 던졌다는 것은 해당 예외에 대한 명확한 복구 방법을 라이브러리에서 제공하고 있다는 것을 반증하기도 한다.

public class Test {
    public void testCheckedException() {
        try {
            File file  = new File("/wrong/path", "test.txt");
            file.createNewFile();
        } catch (IOException e) {
            // handle Checked Exceptions
        } finally {
            // return resources
        }
    }
}

그래서 만약 자신이 함수 구현하고 있고 Checked Exception을 던져야 하는 상황이라면, 반드시 해당 예외에 대한 복구 방법도 동반해서 제공하는 것을 권장한다.

실제로 일반적으로 Checked Exception 예외가 발생한 경우, 그에 맞는 복구 전략으로 예외를 복구할 수 있는 경우는 그렇게 많지 않다.
현재 특정 함수로 부터 전달받은 Checked Exception을 복구할 방법이 정말 없다면, 해당 Checked ExceptionUnchecked Exception으로 변경해서 예외를 상위로 전달하도록 하자.

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

이러한 예외 전환을 통해서 다른 계층에서 일일이 예외를 선언해서 처리할 필요가 없도록 할 수 있다.

만약 Checked Exception 예외에 대한 복구 전략이 명확하다면, try ~ catch 구문으로 복구를 수행하면 된다.

반대로 복구 전략이 명확하지 않다면, 명확한 메시지를 담은 좀 더 구체적인 Unchecked Exception 타입으로 전환하는 것이 효과적이다.

Unchecked Exception

Unchecked Exception은 API 사용자측에게 반드시 예외 처리를 하도록 강제하지 않는다.
즉 호출자 측에서는 Unchecked Exception 예외를 처리해도 되고 안해도 그만이다.
구체적으로 Unchecked Exception는 호출자 측에서 해당 예외에 대한 복구 방법이 없다면, 프로그램을 종료시키겠다는 의미를 가진다.

예를 들어, 만약 개발 과정에서 라이브러리 사용하는데 특정 함수에서 Unchecked Exception을 던진다고 가정해보자.
라이브러리는 해당 함수 실행 과정에서 발생한 예외를 호출자(사용자)측에서 복구할 방법이 있다면 처리하고, 복구 할 수 없다면 상위 호출자에게 예외를 넘기고 최종적으로도 복구 불가능하다면 최후의 방법으로 프로그램을 종료시키겠다는 것이다.
즉, 최종 호출자까지도 복구할 방법을 찾지 못한다면 크리티컬한 예외로서 프로그램을 종료시키는 것이 났다고 판단하는 것이다.

Unchecked ExceptionRuntimeException 또는 하위 객체들이다.

아래 예제는 Integer.parseInt() 인자에 0~9가 아닌 문자를 포함한 경우이다.

public class Test {
    public void testUncheckedException() {
        Integer value = Integer.parseInt("12A");
        System.out.println(value);
    }
}

0~9가 아닌 문자를 포함하게 되면, Integer.parseInt()Unchecked ExceptionNumberFormatException를 던진다.
만약 testUncheckedException()에서도 복구가 불가능하다면, NumberFormatException 예외는 상위 호출자(testUncheckedException() 메서드 호출자)에게 전달되고 최종 호출자도 처리할 수 없다면 프로그램은 다음과 같이 종료된다.

하지만, testUncheckedException()에서도 복구 가능하다면, NumberFormatException 예외는 testUncheckedException() 메서드에서 처리되기 때문에 상위 호출자(testUncheckedException() 메서드 호출자)에게 전달되지 않는다.

public class Test {
    public void testUncheckedException() {
        Integer value;
        try {
            value = Integer.parseInt("12A");
        } catch(NumberFormatException exception) {
            value = Integer.parseInt("123");
        }

        System.out.println(value);
    }
}

참고 자료

profile
한번도 실수하지 않은 사람은, 한번도 새로운 것을 시도하지 않은 사람이다.

0개의 댓글