DB- 예외처리/스프링 예외 추상화

Shaun·2022년 9월 12일
1

DB

목록 보기
5/5
post-thumbnail

데이터 접근시 예외 처리에대해서 스프링은 어떻게 해결했는지 알아보자.

그전에 예외에대해 간단하게 살펴본다.

예외

  • 예외는 크게 Exception 과 Error로 나뉘며 Error은 개발자가 해결할수 없는 오류이다. 우리가 눈여겨 봐야할건 Exception

  • Exception은 크게 두가지로 나뉜다.
    = 컴파일러가 체크하는 체크예외
    = 컴파일러가 체크하지 않는 언체크 예외.

체크예외

  • 예외처리가 필수
  • 장점은 개발자 실수를 컴파일러가 잡아주는 안전장치
  • 단점은 모든 체크예외를 처리해줘야한다.(의존관계 문제)

언체크예외

  • 예외처리 필수 x
  • 장점은 무시하고 싶은 예외는 무시할수 있다.
  • 단점은 컴파일러가 체크를 대신 해주지 않아 개발자의 실수가 일어날 가능성이 크다.
  • 런타임 에러는 보통 생략하지만 정말 중요한 예외인 경우에는 선언해두어 해당 코드를 호출하는 개발자가 이런 예외가 발생한다는 점을 인지할수있다.

기본원칙

  • 기본적으로 런타임 예외를 사용하도록 하자

  • 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에서만 사용 -> 반드시 예외를 잡아서 처리해야하는 문제일 경우(중요 비즈니스 로직.. 계좌이체실패 or 결제 포인트부족 등)

  • 중요한 비즈니스 로직은 개발자가 실수 할수 있으므로 체크 예외로 만들어 컴파일러가 체크하도록 해주자

Why RuntimeException???

그렇다면 왜 런타임 예외를 사용해야 할까??? 체크 예외는 컴파일러가 체크해준다는 장점도 있지만 크게 두가지 문제가 있다.

  1. 복구 불가능한 예외
  • 대부분 예외는 복구불가능하다. 서비스단과 컨트룰러에서 처리해야하며 이런 문제들은 일관성있게 공통을 처리해야한다. 로그를 남기고 개발자가 빠르게 인지하는것이 중요하다.
  1. 의존 관계에 대한 문제
  • 순수한 서비스 단에 SQLException(JDBC기술) 에 의존함으로 만약 JDBC 기술이 다른 기술로 바뀌면 해당 기술에 의존하는 코드들도 다 바꿔줘야한다.

해결책 - 언체크 예외 활용

  • 체크예외를 언체크 예외로 바꿔주자 (ex: SQLException -> RuntimException)

  • 런타임 예외이기 떄문에 컨트룰러,서비스단에서 예외를 처리할 필요가 없다.

  • 그만큼 throw 반복코드를 사용하지 않아서 코드도 깔끔해지고 의존관계도 해결된다.

언체크 예외 활용 주의할점

  • 변환코드를 따로 작성해줘야함

  • 언체크 예외 활용시 문서화를 잘해주자(컴파일러가 체크해주지 않아 빠뜨리기쉽다)

언체크 활용

  • Try-Catch 문으로 잡을때 SQLException 발생시 RuntimeExcepition으로 바꿔 주었다.

  • 여기서 눈여겨 봐야할점은 변환시 이전 예외를 함께 넣어 보내준다는 점이다.(RuntimeException (e)) 그래야 나중에 로그를 볼때 뭐때문에 예외가 터졌는지 알수 있기 떄문이다.

  • 런타임 예외를 활용하면 중간에 기술이 변경되어도 의존관계를 맺지 않았기때문에 연쇄변경전파는 현저히 떨어지게 된다.

체크 예외와 인터페이스

  • 저번에 @Transactional 을 사용해 비즈니스 로직을 깔끔하게 정리했지만 Repository에서 서비스단으로 던지는 예외로 인한 예외누수 문제가 생겼다.

  • 서비스 계층은 가급적으로 특정 구현 기술에 의존하지 않아야 한다는 원칙에 위배된다.

  • 예외를 무시할수 있는 RuntimeException으로 바꿔주자

인터페이스 도입

  • 인터페이스를 도입하면 서비스단에서는 인터페이스에만 의존하면 되고 DI를 활용해 구현기술을 변경할수 있다.

  • 기존 런타임에러가 아닌 체크예외를 사용하면서 인터페이스를 사용할경우 인터페이스, 인터페이스 구현체까지 throws를 해줘야해서 좋지 않은 방법이다. 인터페이스 또한 순수해야 하는데 특정기술에 오염되버리는 문제도 발생

=> 결론적으로 인터페이스 + 런타임에러 방법으로 예외처리를 무시해도 된다는점(서비스단의 순수성과) 인터페이스를 통한 구현체 변환의 자유로움을 챙길 생각이다.

인터페이스+런타임에러

  • 기존코드 catch 부분에서 SQL예외가 발생하면 런타임 에러로 변경해주는 부분이다.

  • 반드시 이전 에러를 같이 넣어서 보내줘야 한다. (그래야 에러 파악)

  • 서비스단에서는 런타임에러라 따로 throw 해줄 필요가 없어져 순수성을 유지할수 있다.

하지만 모든예외가 MyDbException으로 올라오기때문에 예외를 구분할수가 없다!

데이터 접근 예외 직접 만들기

  • 에러가 발생할때 데이터베이스에서 제공하는 errorCode라는것도 같이 담겨져 나온다.이 에러코드를 활용하면 데이터베이스에 무슨 문제가 발생했는지 알수 있다.

  • 이 에러코드마다 의미하는 에러내용이 다르고 각각의 데이터베이스마다도 에러코드 번호가 다르다.

  • 에러코드를 활용해 에러가 발생시 다른 상황을 조치하는(복구) 로직을 짜기위해서는 서비스단으로 예외를 던져야 하는데 그러면 순수성이 무너지게 된다. 앞서 배운 방법 런타임에러로 변환해서 던져주자.

  • id값이 중복 됐다는 예외이다. 기존에 만든 MyDbException을 상속받아서 의미있는 계층 형성을 한다.

  • 우리가 직접만든 예외이기 때문에 특정기술에 의존하지 않는다.

  • 서비스단 순수성을 위한 런타임 변환 + 에러코드 활용해서 예외복구

  • 어떤오류가 일어나는지 알수 있으며 특정기술에 의존하지 않는다는 장점이 있지만 데이터베이스마다 에러코드가 다다르며 그 종류도 엄청나다. 우리가 그걸 하나하나 다 만들수는 없다.

그래서 나온게...

스프링 예외 추상화

  • 스프링에서는 이미 이런 문제에대해 다 정리가 돼있다.

  • 각각의 예외는 특정 기술에 종속적이지 않게 설계되어 있다. 따라서 서비스 계층에서도 스프링이 제공하는 예외를 사용하면 된다. 예를 들어서 JDBC 기술을 사용하든, JPA 기술을 사용하든 스프링이 제공하는 예외를 사용하면 된다.

  • 런타임 예외가 최상위

  • DataAccessException은 크게 두가지로 나뉜다
  1. Transient = 일시적이라는뜻, 하위 예외는 동일한 SQL을 다시 시도했을때 성공가능성이 있다(ex: DB lock)
  2. NonTransient = 일시적이지 않다. 동일 SQL실행시 반복실패(ex: 문법오류)

스프링 예외 변환기

  • 쉽게 말하면 데이터베이스에서 나온 예외-> 스프링에서 정의한 예외로 자동 변환 기능 제공

  • 읽을 수 있는설명,sql, 예외만 파라미터로 넘겨주면 자동으로 변환해준다.

  • 데이터베이스마다 에러코드가 다르지만 스프링에서는 이미 모든에러코드에 대한 정의가 되어있기 때문에 가능한일

  • 예외 변환기 사용은 그리 어렵지않다.

JDBC 반복 문제 - JdbcTemplate

  • 지금까지 서비스단의 순수성을 유지하고 많은 문제를 잘 해결했다. 마지막으로 JDBC의 반복적인 코드문제를 해결하기위해 JdbcTemplate 사용법에 대해 알아보자

  • 사용법은 어렵지 않다. JdbcTemplate를 주입받고 sql과 바인딩값만 잘 넣어주면 됀다.
profile
호주쉐프에서 개발자까지..

0개의 댓글