Replies: 4 comments
|
오 일찍 올려주셨네요 ~ 수고하셨습니다 !! 😃 |
0 replies
|
수고하셧습니다 ! |
0 replies
|
수고하셨습니당 ~~ |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
체크 예외와 인터페이스
예외 처리에 대한 부분을 스프링이 어떻게 깔끔하게 해결하는지 + 반복코드 어떻게 간단히 하는지 알아보자.
서비스 계층은 특정 기술에 의존하지 않고 순수하게 유지해야함.
이렇게 하려면 예외에 대한 의존도 해결해야함.
지금까지 여러 노력을 통해 서비스 계층에서 트랜잭션 코드를 없앴음.
지금 남은 SQLException 던지는걸 해결해야함.
서비스가 처리할 수 없으므로 리포지토리가 던지는
SQLException체크 예외를 런타임 예외로 전환해서 서비스 계 층에 던지자. 이렇게 하면 서비스 계층이 해당 예외를 무시할 수 있기 때문에, 특정 구현 기술에 의존하는 부분을 제거하 고 서비스 계층을 순수하게 유지할 수 있다.이를 코드로 적용해보자.
먼저 MemberRepository 인터페이스를 도입해 구현 기술을 쉽게 변경할 수 있게 해보자.

지금은 서비스가 특정 리포지토리에 의존하고 있어서 구현 기술을 갈아끼기가 쉽지 않음.
리포지토리에 인터페이스를 도입하면, DI 를 통해 구현 기술을 갈아 끼울 수 있음.
특정 기술에 종속되지 않는 순수한 인터페이스이다. 이 인터페이스를 기반으로 특정 기술을 사용하는 구현체를 만들면 된다.
체크 예외와 인터페이스
기존에 이렇게 인터페이스를 도입하지 않은 이유가 있음. 문제가 하나 있기 때문.
왜냐하면
SQLException이 체크 예외이기 때문이다. 여기서 체크 예외가 또 발목을 잡는다.체크 예외를 사용하려면 인터페이스에도 해당 체크 예외가 선언 되어 있어야 한다.
예를 들면 다음과 같은 코드가 된다.
인터페이스에 throws SQLException 가 있음.
인터페이스가 이미 특정 기술에 종속적이 되어버림.
즉
MemberRepositoryV3가throws SQLException를 하려면MemberRepositoryEx인터페이스에도throws SQLException이 필요하다.참고로 구현 클래스의 메서드에 선언할 수 있는 예외는 부모 타입에서 던진 예외와 같거나 하위 타입이어야 한다.
- 예를 들어서 인터페이스 메서드에
throws Exception를 선언하면, 구현 클래스 메서드에throws SQLException는 가능하다.SQLException은Exception의 하위 타입이기 때문이다.결과적으로 특정 기술에 종속적인 인터페이스를 만들 수 밖에 없음.
인터페이스를 만드는 목적은 구현체를 쉽게 변경하기 위함 인데, 이미 인터페이스가 특정 구현 기술에 오염이 되어 버렸다. 향후 JDBC가 아닌 다른 기술로 변경한다면 인터페이 스 자체를 변경해야 한다.
런타임 예외와 인터페이스
런타임 예외는 이런 부분에서 자유롭다. 인터페이스에 런타임 예외를 따로 선언하지 않아도 된다. 따라서 인터페이스가 특정 기술에 종속적일 필요가 없다.
런타임 예외 적용
실제 코드에 런타임 예외 적용하기.
예외 변환 - 기존 예외 무시
이번엔 서비스에서 인터페이스를 사용하도록 변경
테스트 코드
테스트 코드 중 스프링빈 조립하는 부분만 변경.
정리
체크 예외를 런타임 예외로 변환하면서 인터페이스와 서비스 계층의 순수성을 유지할 수 있게 되었다.
덕분에 향후 JDBC에서 다른 구현 기술로 변경하더라도 서비스 계층의 코드를 변경하지 않고 유지할 수 있다.
남은 문제
리포지토리에서 넘어오는 특정한 예외의 경우 복구를 시도할 수도 있다. 그런데 지금 방식은 항상
MyDbException이라는 예외만 넘어오기 때문에 예외를 구분할 수 없는 단점이 있다. 만약 특정 상황에는 예외를 잡아서 복구하고 싶으 면 예외를 어떻게 구분해서 처리할 수 있을까?ex) 같은 아이디가 발생 -> 서비스에서 잡아서 아이디 추천하는 로직을 추가할 수도 그러나 지금은 예외 구분 불가.
데이터 접근 예외 직접 만들기
데이터베이스 오류에 따라 특정 예외는 복구 필요할수도.
예를 들어서 회원 가입시 DB에 이미 같은 ID가 있으면 ID 뒤에 숫자를 붙여서 새로운 ID를 만들어야 한다고 가정해보자.
ID를
hello라고 가입 시도 했는데, 이미 같은 아이디가 있으면hello12345와 같이 뒤에 임의의 숫자를 붙여서 가 입하는 것이다.위에서 말한 아이디 중복이 발생할 때,
데이터 베이스에서 오류 코드를 반환하고,
이 오류 코드를 받은 JDBC드라이버가 SQLException 을 던짐.
그래서 SQLException 에는 데이터베이스에서 제공하는 errorCode 가 들어있음.
H2 데이터베이스의 키 중복 오류 코드
이를 활용하면 데이터베이스에서 어떤 문제가 발생했는지 알 수 있음.
H2 데이터베이스 예
23505: 키 중복 오류42000: SQL 문법 오류참고로 같은 오류여도 각각의 데이터베이스마다 정의된 오류 코드가 다르다. 따라서 오류 코드를 사용할 때는 데이터베이스 메뉴얼을 확인해야 한다.
예) 키 중복 오류 코드
235051062H2 데이터베이스 오류 코드 참고
https://www.h2database.com/javadoc/org/h2/api/ErrorCode.html
그런데 이를 활용하려면 다시 SQLException 을 서비스 계층에 던져야함...
이 문제를 해결하려면 앞서 배운 것 처럼 리포지토리에서 예외를 변환해서 던지면 된다.
SQLException->MyDuplicateKeyException테스트 코드
실행해보면 2개가 저장된것 확인 가능.

리포지토리에서
키 중복 오류의 경우 MyDuplicateKeyException() 을 던짐.
서비스에서
MyDuplicateKeyException 이 온 경우 아이디를 새로 만들어 복구 시도함.
MyDuplicateKeyException예외가 올라오면 이 예외를 잡는다.generateNewId(memberId)로 새로운 ID 생성을 시도한다. 그리고 다시 저장한다. 여기가 예 외를 복구하는 부분이다.MyDbException)면 로그만 남기고 다시 예외를 던진다.정리
SQLException을 특정 기술에 의존하지 않는 직접 만든 예외인MyDuplicateKeyException로 변환 할 수 있었다.MyDuplicateKeyException을 사용해서 문제를 복구하고, 서비스 계층의 순수성도 유지할 수 있었다.남은 문제
예) 키 중복 오류 코드
H2:
23505MySQL:
1062All reactions