코린이의 소소한 공부노트

예외의 분류 - 컴파일러 기준 본문

Java

예외의 분류 - 컴파일러 기준

무지맘 2022. 5. 25. 13:49

간단하게 프로그램 오류를 나눠보면 다음과 같다.

1. 컴파일 에러 - 컴파일 단계에서 발생

2. 런타임 에러 - 실행 단계에서 발생

  1) 에러 - 심각한 오류

  2) 예외 - 미약한 오류

3. 논리적 에러 - 작성 의도와 맞지 않는 경우

 

이 중 예외가 컴파일러 기준으로 다시 2가지로 나뉘게 된다.

1. 체크드(checked) 예외

  - 컴파일러가 컴파일 단계에서 예외 처리 여부를 체크하는 예외로

  - 예외 처리(try-catch)문이 필수적이다.

  - Exception 클래스와 그 자손 클래스에 해당한다.

아래 코드는 throw를 이용해 Exception을 발생시키고 예외 처리 문을 작성하지 않은 것이다.

class Test {
    public static void main(String[] args) {
        throw new Exception(); // 예외를 고의적으로 발생시킴
    }
}

// 결과
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Unhandled exception type Exception // 예외 처리할 곳이 없다는 뜻
    at Test.main(Test.java:3)

Exception은 체크드 예외인데, try-catch문이 없어서 에러가 발생한 것이다.

2. 언체크드(unchecked) 예외

  - 컴파일러가 컴파일 단계에서 예외 처리 여부를 체크하지 않는 예외로

  - 예외 처리(try-catch)문이 선택적이다.

  - RuntimeException 클래스와 그 자손 클래스에 해당한다.

아래 코드는 throw를 이용해 RuntimeException을 발생시키고 예외 처리 문을 작성하지 않은 것이다.

class Test {
    public static void main(String[] args) {
        throw new RuntimeException();	// RuntimeException을 고의로 발생시킴
    }
}

// 결과
Exception in thread "main" java.lang.RuntimeException
    at Test.main(Test.java:3)

RuntimeException은 언체크드 예외라서 try-catch문이 없어도 컴파일 단계는 통과한다. 하지만 프로그램을 실행시키면 RuntimeException을 처리할 방법이 없기 때문에 런타임 에러가 발생하고 프로그램이 비정상 종료된다.

 

[체크드와 언체크드로 나누는 이유]

가장 큰 이유는 코드가 복잡해지는 것을 막기 위해서다. 예외 계층도를 보며 자세히 설명하겠다.

예외 클래스의 계층도. 빨간 선을 기준으로 위쪽은 checked 예외, 아래쪽은 unchecked 예외

1. 체크드 예외 - Exception과 그 자손

  - Exception은 프로그램 외적인 요인에 의해 발생하는 예외를 말한다.

  - 프로그램 외적 요소에 의해 예외가 발생한다면 컴파일러가 미리 체크를 해서 프로그램이 정상 작동하게 해야 한다.

  - 그래서 예외를 처리하는 try-catch문이 있는지 필수적으로 확인한다.

2. 언체크드 예외 - RuntimeException과 그 자손

  - RuntimeException은 프로그래머의 실수로 발생하는 예외를 말한다.

  - 사용자가 실수를 안 할 수도 있고, 실수를 한다고 해도 예외가 언제, 어떻게 일어날지 컴파일러가 예측할 수 없다.

  - 그래서 예외를 처리하는 try-catch문이 있는지 선택적으로 확인한다.

만약 모든 예외가 체크드 예외로 만들면 모든 코드에 try-catch문을 작성해야 한다. 예를 들어보자.

// 모든 예외가 체크드 예외라면..
try{
    int[] arr = new int[10];
    // ..
    System.out.println(arr[0]);
} catch(ArrayIndexOutOfBoundsException ae){
    // ..
} catch(NullPointerException ne){
    // ..
}

우리가 보기에는 사용자가 실수한 부분은 없어 보인다. 그런데 컴파일러 입장에서는 걱정이 이만저만이 아니다.

  - 인덱스 범위를 벗어난 값을 사용하려고 하면 어떡하지?

  - 정수 배열이 비어있는데 값을 사용하려고 하면 어떡하지?

그래서 발생 가능한 모든 예외에 대해 try-catch문을 작성해야 한다. 코드가 너무 복잡해지고, 만약 예외가 발생한다고 해도 사용자가 고칠 수 있는 부분이 많기 때문에 RuntimeException와 그 자손들은 예외 처리를 선택적으로 하는 언체크드 예외로 분류해놓은 것이다.