Java와 예외 처리

3734 단어 WILSpringJavaJava

왜 repository 계층에서 SQLException 예외가 발생하면 RuntimeException 예외로 변환하여 다시 던져주는 것일까? 자바와 예외 처리에 관해서 살펴보자!

Exception hires

Throwable : 최상위 예외

  • Error : 메모리 부족이나 심각한 시스템 오류 등 어플리케이션에서 복구 불가능한 시스템 예외

  • Exception : Checked 예외

    • RuntimeException : Unchecked 예외

    예외처리 - Jump to Java

Checked 예외를 선언하면 throws 예외를 필수로 선언해야 하지만 컴파일 타임에 오류를 잡아 주기 때문에 안전하다는 장점이 있다.
하지만 실무에서는 Checked 예외가 발생하면 받아서 unchecked 예외로 변환한 뒤 던져준다.
실무에서는 어떠한 이유들로 Checked 예외보다 Unchecked 예외를 선호하는 것일까?

Checked 예외의 문제점

1. 항상 throws를 명시한다.

대부분의 예외는 복구가 불가능하다. 가령 repository에서 예외가 발생했을 때 Service 계층이나 Controller 계층까지 예외를 올려도 처리할 수 있는 방법이 없다. 하지만 Checked 예외에는 항상 throw를 명시해 줘야 하므로, Service 계층이나 Controller 계층 역시 예외를 잡거나, 처리하는 의무를 가지게 된다.

2. 특정한 의존 관계를 가지게 된다.

예를들어 repository 계층에서 SQLException이 발생했을 때 예외를 throws 했다고 가정하자. throws했기 때문에 Service 계층, Controller 계층 역시 예외를 처리하거나 throws SQLException을 명시해 줘야 할 것이다.

java.sql.SQLException과 같이 특정한 기술에 의존하게 되므로 Spring의 핵심이라 할 수 있는 OCP, DI를 통한 구현체 변경이 어려워 질 수 있다.

체크 예외는 이렇게 사용하자!

  1. 기본적으로 Unchecked(RuntimeException)을 사용하자 → 나중에 공통 예외처리에서 다루면 된다.
  2. 로직 상 의도적으로 던지는 예외(반드시 잡아서 처리해야 할 문제)에 대해서만 Checked 예외를 사용하도록 하자
  3. checked 예외로 선언한 경우 바로 처리하도록 하고, Unchecked(RuntimeException)으로 선언한 경우 예외를 공통으로 처리하는 부분 을 앞에 만들어서 처리하도록 하자.

공통으로 처리하는 부분 : 서블릿 필터, 스프링 인터셉터, 스프링의 ControllerAdvice를 사용하여 구현

스프링과 데이터 접근 예외 추상화

데이터베이스에서 에러가 발생하면, 데이터베이스는 오류 코드를 반환하고, 이 오류 코드를 받은 JDBCDriver는 SQLException을 발생시킨다. 만약 특정한 상황(예 중복 키 예외) 같은 경우 예외에만 예외를 처리하고 싶다면 오류코드를 비교함으로써 자신만의 예외(Unchecked 예외, Runtime Exception)를 던지거나, 처리하면 된다.

하지만 이게 최선일까?

  • 오류 코드(Error Code)가 데이터베이스 마다 다르다

만약 다른 데이터베이스로 변경하고 싶다고 하면 모든 오류코드들을 변경하려는 데이터베이스의 오류 코드에 맞게 변경해야 할 것이다.

  • 데이터베이스마다 오류코드의 종류가 너무 많다.

발생할 수 있는 모든 상황에 맞게 오류코드를 비교하고 예외처리한다는 것은 너무 비효율적이다.

ex) h2 database error code

스프링에서는 데이터 접근과 관련되 예외를 추상화함으로써 개발자의 수고를 덜어줬다.

또한 기존에는 특정한 데이터베이스에 종속되어 데이터베이스를 변경하려면 많은 코드를 변경해야 했지만, 스프링에서 정의한 예외는 특정 기술에 종속적이지 않게 설계되어 있기 때문에(추상화 하였기 때문에!) JDBC 기술이든 JPA 기술이든 스프링인 제공하는 예외를 사용하면 된다.

DataAccessException

DataAccessException는 RuntimeException을 상속받아 unchecked 예외이며, BadSqlGrammarException, DuplicateKeyException, QueryTimeoutException 등 데이터 접근에 관련된 다양한 예외들을 추상화한 예외이다.

DataAccessException docs

SpringExceptionTranslator

Spring에서는 SpringExceptionTranslator을 지원함으로써 해당 예외에 맞는 적절한 스프링 데이터 접근 계층의 예외로 변환해서 반환해준다.(무식하게 오류 코드를 비교해서 RuntimeException을 던져 줄 필요가 없어진 것이다!)

// SpringExceptionTranslator 예시

DataAccessException e = exceptionTranslator.translate(”save”, save_sql, e); 

SpringExceptionTranslator docs

최근 라이브러리들은 대부분 Unchecked ,Runtime Exception을 기본으로 제공한다. 또한 실무에서는 최근 JPA 기술로 데이터 접근을 처리하고 있고 JPA역시 Runtime Exception을 사용한다. 실무에서는 자주 사용할 일이 많이 없겠지만 꼭 알아두자!

Reference

스프링 DB 1편 - 데이터 접근 핵심 원리
망나니 개발자 - Spring의 다양한 예외 처리 방법

좋은 웹페이지 즐겨찾기