컴파일러가 해당 예외를 처리(catch 하거나, 메서드 시그니처에 throws 선언)하였는지 검사(Check)하는 종류의 예외임 → 예외 처리가 강제된다!
CheckedException을 새로 만들고 싶다면 extends Exception!
UnCheckedException
RuntimeException과 그 하위 예외 + Error
(위에서 언급했듯이, Error는 사용하는 게 아니기에) RuntimeException으로 퉁쳐서 말할 때가 많음)
컴파일러는 해당 종류의 예외를 처리했는지 검사하지 않음!(Unchecked) → 처리 강제 X
UnCheckedException을 새로 만들고 싶다면 extends RuntimeException!
비교
예외 처리가 강제되느냐의 문제입니다.
예외 처리가 강제되는 CheckedException의 경우 런타임에 생길수도 있는 예외를 컴파일 시점에 미리 잡을 수 있다는 장점이 있지만,
예외처리가 강제된다는 점에서, 처리할 수 없는 경우에는 문제가 됩니다.
예외 처리가 강제되지 않는 UncheckedException은 반대입니다.
어떨 때 사용할까?
기본적으로 RuntimeException 쓰자!
CheckedException의 경우, 이 예외를 처리하지 못 할 경우 정말 치명적인 문제가 생기며 && 그 예외를 처리해줄 수 있을 때만 사용하자.
왜 CheckedException을 가급적 안 쓰는 편이 나을까?
실제로 발생하는 CheckedException은 처리할 수 없는 것들이 많다.
네트워크 연결 문제나, DB 연결 상태가 깨져서 발생하는 예외를 개발자가 애플리케이션 코드에서 잡아줄 수는 없다!
예외 처리 강제로 인해 해당 예외에 대한 의존성이 생긴다.
예시) Repository
Repository 인터페이스를 규정, 그 구현체로 JdbcRepository 만들었을 경우
JdbcRepository는 SQLException을 던지게 된다.
구현체 JdbcRepository의 메서드 시그니처에 throws문이 붙기 때문에, 해당 메서드를 규정하고 있는 인터페이스의 메서드 시그니처에도 throws 문이 붙어야 하는데,
이 경우 JDBC와 관련된 SQLException에 대한 의존성을 Repository 인터페이스 자체가 갖게 된다.
만약 JPA로 변경한다면, 위의 SQLException에 대한 코드는 불필요한데도 인터페이스가 해당 예외를 달고 있다는 문제점이 생긴다.
종합해보면
어차피 처리도 못할 예외를
throws 하게 되면 인터페이스도 오염시키고, 해당 계층의 윗 계층까지 오염시키게 된다.
일단적인 웹 어플리케이션의 경우라면, Repository를 호출한 Service도, Service를 사용하는 Controller도 ,...
그냥 catch해서 UncheckedException, RuntimeException으로 변환해주는 것이 낫다!
→ 예외 번역
애플리케이션 코드에서 처리할 수 없는 예외는 어떻게 다룰까?
예외가 catch되지 않고 끝까지 간다면
자바 콘솔 애플리케이션의 경우
예외가 main() 쓰레드까지 올라가고, 예외 로그와 함께 시스템 종료
웹 어플리케이션의 경우
WAS가 해당 예외를 받아서 처리: 보통은 오류 페이지를 보여줌
따라서 웹 어플리케이션의 경우
서블릿 오류 페이지
스프링을 사용할 경우 ControllerAdvice 사용하여 예외를 공통 처리할 수 있음
저 같은 경우 스프링 MVC 강의에서 학습했었는데, 3주차 과제, 웹 애플리케이션 개발 중이시라면 해당 방식을 학습, 적용하셔서 예외 처리하시면 깔끔하리라 생각합니다.
위 경우 보통 500에러가 될 것임
어차피 개발자가 애플리케이션 코드에서 처리하지 못 할 예외라면,
적절한 오류 로깅 + 빠르게 장애 상황을 인지할 수 있는 알림(슬랙, 지라 등등)이 중요하다!
예외와는 무관하게, 위와 관련하여 읽어볼만한 이야기는:
“함께 자라기” 라는 책에서는(88쪽, 실수는 예방하는 것이 아니라 관리하는 것이다)
실수를 예방하는 것 보다도 관리하는 것의 중요성에 대해서 이야기합니다.
문제가 안 생기게 노력하는 것도 중요하지만, 어차피 실수(여기서는 예외와 같은 문제상황이 되겠죠)는 어차피 터질 수 밖에 없기 때문에 상황이 발생했을 때 관리할 수 있는 역량을 강조하고 있습니다. 실제로 조직이나, 학습 양 쪽 모두 실수 예방보다 관리의 관점에서 접근할 때 성과가 더 잘 나온다는 얘기도 있네요.
TransientException: 동일 SQL을 다시 시도했을 때 성공할 가능성이 있는 일시적 예외
예시: 쿼리 타임아웃, 락
NonTransientException: 일시적이지 않음, 같은 SQL을 아무리 반복해서 시도해도 실패
예시: SQL 문법 오류, DB 제약조건 위배
스프링 예외 번역기
스프링은 DB에러코드를 읽어들여, 상황에 맞는 예외로 알아서 변환해 줌
} catch (SQLException e) {
var exceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(dataSource);
DataAccessException resultException = exceptionTranslator.translate("상황을 설명할 수 있는 글", sql, e);
log.info("resultException = {}", resultException);
//SQL 문법이 잘못되었을 경우에는 BadSqlGrammarException으로 변환된 것을 알 수 있음