Spring

[스프링/예외] 스프링-디비1 6-1. 스프링과 문제 해결 - 예외 처리, 반복

ydin 2024. 2. 2. 15:41

체크 예외와 인터페이스

서비스 계층은 가급적 특정 구현 기술에 의존하지 않고, 순수하게 유지하는 것이 좋다. 이렇게 하려면 예외에 대한 의존도 함께 해결해야한다.

그렇다면 서비스가 처리할 수 없는 SQLException에 대한 의존은 어떻게 제거할 수 있을까?

 

이는 리포지토리에서 SQLException 체크 예외를 런타임 예외로 전환해서 서비스 계층에 던지면 된다.

 

스프링 예외 추상화 이해

런타임 예외 변환으로 체크 예외에 의존하는 것은 해결했지만, 각 예외상황마다 오류 코드가 다른 경우가 있다. 예를 들어 키 중복 오류 코드는 H2의 경우 23505, MySQL의 경우 1062인 것 처럼 말이다. 문제는 이 오류코드가 하나의 데이터베이스에도 수백개인데 또 데이터베이스 별로 다 다르다는 것이다.

만약 각 오류 코드 별로 분기문을 만들어 진행한다면 코드 양이 방대해지고, DB를 바꾸게 되면 전체 코드를 바꿔야 할 수도 있다.

 

이 문제를 스프링은 데이터 접근과 관련된 예외를 추상화해서 해결해준다. 계층 구조는 다음과 같다.

  • 스프링은 데이터 접근 계층에 대한 수십 가지 예외를 정리해서 일관된 예외 계층을 제공한다.
  • 각 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다.
  • 가장 상위에는 DataAccessException 예외가 있다. 이는 런타임 예외를 상속 받았기에 모두 런타임 예외이다.
  • DataAccessException
    • 이는 NonTransient, Transient 예외로 구분된다.
    • Transient는 일시적이라는 뜻으로, Transient 하위 예외는 동일한 SQL을 다시 시도했을 때 성공할 가능성이 있다.
      • 예를 들어 쿼리 타임아웃, 락과 관련된 오류들이 있다. 이런 오류들은 데이터베이스 상태가 좋아지거나, 락이 풀렸을 때 다시 시도하면 성공할 수도 있다.
    • NonTransient는 일시적이지 않다는 뜻이다. 같은 SQL을 그대로 반복해서 실행하면 실패한다.
      • SQL 문법 오류, 데이터베이사 제약조건 위배 등이 있다.

 

스프링이 제공하는 예외 변환기

스프링은 데이터베이스에서 발생하는 오류 코드를 스프링이 정의한 예외로 자동으로 변환해주는 변환기를 제공한다. 구체적인 코드는 다음과 같다.

SQLExceptionTranslator exTranslator = 
    new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultEx = exTranslator.translate("select", sql, e);
  • translate(a, b, c) : a는 읽을 수 있는 설명이고, b는 실행한 sql, c는 발생된 Exception을 전달하면 된다. 이렇게 하면 적절한 스프링 데이터 접근 계층의 예외로 변환해서 반환해준다.
  • 이렇게 하면 일일이 if (e.getErrorCode().equals(40122)) 처럼 코드별로 분기문으로 나눠 오류코드를 처리하지 않아도 된다.

 

그렇다면 스프링에서는 어떻게 이 코드들을 인식하고 처리하는 걸까? 비밀은 다음 파일에 있다.

// sql-error-codes.xml 파일
<bean id="H2" class="org.springframework.jdbc.support.SQLErrorCodes">
     <property name="badSqlGrammarCodes">
         <value>42000,42001,42101,42102,42111,42112,42121,42122,42132</value>
     </property>
     <property name="duplicateKeyCodes">
         <value>23001,23505</value>
     </property>
 </bean>
 <bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
     <property name="badSqlGrammarCodes">
         <value>1054,1064,1146</value>
     </property>
     <property name="duplicateKeyCodes">
         <value>1062</value>
     </property>
 </bean>
  • 각 코드 별로 에러명이 분류되어 있는 것을 확인할 수 있다.

 

 

정리

스프링이 예외를 추상화 해준 덕분에, 서비스 계층은 특정 리포지토리의 구현 기술과 예외에 종속적이지 않게 되었다. 따라서 서비스 계층은 특정 구현 기술이 변경되어도 그대로 유지할 수 있게 되었다. 다시 DI를 제대로 적용할 수 있게 된 것이다.

추가로 서비스 계층에서 예외를 catch해서 복구해야 하는 경우, 예외가 스프링이 제공하는 데이터 접근 예외로 변경되어서 서비스 계층에 넘어오기 때문에, 필요한 경우 예외를 잡아서 복구하면 된다.

 

 

Reference

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