예외 처리

김태훈·2023년 12월 21일
0

Java

목록 보기
1/3

구조

출처 : https://velog.io/@jeong-god/Java%EC%9D%98-%EC%A0%95%EC%84%9D-8%EC%9E%A5-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC-Exception-Handling

Throwable

모든 에러, 예외 클래스의 부모 클래스

Error

절대 발생해서는 안되는 && 비정상적 && 심각한 문제.
보통 개발자가 처리할 수 없는 문제(시스템 자체의 문제)가 발생했을 때 사용된다.

ex. StackOverflowError, OutOfMemoryError

Exception

일반적인 개발 과정에서 충분히 발생할 수 있는 문제
Checked Exception과 Unchecked Exception(RuntimeException)으로 나뉘어진다.

공식문서에서 reasonable한 경우라고 적혀 있는데 일반적인 개발 과정이라고 의역했습니다. 혹시 잘못된 설명이라면 지적 부탁드립니다.

Checked Exception

Exception의 자식 중 RuntimeException을 제외한 모든 예외를 CheckedException이라고 한다. 어디에선가 해당 예외를 catch해서 처리하는 걸 강제한다. 만약 처리하지 않으면 컴파일 오류가 발생한다.
예외가 발생한 스코프에서 해당 예외를 catch하지 않는다면 throws를 명시해서 부모 스코프로 던져줘야 한다.

  • CheckedException이 발생했다면 해당 로직 내부에서 해결하던가 상위 로직으로 올려보내야 한다.

Unchecked Exception (== RuntimeException)

컴파일 과정에서 검사할 수 없는 예외이다. 컴파일 과정에서 오류를 발생시키지 않기 때문에 명시적으로 throw를 하지 않아도 된다. 런타임 중 해당 예외 발생 시, catch해주는 로직이 나타날 때까지 부모 스코프를 타고 올라간다.

Checked Exception과 Unchecked Exception은 언제 써야 할까?

예외 발생 시 해당 문제를 복구할 수 있다면 Checked Exception이 권장된다.
그게 아니라면 Unchecked Exception이 권장된다.

해당 개념은 예시를 통해 보면 쉽게 이해할 수 있다고 생각한다.

Checked Exception 예시

  • IOException : IOException이 발생하면 우리는 다시 한 번 IO 요청을 보낸다던가, 또다른 IO로 요청을 보낸다던가 하는 방식으로 문제를 복구할 수 있다. 이럴 때는 Checked 방식이 더 효율적이다. 사용자에게 해당 오류를 처리할 것을 강제하기 때문이다.

RuntimeException 예시

  • NullPointerException
  • IndexOutOfRangeException

보통 RuntimeException은 개발자가 로직을 잘못 작성했거나, 입력값이 잘못되었거나 하는 상황에서 발생된다. 이런 에러가 발생했을 때, 예외문에서 복구를 위해 무얼 할 수 있겠는가.. 그냥 이런 문제가 발생했다는 정보만 전달하면 된다. 추가적인 처리를 할 필요가 없다면 굳이 해당 예외를 명시할 이유가 없다.

catch

예약어 try와 함께 쓰이며, 예외를 처리하기 위해 사용한다.

try {
    ...
} catch(throwable1) {
    ...
} catch(throwable2) {
    ...
}

try 블록 내부에서 명시해둔 throwable가 발생하면, 해당 catch 블록으로 코드의 흐름이 이동한다.

catch 시에는 순서를 신경써라.

catch는 선언되어 있는 순서대로 발생한다. 만약 부모 예외를 먼저 catch하고 이후에 자식 예외를 catch한다면, 자식 예외는 영영 catch가 불가능하다.
위에 선언된 부모 예외를 catch하는 부분에서 모든 자식 예외를 처리하기 때문이다.

finally

try 블록에서 어떤 상황에서도 실행되야 하는 로직이 있다면, finally 블록에 작성된 내용들이 실행된다.

finally는 resource의 정리 목적으로만 사용하는 걸 권장한다고 한다.
만약 finally 블록에 비즈니스 로직을 넣게 되면 우리가 예측한대로 흘러가지 않을 수 있다. 예를 들어, finally 블록 내부에서 return문을 넣는 건 안티 패턴이다. try나 catch에서 리턴한 내용을 무시하고 finally에서 return한 내용을 반환하기 때문이다.

사족을 하나 붙이면, 예전에 인터셉터의 afterCompletion 메서드가 어떻게 동작하는지 궁금해서 코드를 살펴봤던 적이 있다.
그 때, DispatcherServlet의 try문 마지막에 afterCompletion 메서드가 들어가 있었고, catch 블록의 마지막에서도 afterCompletion를 호출하는 걸 확인할 수 있었다.
당시에 나는 afterCompletion는 정상 경우에도, 예외 경우에도 무조건 실행되는 메서드인데 왜 finally에 넣지 않은걸까 궁금했던 적이 있는데 이러한 이유인걸까 궁금해진다.
(확인해보고 싶으시면 DispatcherServlet 클래스에서 triggerAfterCompletion 를 검색해보세요)

출처

https://docs.oracle.com/en/java/javase/11/docs/api/index.html
https://wikidocs.net/229#_2
https://simgee.tistory.com/27

profile
작은 지식 모아모아

0개의 댓글