본문 바로가기

Java

자바 예외 이해 (Checked, Unchekced)

자바 예외 이해

예외 계층

 

  • Object : 자바의 모든 객체는 Obejct를상속받는다. 예외도 마찬가지이다.
  • Throwable : 최 상위 예외이다.
    • Error : 메모리 부족이나 심각한 시스템 오류 등 복구불가능한 예외이다.
    • Exception
  • Checked : RuntimeException을 제외한 모든 예외를 체크예외라 한다.
  • Unchekced : RuntimeException 하위의 모든 예외를 언체크예외라 한다.

예외 기본 규칙

예외는 폭탄돌리기와 같다. 예외를 처리하지 못하면 호출한 곳으로 예외를 던지고 예외를 처리할 수 있으면 처리하고 정상흐름으로 돌릴 수 있다.

예외를 처리하지 못하고 계속 던지면 어떻게 될까?

  • 자바의 main() 쓰레드의 경우 에러 로그를 출력하면서 시스템이 종료된다.
  • 웹 애플리케이션의 경우 예외가 발생한다고 시스템이 종료되면 안된다. WAS가 해당 예외를 받고 처리하는데, 주로 사용자에게 개발자가 지정한 오류 페이지를 보여준다.

체크 예외

  • Exception을 상속받으면 체크예외가 된다. 체크예외는 컴파일 타임에 예외를 처리해야함을 강제한다.
  • RuntimeException을 상속받으면 언체크 예외가 된다.

체크예외의 장단점

  • 안전하게 코드를 작성할 수 있다. 예외를 놓치지 않고 처리하도록 강제한다.
  • 하지만 모든 체크예외를 개발자가 다루는 것이 매우 번거롭다. 예외를 모두 챙겨야한다. 크게 신경쓰지 않아도 되는, 개발자가 잡아서 처리할 수 없는 예외들까지 다뤄야하는 문제가 발생한다.

언체크 예외

  • RUnitmeException 과 그 하위 예외는 언체크 예외로 분류한다.
  • 언체크 예외는 예외를 잡아서 처리하지 않아도 throws 를 해준다.

체크예외와 언체크예외의 차이

  • 이 둘의 차이는 예외를 처리할 때에는 크게 차이가 없다.
  • 그러나 예외를 처리하지 않고 던질 때 필수적으로 밖으로 던지는 코드를 선언하는가 선언하지 않는가의 차이가 있다.
    • 이 둘의 차이가 별거 아닌거 같아보이지만 이를 활용할 때에는 많은 차이를 만든다.

체크 예외 활용

기본 원칙

  • 기본적으로 언체크(런타임) 예외를 사용하자
  • 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자
    • 반드시 처리해야 하는 문제일 때만 체크 예외를 사용해야 한다.
    • ex)
      • 계좌 이체 실패 예외
      • 결제시 포인트 부족 예외
      • 로그인 ID, PW 불일치 예외
    • 이 경우에도 100% 체크 예외로 만들어야 하는 것은 아니다. 다만 매우 심각한 문제는 개발자가 실수로 예외를 놓치면 안된다고 판단할 수 있다.
    • 반드시 놓치면 안되는 예외는 체크예외로 만들 수 있다.

왜 위와같은 원칙을 적용해야할까? 체크예외로 개발자에게 안전한 코드를 강제할 수 있다면 좋은 게 아닐까?

두 가지 문제

  • (복구 불가능한 예외) 물론 안전한 코드를 강제해줘서 좋다. 하지만 구체적인 예외를 전파하는 과정에서 해당 예외에 의존하는 코드가 여러 레이어로 전파가된다.
  • (의존관계 문제) 우리가 처리할 수 없는 예외를 던지는 코드를 각 레이어에 전파함으로써 필요없는 의존성을 맺게되고 의존성이 변경되면 다같이 코드를 수정해야하는 문제를 야기한다.
    • 네트워크 예외나 DB 예외와 같은 체크예외들은 서비스나 컨트롤러에서도 해결할 수가 없다.
      • SQLExecption을 서비스, 컨트롤러에까지 throws 예외 처리를 해줘야한다.
      • SQLException은 JDBC에 의존하는 예외인데 만약 SQLException이 JPAException으로 변경된다면 모든 레이어의 코드를 모두 변경해줘야한다.
    • 주로 웹 애플리케이션에서는 이러한 Exception들을 ControllerAdvice에서 예외를 정해서 처리해준다.

해결방안

  • 만약 최상위 타입인 Exception을 던지는 방법으로 해결하려면 어떻게 될까?
    • 이 방법은 좋지 않다.
    • 모든 CheckedException이 밖으로 던져지는 예외가 발생하므로 체크 예외가 무의미해진다.

언체크 예외 활용

  • 기본적으로 체크예외는 체크 => 언체크로 변경하여 예외를 처리한다.
  • 이렇게 처리하면 컨트롤러, 서비스 레이어 등 다른 레이어로 예외의 의존관계를 전파하지 않을 수 있다.
    • 최근 라이브러리들은 대부분 런타임 예외를 기본으로 제공한다.
  • 런타임 예외는 놓칠 수 있기 때문에 문서화가 중요하다.

예외 포함과 스택 트레이스

  • 예외를 포함한 스택트레이스를 발생시키려면?
  • 예외 생성자의 (Throwable cause) 로 생성자를 만들어주면 실제 기존 예외를 포함하여 확인할 수 있게된다.
  • 예외를 전환할 때는 꼭! 기존 예외를 포함하여 만들어주자

Reference

  • 스프링 DB 1편 - 데이터 접근 핵심 원리 by 김영한