체크 예외(Checked Exception)와 언체크 예외(Unchecked Exception)

Timo·2022년 8월 8일
0

예외의 계층 구조

예외도 객체이므로 Ojbect를 상속하는 Throwable 클래스부터 시작한다.
Throwable 하위로는 ExceptionError 클래스가 있다.

Error

Error 클래스는 어플리케이션 레벨에서 다룰 수 없는 시스템 장애를 말한다.
예) OutOfMemoryError

Exception

Exception 클래스를 포함한 하위 클래스들은 어플리케이션 레벨에서 다룰 수 있는 예외들이다.

Exception 클래스의 하위 클래스는 전부 체크 에외(Checked Exception)이지만 RuntimeException과 그 하위 클래스들은 제외된다.

예외의 두 가지 기본 규칙

예외에는 기본적으로 두 가지 규칙이 있다.

  1. 예외는 잡아서 처리하거나 던져야 한다.
  2. 예외를 잡거나 던질 때 지정한 예외 뿐만 아니라 그 예외의 하위 예외들도 함께 처리된다.

체크 예외

컴파일러가 체크하는 예외이다.
RuntimeException과 그 하위 예외를 제외한 나머지 모든 예외들이 해당된다.
체크 예외는 잡아서 처리하거나 외부로 던지도록 선언해야 하며, 이렇게 처리되지 않은 예외는 컴파일 오류가 발생한다.

언체크 예외(런타임 예외)

RuntimeExcpetion과 그 하위 예외들이며, 컴파일러가 예외를 체크하지 않는다는 점에서 언체크 예외라고 한다. throws로 예외를 선언하지 않고 생략할 수 있다. 이 경우 자동으로 예외를 던지며, 명시적으로 선언할 수도 있다.

언체크 예외는 왜 필요한가?

  1. 개발자가 다룰 수 없는 예외까지 모든 예외를 처리하는 것은 무척 번거롭다.
  2. 예외를 던지게 될 경우 불필요한 의존 관계가 생긴다.

불필요한 의존 관계

불필요한 의존 관계에 대한 하나의 예로 JDBC 기술을 사용할 때 발생하는 SQLException을 들 수 있다. SQLException은 체크 예외이다. 체크 예외는 컴파일 오류가 발생하므로 반드시 잡아서 처리하거나 상위 레벨로 던져야 한다.

문제는 이 예외가 SQL 문법 오류 등으로 발생하는 것이기 때문에 시스템 상에서 마땅히 처리할 수 있는 예외가 아니라는 점이다.

결국 이 예외를 처리할 수 있는 곳이 없어 상위 레벨로 계속 던져지다 보면 최상위 레벨인 컨트롤러까지 전달되게 되고, 컨트롤러는 메서드 선언부에 throws SQLException를 선언하게 된다.

이 상황은 문제가 있다. 컨트롤러는 표현 계층임에도 불구하고 레포지토리 레벨의 특정 기술(JDBC를 사용함으로써 발생하는 예외)에 의존하게 되는 셈이다.

따라서 OCP, DI 원칙을 위배하므로, 구체적인 기술이 변경되었을 때 표현 영역까지 영향도가 전파되는 문제를 갖는다.

예외는 가급적 런타임 예외로

위에서 말한 두 가지 문제점을 해결하기 위해 비즈니스 예외들은 런타임 예외로 구현해주는 것이 좋다. 다만 이 경우 예외 처리가 누락될 수 있으니 해당 예외들을 빠뜨리지 않고, 다룰 수 있도록 문서화 등을 통해 공유하는 것이 중요하다.

물론 꼭 다루어져야 하는 치명적인 예외라면 체크 예외로 구현하는 것도 좋은 방법이 될 수 있다.

체크 예외를 언체크(런타임) 예외로 전환 시 유의할 점

이전 예외를 파라미터로 전달해주어야 한다. Throwable cause 라는 파라미터로 예외를 전달해주어야 로그 출력 시 Caused By 문구를 통해 해당 예외가 어떤 예외를 이어 받았는지 추적할 수 있다.

참고 자료

[인프런] 김영한님의 스프링 DB 1편 - 데이터 접근 핵심 원리

profile
나는 매일 성장하는 사람

0개의 댓글