Search
〰️

Exception

예외계층

Object : 예외도 객체이다. 모든 객체의 최상위 부모는 Object 이므로 예외의 최상위 부모도 Object 이다.
Throwable : 최상위 예외이다. 하위에 ExceptionError 가 있다.
Error : 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구 불가능한 시스템 예외이다. 애플리케이션 개발자는 이 예외를 잡으려고 해서는 안된다.
상위 예외를 catch 로 잡으면 그 하위 예외까지 함께 잡는다. 따라서 애플리케이션 로직에서는 Throwable 예외도 잡으면 안되는데, 앞서 이야기한 Error 예외도 함께 잡을 수 있기 때문에다. 애플리케이션 로직은 이런 이유로 Exception 부터 필요한 예외로 생각하고 잡으면 된다. 참고로 Error 도 언체크 예외이다.
Exception : 체크 예외 (Checked Exception)
애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외이다.
Exception 과 그 하위 예외는 모두 컴파일러가 체크하는 체크 예외이다.
단 RuntimeException 은 예외로 한다.
RuntimeException : 언체크 예외, 런타임 예외 (Unchecked Exception)
컴파일러가 체크 하지 않는 언체크 예외이다.
RuntimeException 과 그 자식 예외는 모두 언체크 예외이다.
RuntimeException 의 이름을 따라서 RuntimeException 과 그 하위 언체크 예외를 런타임 예외라고 많이 부른다.
예외에 대해서는 2가지 기본 규칙을 기억하자!
1.
예외는 잡아서 처리하거나 던져야한다.
2.
예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다.

Checked Exception

체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야한다! 그렇지 않으면 컴파일오류가 발생한다.
처리: try~catch
던지기: throws
Exception을 상속받은 예외는 체크예외가 된다.

Uncheced Exception

RuntimeException과 그 하위 예외
언체크예외는 말 그대로 컴파일러가 체크하지 않는 예외이다.
언체크예외는 체크예외와 기본적으로 동일하다. 차이가 있다면 예외를 던지는 thows를 선언하지 않고 생략할 수 있다. 이 경우 자동으로 예외를 던진다.

체크 예외 활용

기본 원칙

기본적으로 Unchecked Exception (Runtime Exception)을 사용하자.
체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자.
해당 예외를 잡아서 반드시 처리해야하는 문제일 때
계좌 이체 실패 예외
결제시 포인트 부족 예외
로그인 ID/PW 불일치 에외
중요한 예외인 경우 체크예외로 만들어 두면 컴파일러를 통해 놓친 예외를 인지할 수있다!

체크 예외의 문제점

체크예외는 컴파일러가 예외 누락을 체크해주기 때문에 개발자가 실수로 예외를 놓치는 것을 막아준다.
명시적으로 예외를 잡아서 처리 || 예외를 던지도록 선언
기본적으로 체크예외를 사용하면 문제가 되는 이유?
1.
복구 불가능한 예외
대부분의 예외는 복구가 불가능하다.
SQLException 같은 경우 → 복구가 불가능 한 경우 → 서비스나 컨트롤러와 같은 비즈니스 레이어에서는 복구가 불가능하다. 이런 문제는 일관성있게 공통으로 처리해야한다. 오류 로그를 남기고 개발자가 오류를 빠르게 인지하도록.. (서블릿 필터/스프링인터셉터/스프링 ControllerAdvice)
2.
의존관계에 대한 문제
체크예외는 무조건 예외를 던지거나 처리해야하므로, 실제 예외처리가 불가능한 레이어에서도 예외처리를 해주어야하는 문제점 발생
Controller와 Service에서 SQLException의존하게 된다.
SQLException은 저수준 모듈 (구현체), JDBC 기술에 종속된 예외이다.
예를들어 JDBC에서 JPA로 기술을 변경하는 경우? SQLException에 의존하던 모든 서비스, 컨트롤러의 코드를 JPAException에 의존하도록 모두 변경해야한다.
client코드의 변경없이 구현체를 변경한다고 하더라도 CheckedException을 처리해야하는 파급효과가 발생한다!!!
CheckedException은 컴파일이 되지않기 때문에 변경범위가 팡팡
데이터베이스나 네트워크통신처럼 시스템 레벨에서 올라온 예외들은 복구가 불가능하고, 이런 경우에 체크예외를 사용하면 아래에서 올라온 복구 불가능한 예외를 서비스, 컨트롤러 같은 클래스가 모두 알고있어야 한다. → 불필요한 의존관계 문제 발생!

런타임 예외 구현 기술 변경시 파급효과

런타임 예외를 사용하면 중간에 구현체가 변경되어도 해당 예외를 사용하지 않는 컨트롤러, 서비스에서는 코드를 변경하지 않아도 된다.
예외를 공통으로 처리하는 곳에서만 예외를 변경해주면 되기 때문에 변경의 영향범위는 최소화된다!
대신! 런타임 예외는 문서화를 잘해야한다!
특히 중요한 예외인 경우 throws 런타임예외를 명시해서 예외를 인지할 수 있도록 한다!

예외 포함과 스택트레이스

예외를 전환할때는 꼭!!!! 기존 예외를 포함해야한다.
void printEx() { Controller controller = new Controller(); try { controller.request(); } catch (Exception e) { log.info("ex", e); } }
Java
복사
로그를 출력할 때, 마지막 파라미터에 예외를 넣어주면 로그에 스택트레이스를 출력할 수 있다.
public void call() { try { runSQL(); } catch (SQLException e) { throw new RuntimeSQLException(e); // 기존 예외 포함! } }
Java
복사
기존 예외를 포함 → cause 를 넘겨주면 기존 예외를 물고 갈 수 있다.
기존 예외를 포함하지 않은 경우? Caused By가 없다.. Root Cause를 알 수 없다!!!!
Transient: 일시적. 동일한 SQL을 반복해서 실행했을 때 성공할 가능성이 있다.
NonTransient: 동일한 SQL을 반복해서 실행하면 실패