TIL

[TIL] 자바 catch문에는 왜 Exception 타입만 들어갈 수 있을까?

기억지기 개발자 2026. 6. 8. 14:26

자바에서 예외 처리를 처음 배우면 다음과 같은 코드를 자주 접하게 된다.

try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}

그런데 문득 이런 의문이 들었다.

catch 괄호 안에는 항상 Exception 클래스만 들어갈 수 있는 걸까? int나 String은 왜 사용할 수 없을까?

catch의 역할

try 블록 내부에서 예외가 발생하면 JVM은 해당 예외에 대한 객체를 생성한다.

예를 들어 아래 코드를 실행하면

int result = 10 / 0;

실제로는 ArithmeticException 객체가 생성된다.

개념적으로는 다음과 비슷하다.

ArithmeticException ex =
        new ArithmeticException("/ by zero");

그리고 catch 문은 생성된 예외 객체를 전달받는다.

catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}

여기서 e는 단순한 변수가 아니라 예외 객체를 참조하는 변수이다.

따라서 다음과 같은 메서드를 사용할 수 있다.

e.getMessage();
e.printStackTrace();

catch 안에 들어갈 수 있는 타입

catch 괄호 안에는 반드시 Throwable 클래스 또는 그 자식 클래스만 올 수 있다.

예를 들어 다음 코드는 모두 가능하다.

catch (ArithmeticException e) { }

catch (RuntimeException e) { }

catch (Exception e) { }

catch (Throwable e) { }

반면 아래 코드는 컴파일 오류가 발생한다.

catch (int a) { }

catch (String s) { }

catch (Object o) { }

왜냐하면 int, String, Object는 예외 타입이 아니기 때문이다.

Exception 계층 구조

자바의 예외는 상속 구조로 관리된다.

Throwable
 ├─ Error
 └─ Exception
      ├─ RuntimeException
      │    ├─ ArithmeticException
      │    ├─ NullPointerException
      │    └─ IllegalArgumentException
      └─ IOException

따라서 ArithmeticExceptionRuntimeException의 자식이고, RuntimeExceptionException의 자식이다.

그래서 아래 코드는 ArithmeticException도 함께 처리할 수 있다.

catch (Exception e) {
    System.out.println(e.getMessage());
}

여러 예외를 한 번에 처리하기

자바 7부터는 여러 예외를 하나의 catch 문에서 처리할 수 있다.

catch (ArithmeticException | IllegalArgumentException e) {
    System.out.println(e.getMessage());
}

이는 다음 코드와 거의 동일한 의미를 가진다.

catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}

catch (IllegalArgumentException e) {
    System.out.println(e.getMessage());
}

중복 코드를 줄일 수 있다는 장점이 있다.

실무에서는 어떻게 사용할까?

실무에서는 모든 예외를 한 번에 처리하는 방식보다 구체적인 예외를 처리하는 방식을 선호한다.

catch (IOException e) { }

catch (SQLException e) { }

catch (UserNotFoundException e) { }

예외의 종류를 명확하게 구분할 수 있고, 상황에 맞는 처리가 가능하기 때문이다.

특히 Spring에서는 서비스 계층에서 예외를 발생시키고,

throw new UserNotFoundException();

전역 예외 처리기(GlobalExceptionHandler)가 이를 받아 처리하는 구조를 많이 사용한다.

마무리

catch 괄호 안에는 아무 타입이나 넣을 수 있는 것이 아니라 Throwable 또는 그 자식 클래스만 사용할 수 있다.

이는 catch 문이 단순히 값을 전달받는 것이 아니라, JVM이 생성한 예외 객체를 전달받아 처리하는 역할을 수행하기 때문이다.

즉,

catch (ArithmeticException e)

의 e는 단순한 변수가 아니라 실제 예외 객체이며, 자바의 예외 처리 메커니즘은 이러한 객체를 기반으로 동작한다.