예외처리 전략

dev-jjun·2024년 1월 18일
0

Server

목록 보기
28/33

오류(Error)와 예외(Exception)의 차이

오류(Error)

시스템이 종료되어야 할 수준의 상황과 같이 수습할 수 없는 심각한 문제

이미 복구가 불가능한 상태의 예외 - 메모리 부족, 시스템 오류 등

→ 이는 개발자가 잡을 수 없음 ex. StackOverflowError, OutOfMemoryError

애플리케이션 로직은 Throwable 예외도 잡아서는 안되며, Exception부터 잡는 게 적합하다.

예외(Exception)

#체크_예외

특정 부적절한 상황에 던져지는/던질 수 있는 예외

애플리케이션 로직에서 사용할 수 있는 실질적인 최상위 예외

→ 모두 컴파일러가 체크하는 체크 예외 (RuntimeException 제외) ex. NullPointerException, IllegalArgumentException

RuntimeException

#언체크예외, 런타임예외

컴파일러가 체크하지 않는 언체크 예외로, 이를 포함한 하위 예외는 모두 런타임 예외에 해당한다.

*Exception과 Error 모두에 상속관계를 가지는 Throwable 클래스는 getMessage(), printStackTrace()라는 공통 메서드를 가지며, 이를 통해 오류나 예외의 메시지를 담는다.

[결론] Error와 Exception의 발생 상황은 명확히 구분되어야 한다!

Checked Exception VS Unchecked Exception의 처리

예외 = 폭탄 돌리기

🌟 이것만은 지키자! [예외 기본 규칙]
  1. 예외는 잡아서 처리하거나 던져야 한다.
  2. 예외를 잡거나 던질 때 지정한 예외 뿐만 아니라 그 예외의 자식들도 함께 처리된다.
    - catch : 그 하위 예외들까지 모두 잡기
    - throws : 그 하위 예외들까지 모두 던지기

던진 예외들을 처리하지 않으면?

예외에 의해 시스템 오류가 발생해서는 안 되므로, 이를 WAS가 잡아서 개발자가 설정한 서버 오류 페이지를 보여주는 식으로 처리한다. 즉, 이때는 서버가 죽지 않으므로 예외 상황들을 던지는 게 훨씬 안전!

Checked ExceptionUnchecked Exception
처리 방법체크 예외는 잡아서 처리하거나, 또는 밖으로 던지도록 선언해야 한다. 그렇지 않으면 컴파일 오류가 발생한다.⇒ 무조건 잡아서 던지거나 처리하기컴파일러가 예외를 처리하지 않는다. *예외를 던지는 throws를 선언하지 않고, 생략할 수 있다. 이 경우 자동으로 예외를 던진다.
장점*가장 좋은 오류는 컴파일 오류! 개발자가 실수로 예외를 누락하지 않도록 컴파일러를 통해 문제를 잡을 수 있다신경쓰고 싶지 않은 언체크 예외 모두 무시 가능!
단점① 실제로는 개발자가 모든 체크 예외를 반드시 잡거나 던지도록 처리(throws)해야 하기 때문에 너무 번거로워진다. ② 의존관계에 따른 단점 존재 (여러 계층에 걸친 throws) ③ 복구 불가능한 예외 (DB, 외부 네트워크에서 발생한 예외는 따로 처리할 방법이 없다)개발자가 실수로 예외를 누락할 수 있다

🌟 체크 예외 VS 언체크 예외 차이점

“예외를 처리할 수 없을 때 예외를 밖으로 던지는 부분(throws 예외)”을 필수로 선언해야 하는가 생략할 수 있는가!

체크/언체크 예외 활용

체크/언체크 예외는 언제 사용할까?

*기본적으로 언체크(런타임) 예외가 더 좋다!

  1. 기본적으로 언체크(런타임) 예외를 사용하자

    @Transactional 로 선언적 트랜잭션 처리를 할 때, 체크 예외는 롤백 되지 않고, 언체크 예외는 롤백이 된다

  2. 체크 예외는 비즈니스 로직상 의도적으로 던지는 예외에만 사용하자

    [예시] 결제 시 포인트 부족, 계좌 이체 실패, 로그인 ID/PW 불일치 등

    → 반드시 해당 예외를 잡아서 처리해야 하는 문제일 때만 사용! (개발자가 실수로 놓칠 수도 있는 부분)

예외처리 전략

1. 예외 복구

예외 상황을 파악하고 문제를 해결해서 정상 상태로 돌려놓는 것

→ 예외로 어떤 작업의 처리가 불가능하다면 다르게 작업을 처리하도록 유도

2. 예외 처리 회피

예외를 복구할 수 없는 경우, 예외 처리를 직접 처리하지 않고, 자신을 호출한 곳으로 던져버리는 것

→ 다른 메서드로 책임 전가

3. 예외 전환

예외를 복구할 수 없는 경우, 적절한 예외로 변환하여 던지는 것

→ 런타임 예외로 포장하는 것이 이에 해당 ☑️

  • 의미 있고 추상화된 예외로 전환
  • 불필요한 처리를 줄여줌

⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️⬇️

예외 포함과 스택 트레이스

예외를 전환할 때는 기존 예외를 반드시 포함해야 한다!

실무에서는 항상 로그를 사용하는 것이 좋다. 로그를 통해 잡힌 예외를 확인하고 원인 분석을 통한 개선이 가능하기 떄문이다.

이때, 스택 트레이스를 출력할 수 있는데 e.printStackTrace() 를 사용하거나 log.info(”ex”, e)와 같이 로그를 찍어주면, 예외가 로그에 출력된다

→ Cause By - Cause By 로 어떤 예외를 원천을 발생했는지 꼬리를 무는 형식으로 원인을 찾을 수 있다.

[결론] Checked Exception은 Unchecked인 RuntimeException으로 대신 던지도록 하자!

자바에는 체크 예외가 더 많고, 이들 중 복구할 수 없는 예외가 정말 많다. throws 를 덕지덕지 붙이거나, 아예 Exception을 던져버리는 선택도 하는데 이는 절대 권장하지 않는 방법이다.

이를 해결할 수 있는 방법은 런타임 예외로 대신 던지도록 하는 것이다.

→ 이들을 문서화하거나 코드에 throws 런타임 예외로 명시해줌으로써 개발자가 놓치지 않도록 한다.

참고 자료

[Java] 체크 예외(Check Exception)와 언체크 예외/런타임 예외 (Uncheck Exception, Runtime Exception)의 차이와 올바른 예외 처리 방법

Inflearn - 김영한 ‘스프링 DB 1편 - 데이터 접근 핵심 원리’ 강의

profile
서버 개발자를 꿈꾸며 성장하는 쭌입니다 😽

0개의 댓글