본문 바로가기

개발/Language

Java - checked/unchecked exception

회사 클린 코드 스터디에서 예외에 대해 공부했다.

 

 

checked와 unchecked exception에 대한 언급이 자주 나왔는데,

둘이 어떤 차이가 있는지는 제대로 알지 못했다.

 

 

이번 기회에 제대로 짚고 넘어가야겠다.

 

 

참고한 Oracle Java tutorial - Exceptions Index 페이지 링크다.

docs.oracle.com/javase/tutorial/essential/exceptions/index.html

 

Lesson: Exceptions (The Java™ Tutorials > Essential Classes)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

 


Exception의 종류: Checked & Unchecked Exception

 

docs.oracle.com/javase/tutorial/essential/exceptions/catchOrDeclare.html

 

The Catch or Specify Requirement (The Java™ Tutorials > Essential Classes > Exceptions)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

위의 페이지에 따르면 Java에는 세 가지 Exception이 있다고 한다.

 

 

Checked Exception, Error, Runtime Exception이 존재한다.

 

 

 

Checked Exception 

 

 

Checked Exception은 예외 발생을 예상할 수 있는, recover가 가능한 예외다.

따라서 개발자가 예외를 직접 체크(catch)해서 해결할 수 있도록 의도하여 만들어졌다.

 

 

자바에서 예외에 해당하는 Base class로는 Error와 Exception이 존재한다.

 

 

Error는 모두 Unchecked Exception인 반면,

Exception에서는 Checked와 Unchecked인 것이 나누어진다.

 

 

Exception 클래스는 다음과 같이 정의되어 있다.

 

public class Exception extends Throwable {}

 

Exception의 유명한 하위 클래스로는 RuntimeException이 있다.

RuntimeException은 Unchecked Exception이다.

 

 

그래서 얘랑 관련없는 모든 Exception 하위 클래스들을 Checked Exception이라 생각하면 된다.

좀 복잡하다;;

 

 

그럼 Checked Exception의 특징을 알아보자.

 

    private void throwInterruptedException() {
        throw new InterruptedException();
    }

 

InterruptedException은 Exception을 직접적으로 상속하고 있는 Checked Exception이다.

이 코드를 컴파일하면 다음과 같은 에러가 난다.

 

 

 

왜냐면 컴파일러가 두 눈 크게 뜨고 Checked Exception이 제대로 체크되고 있는지 확인하기 때문이다.^^

 

 

체크의 기준은

1) catch: try catch 문으로 예외를 잡아 처리한다.

2) specify: method에 throws 키워드로 caller에게 예외 처리를 위임(강제)한다.

요것 중 하나를 했느냐다.

 

 

이러한 작업들을 안하면 컴파일이 안되기 때문에,

개발자는 예외가 발생할 수 있다는 것을 인지할 수 밖에 없고,

직접 처리를 하든 애먼 곳에 떠넘기든(?) 해야하는 것이다.

 

 

따라서 Checked Exception은 컴파일 타임에 예외 처리를 강제하는 예외다.

 

 

 

Runtime Exception (Unchecked Exception)

 

 

Runtime Exception은 앱 내부적으로 발생하고, 예상치 못한, recover 할 수 없는 예외이다.

 

 

주로 예상치 못한(원치 않는) 프로그래밍 오류로 발생하기 때문에 사전에 체크해서 해결하기 어렵다.

만약 catch를 한다고 하더라도 오류로 인해 프로그램을 더 이상 실행하기 어려운 상황에 처하기도 해서 체크를 하지 않는 편이 효율적일 수 있다.

따라서 Runtime Exception은 Unchecked Exception이다.

 

 

예를 들면, API를 만들 때 꼭 필요한 입력값에 대해서는 null인 매개변수를 허용하지 않는 경우가 많다.

제대로된 입력값을 얻지 못했으니 골칫덩어리 null이 들어오면 작업을 곧바로 종료하는 편이 나을 것이다.

그래서 NullPointerException을 던지는 Objects.requireNonNull을 사용해서 사전에 차단하기도 한다.

 

 

하지만 API를 가져다 쓰는 입장에서는 이런 Unchecked Exception이 많으면 불안하다.

그래서 명세에 어떤 Exception이 발생할 수 있는지를 명시해주는 것이 좋다.

 

 

NullPointerException은 다음과 같이 정의되어있다.

 

public class NullPointerException extends RuntimeException {}

 

    private void throwNullPointerException() {
        throw new NullPointerException();
    }

 

위의 코드는 성공적으로 컴파일된다.

Checked Exception과 달리 catch나 throws 키워드 사용이 필요 없다.

 

 

하지만 throws 키워드를 사용하지 못하는 건 아니다.

그저 사용할 이유가 없는 것이다.

 

 

catch도 하고 싶으면 하면 된다.

특히 API를 가져다 쓸때 명세가 있고 일어나지 않을거라 확신할 수 없는 예외는,

프로그램 안정성을 위해서 catch할 수도 있을 것이다.

 

 

Unchecked Exception은 그저 예외 처리를 강제하지 않는 예외이다.

 

 

 

Error (Unchecked Exception)

 

 

Error는 앱 외부적으로 발생하고, 예상치 못한, recover 할 수 없는 예외이다.

 

 

앱 내부적인 문제 때문이 아니라 System이나 Hardware와 같은 로우 레벨 단에서 문제가 생긴 경우 발생한다.

그렇기 때문에 앱 개발자는 이를 사전에 체크할 수도, 체크한다고 해도 처리할 수도 없다.

 

 

따라서 Error는 Unchecked Exception이다.

 

 

참고로 Error 클래스는 다음과 같이 정의되어 있다.

 

public class Error extends Throwable {}

 

Error는 Exception의 하위 클래스가 아니라 Exception과 같이 Throwable을 상속하고 있는 별개의 클래스다.

 

 


마무리

 

Clean code에 따르면 Unchecked Exception을 사용하라는 내용이 있다.

 

 

언어나 플랫폼에 의해 만들어진 API나 Exception을 사용할 때에 대한 것은 아니고

개발을 하면서 직접 Exception을 던질 때에 해당되는 이야기다.

 

 

Checked Exception을 던지는 메서드를 직접 만들게 되면 throws가 붙게 된다.

caller에서 예외를 바로 catch하지 않는다면 상위 메서드까지 throws 키워드가 따라 붙어야 한다.

예외를 다른 예외로 바꾸거나 예외를 없앤다해도 상위 caller들의 구현도 바뀌게 된다.

 

 

뭐 하나 바꾼다고 이것저것 바꾸는게 영 비효율적이어 보인다.

 

 

하지만 Checked Exception이 유용한 경우도 있다.

 

 

정상적인 프로그램 동작이지만 어떠한 문제가 있음을 효과적으로 알려주고 싶을 때이다!

사용하는 입장에서는 예외를 잡아서 재시도를 할 수도, 다른 작업을 할 수도 있을 것이다.

 

 

어렴풋하게 이해한 것 같지만,

그래도 좀 더 알게 된 것 같다.

'개발 > Language' 카테고리의 다른 글

Kotlin - internal 접근제어자  (0) 2021.10.03
Kotlin - 인터페이스 default 메서드  (0) 2020.12.19