서비스 계층은 가급적 특정 구현 기술에 의존하지 않고, 순수하게 유지하는 것이 좋다.
인터페이스를 이용하기
특정 기술에 종속하지 않는 순수한 인터페이스를 만들어서 인터페이스 의존하면 된다.
하지만 인터페이스를 도입해도 추상 메서드에 throw SQLException을 던져줘야 한다.
구현클래스의 메소드에 throw SQLException을 하려해도 구현 클래스의 메서드도 예외를 던질 수 있다.
- 참고로 구현 클래스의 메서드에 선언할 수 있는 예외는 부모 타입에서 던진 예외와 같거나 하위 타입이어야 한다.
- 예를 들어서 인터페이스 메서드에 throws Exception 를 선언하면, 구현 클래스 메서드에 throws SQLException 는 가능하다. SQLException 은 Exception 의 하위 타입이기 때문이다.
특정 기술에 종속되는 인터페이스 구현 기술을 쉽게 변경하기 위해서 인터페이스를 도입하더라도 SQLException 과 같은 특정 구현 기술에 종속적인 체크 예외를 사용하게 되면 인터페이스에도 해당 예외를 포함해야 한다. 하지만 이것은 우리가 원하던 순수한 인터페이스가 아니다. JDBC 기술에 종속적인 인터페이스일 뿐이다. 인터페이스를 만드는 목적은 구현체를 쉽게 변경하기 위함인데, 이미 인터페이스가 특정 구현 기술에 오염이 되어 버렸다. 향후 JDBC가 아닌 다른 기술로 변경한다면 인터페이스 자체를 변경해야 한다. |
런타임 예외와 인터페이스 런타임 예외는 이런 부분에서 자유롭다. 인터페이스에 런타임 예외를 따로 선언하지 않아도 된다. 따라서 인터페이스가 특정 기술에 종속적일 필요가 없다. |
정리하자면 인터페이스로 하려해도 예외 누수가 우선 해결되야한다.
catch(SQLException e){
throw new RuntimeCustomException(e);
}
- 체크 예외를 런타임 예외로 변환하면서 인터페이스와 서비스 계층의 순수성을 유지할 수 있게 되어 향후 다른 구현 기술로 변경하더라도 서비스 계층의 코드를 변경하지 않고 유지할 수 있지만,
리포지토리에서 넘어오는 특정한 예외의 경우 복구를 시도할 수도 있다. 위와 같은 방식은 항상 RuntimeCustomException라는 예외만 넘어오기 때문에 예외를 구분할 수 없다는 단점이 있다. 이럴 때 어떻게 해야할까?
데이터 접근 예외 직접 만들기
데이터베이스 오류에 따라서 특정 예외는 복구하고 싶을때 예를 들어 회원 가입시 DB에 이미 같은 ID가 있으면 ID 뒤에 숫자를 붙여서 새로운 ID를 만들어야 한다고 가정해보자.
ID를 hello 라고 가입 시도 했는데, 이미 같은 아이디가 있으면 hello12345 와 같이 뒤에 임의의 숫자를 붙여서 가입하는 것이다.
데이터를 DB에 저장할 때 같은 ID가 이미 데이터베이스에 저장되어 있다면, 데이터베이스는 오류 코드를 반환하고,
이 오류 코드를 받은 JDBC 드라이버는 SQLException 을 던진다. 그리고 SQLException 에는 데이터베이스가 제공하는 errorCode 라는 것이 들어있다.
H2 데이터베이스 예
23505 : 키 중복 오류
42000 : SQL 문법 오류
참고로 같은 오류여도 각각의 데이터베이스마다 정의된 오류 코드가 다르다. 따라서 오류 코드를 사용할 때는 데이터베이스 메뉴얼을 확인해야 한다.
예) 키 중복 오류 코드
H2 DB: 23505
MySQL: 1062
서비스 계층에서는 예외 복구를 위해 키 중복 오류를 확인할 수 있어야 한다. 그래야 새로운 ID를 만들어서 다시 저장을 시도할 수 있기 때문이다. 이러한 과정이 바로 예외를 확인해서 복구하는 과정이다. 리포지토리는 SQLException 을 서비스 계층에 던지고 서비스 계층은 이 예외의 오류 코드를 확인해서 키 중복 오류( 23505 )인 경우 새로운 ID를 만들어서 다시 저장하면 된다.
그런데 SQLException 에 들어있는 오류 코드를 활용하기 위해 SQLException 을 서비스 계층으로 던지게 되면, 서비스 계층이 SQLException 이라는 JDBC 기술에 의존하게 되면서, 지금까지 우리가 고민했던 서비스 계층의 순수성이 무너진다.
이 문제를 해결하려면 앞서 배운 것 처럼 리포지토리에서 예외를 변환해서 던지면 된다.
- 남은 문제
- SQL ErrorCode는 각각의 데이터베이스 마다 다르다. 결과적으로 데이터베이스가 변경될 때 마다 ErrorCode
도 모두 변경해야 한다. - 데이터베이스가 전달하는 오류는 키 중복 뿐만 아니라 락이 걸린 경우, SQL 문법에 오류 있는 경우 등등 수십 수백가지 오류 코드가 있다. 이 모든 상황에 맞는 예외를 지금처럼 다 만들어야 할까?
- SQL ErrorCode는 각각의 데이터베이스 마다 다르다. 결과적으로 데이터베이스가 변경될 때 마다 ErrorCode
'Spring > JDBC' 카테고리의 다른 글
[Spring DB] Jdbc Template (0) | 2024.08.12 |
---|---|
[Spring DB 개념] 스프링 예외 추상화 (0) | 2024.08.12 |
[Spring DB] 스프링 부트 리소스 등록 (0) | 2024.08.08 |
[Spring DB 개념] 트랜잭션 - AOP(proxy) (1) | 2024.08.08 |
[Spring DB 개념] 트랜잭션 탬플릿 (0) | 2024.08.08 |