Home Item70 (복구할 수 있는 상황에서는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라)
Post
Cancel

Item70 (복구할 수 있는 상황에서는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라)

복구할 수 있는 상황에서는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라

Java에서는 문제 상황을 알리는 타입(throwable)으로 검사 예외, 런타임 예외, 에러 이렇게 3가지를 제공한다.

  • 검사 예외 (Checked Exception)
    • 반드시 처리해야 하는 예외로 컴파일 단계에서 확인이 가능하다.
    • RuntimeException 및 RuntimeException의 하위 클래스를 제외한 나머지 예외는 검사 예외이다.
    • ex) IOExcetpion, SQLException
  • 런타임 예외 (Runtime Exception)
    • 실행 중 발생하는 예외로 컴파일러가 강제하지 않고, 주로 프로그래밍 오류로 인해 발생한다.
    • 개발자가 명시적으로 예외 처리를 하지 않아도 되며, 처리하지 않을 경우에는 프로그램이 중단될 수 있다.
    • ex) NullPointerException, ArrayIndexOutOfBoundsException
  • 에러 (Error)
    • 시스템 혹은 하드웨어의 오작동으로 발생한다.
    • Error는 심각한 수준의 오류이기 때문에 애플리케이션에서 복구할 수 없다.
    • 개발자는 일반적으로 이를 처리하지 않는다. 발생 시 프로그램이 종료될 가능성이 크다.
    • ex) OutOfMemoryError, StackOverFlowError

예외 정리 참고

3가지 중 언제 무엇을 활용해야 하는 지 헷갈릴 수 있는데, 100% 명확하진 않지만 참고할 수 있는 지침들이 있다.

검사 예외를 사용하는 경우

호출하는 쪽에서 복구하리라 여겨지는 상황이라면 검사 예외를 사용하자.

이것이 검사와 비검사 예외를 구분하는 기본이다.

검사 예외를 던지게 되면 일반적으로 호출한 쪽에서 해당 예외를 catch하거나 더 바깥으로 예외를 전파하도록 강제한다.

메서드 선언에 포함된 검사 예외 각각은 그 메서드를 호출했을 때 발생할 수 있는 유력한 결과임을 API 사용자에게 알려주는 역할을 하는 것이다.

API 설계자의 입장에서 보면 API 사용자에게 검사 예외를 던져 그 상황을 복구하라고 요구한 것이다.

하지만 일반적으로 검사 예외가 발생했을 때 복구 전략을 활용해 복구할 수 있는 경우는 많지 않다.

예를 들어 SQLException은 자바 애플리케이션에서 복구가 힘들다. 이러한 복구 불가능한 예외를 처리하겠다고 잡아서 계속 밖으로 던지면 결국 끝까지 예외를 던지게 되는 것이다.

따라서 검사 예외는 해당 예외를 잡아 반드시 처리해야 하는 문제일 때만 사용해야 한다.

  • 계좌 이체 실패 예외
  • 결제 시 포인트 부족 예외
  • 로그인 ID, PW 불일치 예외

위 경우들에도 반드시 검사 예외를 사용하라는 것은 아니다.

하지만 위와 같은 심각한 문제는 개발자가 실수로라도 예외를 놓치면 안되기 때문에 검사 예외로 만들어두어 컴파일러를 통해 놓친 예외를 인지할 수 있도록 하는 것이다.

또한 검사 예외는 바깥으로 예외를 던지면서 그 예외에 의존하게 만든다. SQLException을 계속 던지는데, 만약 관련 기술을 바꿔서 그 예외가 사용되지 않는다면 그 부분을 모두 수정해야 한다.

SQLException 같은 부분은 필터/인터셉터 등을 이용해 공통으로 처리하도록 하는 것도 방법이다.

참고(예외 이해하기)

런타임 예외를 사용하는 경우

프로그래밍 오류를 나타낼 때는 런타임 예외를 사용하자.

런타임 예외 대부분은 전제 조건을 만족하지 못했을 때 발생한다.

  • 전제 조건 위배
    • 클라이언트가 해당 API의 명세에 기록된 제약을 지키지 못했다는 뜻
    • ex) ArrayIndexOutOfBoundsException은 범위 전제 조건을 지키지 않아 발생한 것이다.

그런데 이러한 상황은 복구할 수 있는 상황인지 프로그래밍 오류인지 항상 명확히 구분되지 않는다.

예를 들면 자원 고갈은 말도 안되는 크기의 배열을 할당해 생긴 프로그래밍 오류일 수 있고, 혹은 진짜 자원이 부족해 발생한 문제일 수도 있는 것이다.

확신하기 어려울 때는 런타임 예외를 사용하는 것이 바람직할 것이다.

Error를 사용하는 경우

에러는 보통 JVM이 자원 부족, 불변식 깨짐 등의 상황이 있어 더 이상 수행을 지속할 수 없는 상황을 나타낼 때 사용된다.

Java 언어 명세가 요구하는 바는 아니지만 업계에 널리 퍼진 규약으로는 Error 클래스를 상속해 하위 클래스를 만드는 일은 자제해야 한다.

우리가 구현하는 비검사 throwable은 모두 RuntimeException의 하위 클래스여야 한다.

Error는 상속을 하지 말아야할 뿐만 아니라 throw문으로 던져서도 안된다.

throwable..?

Exception, RuntimeException, Error를 상속하지 않는 throwable을 만들 수 있다.

보통 일반적인 검사 예외처럼 다루게 된다.

그러나 이는 이로울게 없다. 절대 사용하지 말자.

throwable은 정상적인 검사 예외보다 나을 것도 없고, API 사용자를 오히려 헷갈리게 한다.

주의! 예외도 객체다.

예외도 어떤 메서드라도 정의할 수 있는 완벽한 객체이다.

예외의 메서드는 주로 그 예외를 일으킨 상황에 관한 정보를 코드 형태로 전달하는 용도로 사용된다.

이러한 메서드가 없다면 프로그래머들은 오류 메시지를 파싱해 정보를 빼내야 한다.

결론은 호출자가 예외 상황에서 벗어나는데 필요한 정보를 알려주는 메서드를 반드시 함께 제공해야 한다.

정리

복구할 수 있는 상황이라면 검사 예외, 그 중에서도 반드시 복구해야 하는 경우 검사 예외를 활용하자.

그 외의 경우에는 검사 예외는 의존성 문제 등의 문제가 있다.

다른 경우에는 RuntimeException을 던지면 된다.

그리고 명심해야할 부분은 예외 내부에 필요한 정보를 제공하는 메서드를 함께 제공해야 한다.

This post is licensed under CC BY 4.0 by the author.

Item69 (예외는 진짜 예외 상황에만 사용하라)

Item71 (필요 없는 검사 예외 사용은 피하라)