Notice
Recent Posts
Recent Comments
Link
관리 메뉴

look-forest

자바 예외 이해 본문

Spring/Spring 데이터 접근 - 핵심 원리

자바 예외 이해

studyHub 2024. 8. 15. 19:26

 

요약

1. 체크 예외 단점 : (복구할 수 없음에도 강제하므로) 번거로움, 의존 관계 이슈 

2. 기본적으로 언체크 예외 사용할 것. 

3. 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용 (=> Exception으로 퉁쳐서 사용하면 의미가 사라지니 사용X)


예외 기본

예외에 대해서는 2가지 기본 규칙을 기억하자.

  1. 예외는 잡아서 처리하거나 던져야 한다.
  2. 예외를 잡거나 던질 때 지정한 예외뿐만 아니라 그 예외의 자식들도 함께 처리된다.

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

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

체크 예외 VS 언체크 예외

  • 체크 예외: 예외 처리 강제. 항상 throws 에 던지는 예외를 선언해야 한다. (컴파일러가 체크해준다)
    • 예외를 누락하지 않도록 컴파일러를 통해 문제를 잡아주는 훌륭한 안전 장치
    • 모든 체크 예외를 반드시 잡거나 던지도록 처리해야 하기 때문에, 너무 번거로운 일이 된다.
    • 의존관계에 따른 단점
  • 언체크 예외: 예외를 잡아서 처리하지 않아도 throws 를 생략할 수 있다.
    • 신경쓰고 싶지 않은, 처리할 수 없는 언체크 예외를 무시할 수 있다.
    • 언체크 예외는 개발자가 실수로 예외를 누락할 수 있다

 

체크 예외 활용

그렇다면 언제 체크 예외를 사용하고 언제 언체크(런타임) 예외를 사용하면 좋을까?

 

기본 원칙은 다음 2가지를 기억하자.

  1. 기본적으로 언체크(런타임) 예외를 사용하자.
  2. 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자.
    해당 예외를 잡아서 반드시 처리해야 하는 문제일 때만 체크 예외를 사용해야 한다 ( 결제시 포인트 부족 예외 등)

체크 예외의 문제점

1. 복구 불가능한 예외

처리할 수 있는 체크 예외라면 서비스나 컨트롤러에서 처리하겠지만, 데이터베이스나 네트워크 통신처럼 시스템 레벨에서 올라온 예외들은 대부분 복구가 불가능하다. 그리고 실무에서 발생하는 대부분의 예외들은 이런 시스템 예외들이다.

이런 문제들은 일관성 있게 공통으로 처리해야 한다. 오류 로그를 남기고 개발자가 빠르게 인지하는 것이 필요하다.
서블릿 필터, 스프링 인터셉터, 스프링의 ControllerAdvice를 사용하면 이런 부분을 깔끔하게 공통으로 해결할 수 있다.

 

2. 의존 관계에 대한 문제

문제는 이런 경우에 체크 예외를 사용하면 아래에서 올라온 복구 불가능한 예외를 서비스, 컨트롤러 같은 각각의 클래스가 모두 알고 있어야 한다. 그래서 불필요한 의존관계 문제가 발생하게 된다.

JDBC에서 JPA 같은 기술로 변경하면 예외도 함께 변경해야한다. 그리고 해당 예외를 던지는 모든 부분을 변경해야 한다.

 

throws Exception을 사용한다면?

체크 예외의 최상위 타입인 Exception 을 던지게 되면 다른 체크 예외를 체크할 수 있는 기능이 무효화되고, 중요한 체크 예외를 다 놓치게 된다. 이렇게 하면 모든 예외를 다 던지기 때문에 체크 예외를 의도한 대로 사용하는 것이 아니다. 따라서 꼭 필요한 경우가 아니면 이렇게 Exception 자체를 밖으로 던지는 것은 좋지 않은 방법이다.

 

=> 언체크 예외를 활용하자.

런타임 예외를 사용하면 서비스나 컨트롤러가 이런 복구 불가능한 예외를 신경쓰지 않아도 된다.
물론 이렇게 복구 불가능한 예외는 일관성 있게 공통으로 처리해야 한다

SQLException 을 런타임 예외인 RuntimeSQLException 으로 변환

 

런타임 예외는 해당 객체가 처리할 수 없는 예외는 무시하면 된다. throws 생략으로 불필요한 의존성 제거

  • 런타임 예외를 사용하면 중간에 기술이 변경되어도 해당 예외를 사용하지 않는 컨트롤러, 서비스에서 코드 변경 X
  • 구현 기술이 변경되는 경우, 예외를 공통으로 처리하는 곳에서는 예외에 따른 다른 처리가 필요할 수 있다.
    하지만 공통 처리하는 한곳만 변경하면 되기 때문에 변경의 영향 범위는 최소화된다.

 

체크 예외의 이런 문제점 때문에 최근 라이브러리들은 대부분 런타임 예외를 기본으로 제공한다.

런타임 예외는 놓칠 수 있기 때문에 문서화가 중요하다.

 

예외 포함과 스택 트레이스

예외를 전환할 때는 꼭! 기존 예외를 포함해야 한다. 그렇지 않으면 스택 트레이스를 확인할 때 진짜 원인을 알 수 없다.

 


참고 자료 & 이미지 출처
스프링 DB 1편 - 데이터 접근 핵심 원리 (김영한 님)