-
체크 예외, 언체크(런타임) 예외스프링/스프링 기초DB 2023. 3. 19. 14:41
체크 예외(Checked Exception)
체크 예외 : Exception 하위 예외 중 RuntimeException을 제외한 에외
- 체크 예외는 컴파일러가 예외를 체크하는 예외이다.
체크 예외 장점 - 컴파일 오류를 발생시켜 만약에 개발자가 예외처리를 제대로 하지 않았을 때 개발자가 알아차릴 수 있도록 도와준다.
체크 예외 단점 - 개발자가 크게 신경쓰고 싶지 않은 예외까지 반드시 잡거나 던지도록 처리해야 한다.
static class MyCheckedException extends Exception { // Exception을 상속받으면 체크 예외 (RuntimeException은 언체크 예외) public MyCheckedException(String message) { super(message); } } static class Repository { public void call() throws MyCheckedException { throw new MyCheckedException("ex"); // 예외를 던질 때에는 무조건 밖으로 던져야한다. } }
CheckedException 클래스이다. 예외가 발생하면 무조건 잡아주거나 throws로 던져줘야한다.
언체크 예외(UnChecked Exception)
언체크예외 : RuntimeException과 그 하위 예외
- 언체크 예외는 컴파일러가 예외를 체크하지 않는 예외이다. 체크 예외와의 차이점은 throw를 선언하지 않고 생략 가능하고, 이 경우는 자동으로 예외를 던진다.
언체크 예외 장점 - 신경쓰고 싶지 않은 예외들을 전부 무시할 수 있다. 신경쓰고 싶지 않은 예외의 의존관계를 참고하지 않아도 된다.
언체크 예외 단점 - 개발자가 실수로 예외를 누락할 수도 있다.
static class MyUncheckedException extends RuntimeException { public MyUncheckedException(String message) { super(message); } } static class Repository { public void call() { throw new MyUncheckedException("ex"); // 언체크 예외는 메서드 뒤에 throws ~~~를 선언하지 않아도 된다. } }
UnCheckedException 클래스이다. 예외를 throws로 던져주지 않아도 자동으로 throws가 된다. 리포지토리에서 발생해도 서비스, 컨트롤러에서 선언도 하지 않고 그 다음 예외 공통처리 메서드에서 잡아주면 된다.
(자동으로 throws가 될 뿐, 예외는 잡아줘야 함)
체크 예외와 언체크 예외 차이점
- 체크 예외는 잡아서 처리하지 않으면 항상 throws에 던지는 예외를 선언해야 하는 반면에 언체크 예외는 예외를 잡아서 처리하지 않아도 throws를 생략할 수 있다.체크 예외를 꼭 사용해야 할 시점은 비즈니스 로직상 의도적으로 던지는 예외에서만 사용해야한다. 계좌 이체 실패나 로그인 ID, PW 불일치 등의 예외에서는 체크 예외를 주로 사용한다. 위의 상황과 같이 매우 심각한 문제는 개발자가 실수로라도 예외를 놓치면 안되기 때문에 체크 예외로 만들어서 컴파일러를 통해 놓친 예외를 인지할 수 있다.
체크 예외를 사용하게 되면 문제점
1. 복구 불가능한 예외
SQLException을 예를 들면 데이터베이스에 무언가 문제가 발생해서 서버가 다운되거나 데이터 자체에 뭔가 문제가 있을 수도 있다. 대부분의 서비스나 컨트롤러는 이런 문제를 해결할 수 없기 때문에 오류 로그를 남겨서 개발자가 해당 오류를 빠르게 인지할 수 있게 만들어주고 스프링의 ControllerAdvice 등을 통해서 공통으로 해결해야한다.
2. 의존 관계에 대한 문제
repository에서 예외가 발생한다고 가정하자. 서비스나 컨트롤러에서 예외를 처리할 수 없어도 어쩔 수 없이 throws 예외를 계속해서 선언해야한다.
-> Service와 Controller에서 java.sql.SQLException을 의존해야한다. 이것은 JDBC 기술이며 JDBC의 코드를 의존해야 한다는 것이다. 향후 리포지토리를 JDBC 기술이 아닌 다른 기술로 변경한다면, 그래서 SQLException이 아니라 JPAException으로 예외를 변경해야한다면 SQLException에 의존하던 모든 서비스와 컨트롤러를 JPAException에 의존하도록 수정해야한다.
static class Repository { public void call() { try { runSQL(); } catch (SQLException e) { throw new RuntimeException(e); // 기존의 체크 예외를 잡고 런타임 예외로 바꿔서 던진다. } } public void runSQL () throws SQLException { throw new SQLException("ex"); } }
위와 같이 SQLException이라는 체크 예외가 발생했을 때 체크 예외를 잡고 런타임 예외로 바꿔서 던져줘도 된다.
이 때 중요한 것은 throw new RuntimeException();이 아니라 무조건 기본 예외인 e를 넣어줘야한다.
그 이유는 장애가 났을 때에 원래 어떤 오류가 발생했는지 로그를 통해 확인해야하는데, 기존 예외를 넣어주지 않으면 어떤 예외가 발생했는지 알 수 없기 때문에 해결하는 데에 문제가 발새앟게 된다.
예외를 전환할 때에는 꼭 기존 예외를 포함해야 한다!
결론
체크 예외는 모든 예외를 직접 처리해줘야하고, 의존 관계에 있어서도 좋은 방법이 아니기 때문에 최근 라이브러리들은 대부분 런타임 예외를 기본으로 제공한다. JPA, 스프링 기술도 런타임(언체크) 예외를 기본으로 사용한다.
런타임 예외도 필요하면 잡아서 처리할 수 있고, 그렇지 않으면 자연스럽게 던지도록 둘 수 있다. 신경쓰지 않는다면 예외를 공통으로 처리하는 부분을 앞에 만들어서 처리하면 된다.
-> 런타임 예외는 개발자가 놓칠 수 있기 때문에 문서화가 중요하다.
'스프링 > 스프링 기초DB' 카테고리의 다른 글
데이터 접근 기술 - JPA (0) 2023.03.26 db 테스트에서의 트랜잭션 (0) 2023.03.25 스프링 예외 추상화 (0) 2023.03.19 DB 트랜잭션 - 자동 커밋, 수동 커밋 / DB 락 (0) 2023.03.14 DB - 커넥션풀과 DataSource (0) 2023.03.14