개발자로 후회없는 삶 살기
[문법] 스프링 예외 추상화 본문
서론
※ 과거에 기록한 내용에서 중요한 부분만 발췌하여 모두가 이해하기 쉽게 다시 서술한다.
본론
- 자바 예외 원칙
1) 거의 대부분 런타임 예외를 사용
2) 체크 예외는 잘 안 쓰고, 비즈니스 로직상 너무 중요해서 의도적으로 던질 때만 사용
-> 예시
해당 예외를 잡아서 반드시 처리해야 하는 문제일 때만 체크를 사용하고 나머지는 다 언체크 사용
🚨 체크 예외의 문제점
무조건 잡거나, 잡지 못하면 throw를 선언해 던져야만 한다.
DB나 네트워크 오류는 어플리케이션 로직에서 처리할 방법이 없다. 그래서 이들이 체크 예외라면 모두 밖으로 throws를 선언하고 던져야한다. 컨트롤러도 던지면 was에서 controlladvice에서 공통으로 처리하는데 이러한 오류는 500 에러로 해결이 불가능한 공통 예외는 고객한테는 지금 서비스에 문제가 있다는 표시만 하고 별도의 오류 로그를 남기고 개발자가 오류를 빨리 인지할 수 있도록, 메일 알림을 통해서 전달 받아야한다.
🚨 문제점
서비스 입장에서는 예외를 해결할 수도 없어서 터졌다는 사실을 알고 싶지도 않은데, 체크여서 선언하고 던져야 한다. 근데 이게 SQL 예외에 의존하는 것이고 JDBC 기술이라서 향후 JPA로 변경하면 JPA 예외에 의존하도록 고쳐야한다.
- 정리
처리할 수 있는 체크 예외라면 서비스에서 처리하겠지만, 지금처럼 복구 불가능한 SQL 에러 같은 시스템 레벨에서 올라온 예외들은 서비스에서 처리 못한다. 그리고 실무에서 발생하는 대부분의 예외들은 이런 시스템 예외이다. 이런 경우 체크 예외를 하면 복구 불가능한 예외를 서비스에서 모두 알고 있어야 하고 불필요한 의존관계 문제가 생긴다.
→ 해결방법
1) 예외 전환
레포지토리에서 JDBC 기술을 쓰면 SQL 예외, JPA를 쓰념 JPA 예외가 반드시 터진다. 근데 이는 체크 예외라서 명시해야하고, 하지만 서비스에서 처리할 수 없는 예외이다. 이를 런타임 예외로 바꿔서 던지면 throws를 명시하지 않아도 자동으로 던져진다.
2) 복구 불가능한 예외 처리
시스템에서 발생하는 예외는 서비스나 컨트롤러에서 복구 불가능해서 신경쓰지 않아도 된다. 이후 was에서 공통처리한다.
- 체크 예외와 인터페이스
서비스 계층은 기술에 의존하지 않고 순순하게 유지하는게 좋다. 이러면 예외에 대한 의존도 함께 해결해야한다.
지금 계좌 이체 코드는 트랜잭션 코드를 프록시에 넣고 파라미터도 매니저로 없앴는데 sql 예외가 남아있다. 이는 서비스가 처리할 수 없는 시스템 예외라서, 해결하려면 런타임으로 전환해서 던지면 된다.
기존에는 예외를 e로 던졌는데, 그러면 체크 예외를 던지는 것이라서 런타임 예외로 바꿔서 던지면 된다.
→ 남은 문제
지금은 레포에서 올라오는 예외를 전부 MyDB 런타임 예외로 처리한다. 하지만, 특정한 예외의 경우 복구를 시도할 수도 있다. 지금 예외가 sql 문법 오류인지 기본 키 오류인지 구분할 방법이 없고, 예외를 상황별로 구분해서 처리하는 방법이 필요하다.
sql 예외는 에러 코드를 가지고 있는데 이를 매칭해서 원하는 에러를 나타내는 런타임 사용자 지정 에러로 바꿔서 던지면 된다.
서비스에서 해당 예외를 잡고 싶으면 잡을 수 있는 언체크 예외가 된다.
- 정리
db 예외 코드를 db에 어떤 오류가 있는지 확인할 수 있고 sql 예외를 사용자 런타임 예외로 변환하여 서비스 계층은 순수성을 유지할 수 있었다. 예외 누수를 해결하고 sql 기술에 의존하지 않게 됐다.
- 남은 문제
sql 예외가 db마다 다 다르고 오류는 수백 가지가 있다. 이 모든 에러 상황에 맞는 예외를 다 사용자 지정으로 만드는 것이 아니고 스프링이 추상화해놨다.
→ 스프링이 제공하는 예외 변환기
db에서 발생한 오류 코드를 분석하여, 스프링이 정의한 예외로 자동으로 변환해주는 변환기를 제공한다. 스프링이 예외 코드와 스프링 정의 예외를 매칭을 다 해놔서 자동으로 변환해준다.
예외 변환기를 추가한다. sql 예외(e)를 분석해서 변환해준다.
여기서는 문법 오류로 변환해준다. 스프링이 변환해주지 않으면 레포에서 if문을 다 나누고 서비스에서 해결할 수 없는 모든 예외를 사용자 정의 예외로 만들어서 대신 날려야 했는데 스프링이 분석을 다 해주고, 레포지토리에서 발생하는 SQL 예외를 스프링 런타임 예외로 변환해서 날려준다.
최상위 DataAccessException의 부모가 런타임이라서 예외 누수도 해결된다. 서비스에서는 이제 데이터 접근 예외를 필요한 경우 잡아서 복구하면 된다.
이전에 if와 사용자 지정 예외 만드는 것이 전부 없어지고 스프링에 의존하여 서비스가 예외 추상화 계층에 맞게 약속만 하면 된다. 하지만 대부분 잡지 않고 런타임으로 자동으로 throws 된다.
'[백엔드] > [spring | 학습기록]' 카테고리의 다른 글
[테스트] 컨트롤러 테스트 하는 방법 (with Mockito) (0) | 2025.01.08 |
---|---|
[문법] 스프링 트랜잭션 전파 활용 (0) | 2024.08.08 |
[문법] 스프링 트랜잭션 이해와 적용 (0) | 2024.08.06 |
[문법] 파일 업로드 (0) | 2024.08.05 |
[문법] 스프링 예외처리 (0) | 2024.08.05 |