Spring Exception 종류 - Spring Exception jonglyu

오류 vs 예외

Spring Exception 종류 - Spring Exception jonglyu

ExceptionError 모두 Throwable 클래스를 상속받고 있다.

오류 (Error)

  • java.lang.Error 클래스의 서브 클래스들이다.
  • 시스템에 비정상적인 상황이 발생했을 경우에 사용된다.
  • 주로 JVM에서 발생시키기 때문에 애플리케이션 코드에서 잡아서는 안되며, 잡아서 대응할 수 있는 방법도 없다.
  • 따라서 시스템 레벨에서 특별한 작업을 하는게 아니라면 이런 에러 처리는 하지 않아도 된다.

예외 (Exception)

  • java.lang.Exception 클래스의 서브 클래스들이다.
  • Error와 달리 애플리케이션 코드에서 예외가 발생하였을 경우에 사용된다.
  • 예외는 개발자가 처리할 수 있기 때문에 예외를 구분하고 그에 따른 처리 방법을 명확하게 알고 적용하는 것이 중요하다!

예외 종류

Checked Exception

RuntimeException 클래스를 상속하지 않은 Exception 클래스들

  • 실행하기 전에 예측 가능한 예외
  • 체크 예외는 예외가 발생할 수 있는 메서드를 사용할 경우 반드시 예외를 처리하는 코드를 함께 작성해야 한다.
  • 예외를 처리하기 위해서는 try - catch으로 잡거나 throws를 통해 메서드 밖으로 던질 수 있다.
  • 만약 예외를 처리하지 않으면 컴파일 에러가 발생한다.

Unchecked Exception

RuntimeException을 상속한 Exception 클래스들

  • 실행하고 난 후에 알 수 있는 예외
  • Error와 마찬가지로 에러를 처리하지 않아도 컴파일 에러가 발생하지 않는다.

💡 RuntimeException이란?

  • java.lang.RuntimeException 클래스를 상속한 예외들
  • 명시적인 예외 처리를 강제하지 않는다.
  • 런타임 예외는 주로 프로그램의 오류가 있을 때 발생하도록 의도된 것이다.
  • 대표적으로는 NullPointerException, IllegalArgumentException 등이 있다.

📌 Checked Exception vs Unchecked Exception 정리

Spring Exception 종류 - Spring Exception jonglyu

가장 명확한 구분 기준은 꼭 처리를 해야 하는가!이다.


예외 처리

예외 처리 방법

  • 예외 복구: 예외가 발생하면 예외 상황에 대해 알맞게 처리하여 복구한다.
    • Ex) try - catch
  • 예외 회피: 예외를 직접 처리하지 않고 예외를 상위 메소드에 위임한다.
    • Ex) throw
  • 예외 전환: 예외를 위임하되 발생한 예외를 그대로 위임하는 것이 아닌 적절한 예외로 전환하여 위임한다.
    • Ex) RestTemplate.doExecute

예외 복구 범위

  • 메소드 영역: 메소드 영역은 종속된 복구 기능으로 단순히 try, catch를 사용하면 된다.
  • 클래스 영역: 클래스 내 공통 예외 복구는 @ExceptionHandler를 사용할 수 있다.
  • 전역 영역: 여러 클래스의 공통 예외 복구는 @ControllerAdvice를 사용할 수 있다.

정리

오늘은 예외에 대해서 알아보았다.
다음에는 예외 복구에 대해서 더 자세히 알아보고@ExceptionHandler, @ControllerAdvice 사용법에 더 자세히 알아볼 것이다🤩


참고자료

https://mangkyu.tistory.com/152
https://velog.io/@kdhyo/Java%EC%98%88%EC%99%B8Exception%EA%B4%80%EB%A0%A8-%EC%A0%95%EB%A6%AC

2020.06.27

개요

이번 4장에서는 배울 것들은 다음과 같습니다.

  1. 스프링의 데이터 액세스 기능에 담겨 있는 예외 처리와 관련된 접근 방법에 대해서 알아봅니다.
  2. 이를 통해, 예외를 처리하는 베스트 프랙티스를 살펴봅니다.

잘못 사용되는 예외 처리

1. 예외를 잡고 아무것도 하지 않는 경우

아래의 경우는 예외가 어디서 발생했는지 알지 못하기 때문에 예외가 발생하는 것보다 더 위험합니다.

Java

try {
    // ...
}
catch(SQLException){}

2. 예외를 출력만 하는 경우

배포 서버(운영 서버)에 올라가게 되는 경우 누군가 계속 모니터링하지 않는 이상 심각한 상황을 만듭니다.

모든 예외는 적절하게 복구되든지, 작업을 중단시키고 개발자에게 분명하게 통보되어야 합니다.

Java

try {
    // ...
}
catch(SQLException e){
    System.out.println(e);
    e.printStackTrace();
}

예외의 종류와 특징

자바에서 throw를 통해 발생시킬 수 있는 예외는 크게 3가지가 있습니다.

1. Error

첫째는 java.lang.Error 클래스의 서브클래스들입니다.
Error는 시스템에 비정상적인 상황이 발생된 경우로 주로 자바 VM에서 발생됩니다.
그래서, 애플케이션 단에서는 이런 에러에 대한 처리는 신경쓰지 않아도 됩니다.

2. Exception

Error와 달리 애플리케이션 단에서 예외가 발생한 경우에 사용됩니다.

Exception 클래스는 다시 체크 예외, 언체크 예외로 구분됩니다.

1. Checked Exception (체크 예외)

  • 컴파일 시점에서 에러가 발생하는 경우
  • 반드시 예외를 처리해야함

사용할 메서드가 Checked Exception 예외를 던진다면, catch문으로 잡든지 다시 throws를 정의해서 메소드 밖으로 던져야합니다.

2. Unchecked Exception (언체크 예외)

  • 런타임 시점에서 에러가 발생하는 경우
  • 예외 처리를 강제하지 않음
  • RuntimeException라고 불림

프로그램의 오류가 있을 때 발생하도록 의도된 예외로 예상하지 못한 상황에서 발생하는 것이 아니기 때문에 굳이 catchthrows를 사용하지 않아도 됩니다.

예외 관계도

Spring Exception 종류 - Spring Exception jonglyu

예외 처리 방법

1. 예외 복구

첫번째 방법은 예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 방법입니다.

예외가 처리됐으면 애플리케이션에서는 다시 정상적으로 설계된 흐름에 따라 진행되어야 합니다.

2. 예외 처리 회피

두번째 방법은 예외 처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것입니다. (예외 버블링)

예외를 회피할 때는 자신을 사용하는 쪽에서 예외를 다루는 것이 최선의 방법이라는 의도와 확신이 있어야합니다.

3. 예외 전환

세번째 방법은 예외 회피와 달리, 발생한 예외를 그대로 넘기는 게 아니라 적절한 예외로 전환해서 던지는 방법입니다.

예외 전환 방법은 다음과 같이 2가지가 있습니다.

  1. 중첩 예외로 만들기

    보통 전환하는 예외에 원래 발생한 예외를 담아서 만듭니다.

    Java

    catch(SQLException e){
       // ...
       throw DuplicateUserIdException(e);
    }

  2. 예외를 포장(Wrap)하기

    주로 예외 처리를 강제하는 체크 예외언체크 예외로 바꾸는 경우에 사용합니다.

    대표적으로 런타임 예외인 EJBException 가 있습니다.

    Java

    try {
       // ...
    } catch(SQLException e){
       throw new EJBException(e);
    }

복구가 가능한 예외는 try/catch를 사용해 에러를 처리해야하고

복구가 불가능한 예외는 런타임 예외로 포장해서 메소드 밖으로 throw해서 에러를 처리합니다.
런타임 예외 처리의 경우 장점도 있지만, 컴파일 시점에 예외를 강제하지 않기 때문에 개발자가 예외 상황을 고려하지 못할 가능성이 있습니다.

예외 전환

예외를 다른 것으로 바꾸어 던지는 예외 전환의 목적은 설명 했듯이 2가지 입니다.

  1. 런타임 예외로 포장해서 던지면 호출하는 쪽에서 필요하지 않은 try/catch를 줄여주는 것
  2. 로우레벨의 예외를 좀 더 의미 있고 추상화된 예외로 바꿔서 던져주는 것

JDBC의 한계

DB 종류에 상관없이 사용할 수 있는 데이터 액세스 코드를 작성하는 일은 쉽지 않습니다.
표준화된 JDBC API가 DB 개발 방법을 더 편리하게 도와주지만 DB를 자유롭게 변경해서 사용할 수 있는 유연한 코드를 보장해주지는 못합니다.
그 이유로는 2가지 문제가 있습니다.

1. 비표준 SQL

첫번째 문제는 JDBC 코드에서 사용하는 SQL입니다. 대부분의 DB는 표준을 따르지 않는 비표준 문법과 기능도 제공합니다.
이러한 비표준 문법은 해당 DB의 특별한 기능을 사용하거나 최적화된 SQL을 사용할 때 유용합니다.

이런 비표준 SQL을 지원하기 위해선 DAO는 DB별로 종속적인 코드가 되어버려 문제가 생깁니다.

2. 호환성 없는 SQLException의 DB 에러 정보

두번째 문제는 SQLException 입니다.
DB에 따라 발생할 수 있는 예외의 원인과 종류가 제각각이라는 점입니다.

DB에 독립적으로 예외 처리

DB에 독립적으로 예외를 처리하기 위한 해결 방안은 ?

스프링은 DataAccessException이라는 SQLException을 대체하는 런타임 예외를 제공할 뿐아니라 DataAccessException의 서브 클래스로 세분화된 예외를 제공합니다.

예외의 종류 (일부)

  1. SQL 문법 때문에 발생하는 에러 - BadSqlGrammerException
  2. DB 커넥션을 가져오지 못한 경우 - DataAccessResourceFailureException
  3. 데이터의 제약 조건 위배, 일관성을 지키지 않은 작업을 수행 - DataIntegrityViolationException
  4. 중복 키 때문에 발생한 경우 - DuplicatedKeyException

문제는 DB마다 에러 코드가 제각각이라는 점입니다.

스프링은 DB별 에러 코드를 분류해서 스프링이 정의한 예외 클래스와 매핑해놓은 에러 코드 매핑정보 테이블을 만들어두고 이를 이용합니다.

그래서 JdbcTemplate은 DB 종류에 상관없이 동일한 예외를 받을 수 있습니다.

XML

<bean id="Oracle" class="org.springframework.jdbc.support.SQLErrorCodes">
    <property name="badSqlGrammerCodes">
        <value> 900, 903, 904, 917, 936, 942, 17006</value>
    </property>
    <property name="invalidResultSetAccessCodes">
        <value> 17003 </value>
    </property>
    <property name="duplicateKeyCodes">
        <value> 1 </value>
    </property>
    <property name="dataIntegrityViolationCodes">
        <value> 1400, 1722, 2291, 2292 </value>
    </property>
    <property name="dataAccessResourceFailureCodes">
        <value> 17002, 17447 </value>
    </property>
    ...
</bean>

데이터 액세스 기술에 따른 예외 처리

DataAccessException은 DB별 예외 처리뿐 아니라 JDBC 외의 자바 데이터 액세스 기술에서 발생하는 예외에도 적용됩니다.

JDBC 이외에 자바 데이터 액세스 기술에는 JPA(ORM), MyBatis(SQL Mapper) 등이 있습니다.