프레임워크(Framework)/Spring

[Spring DB] 스프링 예외 추상화

잇트루 2022. 11. 23. 00:28
반응형
본 내용은 온라인 강의 사이트 인프런의 김영한 님의 강의 내용이 포함되어 있습니다.
'스프링 DB 1편 - 데이터 접근 핵심 원리'
 

스프링 DB 1편 - 데이터 접근 핵심 원리 - 인프런 | 강의

백엔드 개발에 필요한 DB 데이터 접근 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 DB 접근 기술의 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., - 강의

www.inflearn.com

 

 

웹 애플리케이션을 개발하면서 데이터 접근 계층에서 발생하는 예외(SQLException, ConnectException)는 많은 문제점들이 있다.

  • 데이터 접근 계층에서 발생하는 예외는 체크 예외
    • 서비스나 컨트롤러는 데이터 접근 계층에서 발생하는 예외를 처리할 수 없다.
    • 따라서 throws를 통해 밖으로 던지는 방법 외에는 없다.

 

  • throws를 통해 밖으로 던지더라도 문제점이 해결되지 않음
    • 예외에 대한 복구가 불가능한 문제가 발생한다.
    • 서비스나 컨트롤러는 throws를 선언해야 하는 의존 관계에 대한 문제가 발생한다.

 

  • 예외 변환을 통해 해결하더라도 데이터 접근 계층의 추상화 단계에서 다시 발목을 잡게 된다.
    • 리포지토리를 인터페이스화 하게 되면 메서드에 다시 throws를 선언해 주어야 하는 문제가 발생한다.
    • SQLException은 체크 예외이기 때문이다.

 

  • 리포지토리 인터페이스에 예외 변환을 다시 적용하여 모든 문제를 해결한다면?
    • 리포지토리 인터페이스와 서비스 계층의 순수성을 유지할 수 있게 된다.
    • 하지만, 리포지토리에서 넘어오는 특정한 예외의 경우 복구를 시도할 수 있다.
    • 이를 해결하기 위해서는 데이터베이스 접근 예외를 직접 만들어서 처리해야 한다.

 

  • 데이터베이스 접근 계층에 예외를 변환하는 과정은 결코 쉽지만은 않다.
    • SQL ErrorCode는 같은 문제의 예외여도 각각의 데이터베이스마다 다르다.
    • 결국 데이터베이스가 변경될 때마다 ErrorCode도 모두 변경해 주어야 한다.

 

뿐만 아니라 SQL 관련 예외는 수십에서 수백 가지 오류코드가 존재한다.

모든 상황에 맞는 예외를 모두 직접 만들어서 처리 하기에는 너무나 비효율적이다.

결국, 데이터 접근 계층의 체크 예외 문제를 직접 해결하는 것은 시간과 비용을 낭비하게 된다.

 

스프링 예외 추상화

스프링은 앞서 설명한 문제점을 해결하기 데이터 접근과 관련된 예외를 추상화해서 제공한다.

  • 스프링은 데이터 접근 계층에 대한 수십 가지 예외를 정리해서 일관된 예외 계층을 제공한다.
  • 각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있어 기술 변경에도 유연하다.
  • 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다.
  • JDBC나 JPA를 사용할 때 발생하는 예외를 스프링이 제공하는 예외로 변환해주는 역할도 제공한다.

 

 

스프링 데이터 접근 예외 계층(일부 계층은 생략되어 있음)

  • 스프링 데이터 접근 예외의 최고 상위 계층은 org.springframework.dao.DataAccessException이다.
  • DataAccessException은 RuntimeException을 상속받는다.
    • 스프링이 제공하는 데이터 접근 계층의 모든 예외는 런타임 예외로 체크 예외로 인해 발생하는 문제를 해결한다.
  • DataAccessException은 NonTransient 예외와 Transient 예외로 구분된다.
    • Transient : 일시적이라는 뜻으로 쿼리 타임아웃(Timeout), 락(Lock)과 관련된 예외이다.
    • NonTransient : 일시적이지 않다는 뜻으로 SQL 문법 오류, 데이터베이스 제약조건 위배 등이 있다.

 

Transient 예외는 일시적이기 때문에 데이터베이스 상태가 좋아지거나, 락이 풀렸을 때 다시 시도하면 성공할 가능성이 있으나, NonTransient는 같은 SQL을 그대로 반복해서 실행하더라도 실패한다.

 

예외 변환기

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

따라서, Intro에서 설명한 데이터베이스마다 다른 SQL ErrorCode를 모두 변경해야 하는 문제를 해결한다.

 

SQL 예외 변환기 선언

DataSource dataSource;
SQLErrorCodeSQLExceptionTranslator exTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);

 

SQL 예외 변환기 사용

DataAccessException resultEx = exTranslator.translate("select", sql, e);
log.info("resultEx", resultEx);
  • translate() 메서드
    • “select” : 읽을 수 있는 설명
    • sql : 실행한 sql
    • e : 발생한 SQLException
  • SQL 문법이 잘못되었을 경우 BadSqlGrammarException이 발생하게 된다.
    • 반환 타입이 최상위 타입인 DataAccessException이다.
    • BadSqlGrammarException은 DataAccessException을 상속받는다.
    • 따라서 실제로 반환되는 예외는 BadSqlGrammarException이다.

 

스프링의 예외 변환기

스프링은 데이터베이스마다 다른 오류 코드 문제와 해당 오류 코드를 예외 체계에 맞추어 예외를 직접 변환해야 하는 문제를 해결하기 위한 설정 파일을 제공하고 있다.

 

org.springframework.jdbc.support.sql-error-codes.xml

<bean id="DB2" name="Db2" class="org.springframework.jdbc.support.SQLErrorCodes">
	<property name="databaseProductName">
		<value>DB2*</value>
	</property>
	<property name="badSqlGrammarCodes">
		<value>-007,-029,-097,-104,-109,-115,-128,-199,-204,-206,-301,-408,-441,-491</value>
	</property>
	<property name="duplicateKeyCodes">
		<value>-803</value>
	</property>
	<property name="dataIntegrityViolationCodes">
		<value>-407,-530,-531,-532,-543,-544,-545,-603,-667</value>
	</property>
	<property name="dataAccessResourceFailureCodes">
		<value>-904,-971</value>
	</property>
	<property name="transientDataAccessResourceCodes">
		<value>-1035,-1218,-30080,-30081</value>
	</property>
	<property name="deadlockLoserCodes">
		<value>-911,-913</value>
	</property>
</bean>
...

스프링 SQL 예외 변환기는 SQL ErrorCode를 위 설정 파일에 대입하여 어떤 데이터 접근 예외로 전환해야 할지를 찾아낸다.

 

예를 들어, H2 데이터베이스에서 SQL ErrorCode가 42000인 예외가 발생한다면, badSqlGrammarCodes이기 때문에 BadSqlGrammarException을 반환하는 것이다.

 

스프링은 10개 이상의 관계형 데이터베이스를 지원하고 있다.

반응형