4장 예외
- Created
- 2022년 9월 3일 PM 8:18
잘못된 예외처리 습관의 위험성#
- 예외가 발생했는데 catch 블록을 써서 잡아내는 것까지는 좋으나 이후 아무 조치도 취하지 않고 넘어간다면 정말 위험한 일이다.
- 원치 않는 예외가 발생하는 것보다도 훨씬 더 나쁜 일이다.
- 프로그램 실행 중 어디선가 오류가 있어서 예외가 발생했는데 그것을 무시하고 계속 진행한다는 걸 의미하기 때문이다.
- 결국 발생한 예외로 인해 어떤 기능이 비정상적으로 동작하거나, 메모리나 리소스가 소진되어 예상치 못한 다른 문제를 일으킬 것이다.
- 최종적으로 오작동을 하거나 시스템 오류가 나서 운영자가 알아차렸을 때는 이미 조치를 취하기엔 너무 늦었다.
초난감 예외처리#
java
try {
...
} catch (SQLException e) {
// 예외를 잡고는 아무것도 하지않는다.
}java
try {
...
} catch (SQLException e) {
System.out.println(e);
}java
try {
...
} catch (SQLException e) {
e.printStackTrace();
}초난감 예외처리 코드의 문제점
- 1번 코드의 경우 문제가 있음에도 무시하고 진행한다.
- 2, 3번 코드의 경우 누군가 콘솔로그를 지속적으로 확인하지 않는다면 에러를 잡아내기 어렵다.
⇒ 최종적으로 오작동을 일으킬 확률이 높다 = 시한폭탄과 같은 코드다.
예외를 처리할 때는 반드시 지켜야할 핵심 원칙은 한가지로, 모든 예외는 적절하게 복구되든지 아니면 작업을 중단시키고 운영자/개발자에게 통보되어야 한다.
무의미/무책임한 throws#
java
public void method1() throws Exception {
method2();
...
}
public void method2() throws Exception {
method3();
...
}
public void method3() throws Exception {
...
}- EJB가 한창 쓰이던 시절에 흔히 볼 수 있던 코드다.
- 매번 정확하게 예외 이름을 적어서 선언하기도 귀찮으니 아예 throws Exception 이라는, 모든 예외를 무조건 던져버리는 선언을 모든 메소드에 기계적으로 넣는것
- 예외 블랙홀 보다는 조금 낫지만, 메소드 선언에 의미있는 정보를 얻을 수 없다는 단점이 있다.
- 결과적으로 적절한 처리를 통해 복구될 수 있는 예외상황도 제대로 다룰 수 있는 기회를 박탈당하는 것이다.
예외 처리의 종류와 특징#

Error#
첫째는 java.lang.Error 클래스의 서브클래스들이다. 에러는 시스템에 뭔가 비정상적인 상황이 발생했을 경우에 사용된다. 그래서 주로 자바 VM에서 발생시키는 것이고 애플리케이션 코드에서 잡으려고 하면 안된다. 시스템 레벨에서 특별한 작업을 하는 게 아니라면 애플리케이션에서는 이런 에러에 대한 처리는 신경 쓰지 않아도 된다.
Checked, Unchecked#
java.lang.RuntimeException 클래스를 상속한 예외들은 명시적인 예외처리를 강제하지 않기 때문에 언체크 예외라고 불린다. 또는 대표 클래스 이름을 따서 런타임 예외라고도 한다.
에러와 마찬가지로 이 런타임 예외는 catch 문으로 잡거나 throws로 선언하지 않아도 된다. 물론 명시적으로 잡거나 throw로 선언해줘도 상관없다.

예외처리 방법#
복구#
예외상황을 파악하고 문제를 해결해서 정상 상태로 되돌려 놓는 것
회피#
예외처리를 자신이 담당하지 않고 자신을 호출한 쪽으로 던져버리는 것
- throw문을 이용하거나, catch문으로 일단 예외를 잡은 후 로그를 남기고 다시 예외를 던진다.
- 예외를 회피하는 것은 예외를 복구하는 것처럼 의도가 분명해야 한다.
전환#
예외 회피와 비슷하게 예외를 복구해서 정상적인 상태로는 만들 수 없기 때문에 예외를 메소드 밖으로 던지는 것
- 하지만 예외 회피와 달리, 발생한 예외를 그대로 넘기는 게 아니라 적절한 예외로 전환해서 던진다는 특징
- 예외 전환이 사용되는 목적
- 내부에서 발생한 예외를 그대로 던지는 것이 그 예외상황에 대한 적절한 의미를 부여해주지 못하는 경우에, 의미를 분명하게 해줄 수 있는 예외로 바꿔주기 위해서
- 예외를 처리하기 쉽고 단순하게 만들기 위해 포장하는 것
⇒ 주로 예외처리를 강제하는 체크 예외를 언체크 예외인 런타임 예외로 바꾸는 경우에 사용
예외처리 전략#
런타임 예외의 보편화#
- 일반적으로
- 체크 예외 = 복구 가능성이 조금이라도 있는 예외적 상황, 예외처리 강제 X
- 언체크 예외 = 시스템장애/프로그램오류에 사용, 예외처리 강제 O
- 독립형 어플리케이션과 다르게 서버는 특정 계층에서 예외가 발생했을때 작업을 일시중지하고, 사용자와 커뮤니케이션해 바로 예외를 처리할 수 있는 방법이 없다.
- 자바의 환경이 서버로 이동하면서 체크 예외의 활용도와 가치는 점점 떨어지고 있다.
- 자칫하면 throws Exception으로 점철된 아무런 의미도 없는 메소드들을 낳을 뿐이다.
- 때문에 대응이 불가능한 체크 예외라면 빨리 런타임 예외로 전환해서 던지는게 낫다.
- 자바 초기부터 있었던 JDK의 API와 달리 최근에 등장하는 표준 스펙 또는 오픈소스 프레임워크에서는 API가 발생시키는 예외를 체크 예외 대신 언체크 예외로 정의하는 것이 일반화되고 있다.
- 언체크 예외라도 필요하다면 얼마든지 catch 블록으로 잡아서 복구하거나 처리할 수 있다.
- 하지만 대개는 복구 불가능한 상황이고 보나마나 RuntimeException 등으로 포장해서 던져야 할 테니 아예 API 차원에서 런타임 예외를 던지도록 한 것이다.
- 런타임 예외를 사용하는 경우 API문서나 레퍼런스 문서 등을 통해, 메소드를 사용할 때 발생할 수 있는 예외의 종류와 원인/활용방법을 자세히 설명해두자
어플리케이션 예외#
- 런타임 예외 중심의 전략은 굳이 이름을 붙이자면 낙관적인 예외처리 기법이라고 할 수 있다.
- 일단 복구할 수 있는 예외는 없다고 가정하고 예외가 생겨도 어차피 런타임 예외이므로 시스템 레벨에서 알아서 처리해 줄 것이고, 꼭 필요한 경우는 런타임 예외라도 잡아서 복구하거나 대응해줄 수 있으니 문제 될 것이 없다는 낙관적인 태도를 기반으로 하고 있다.
- 반면 시스템 또는 외부의 예외상황이 원인이 아니라 어플리케이션 자체의 로직에 의해 의도적으로 발생시키고, 반드시 catch해서 무엇인가 조치를 취하도록 요구하는 예외도 있다.
- 정상적인 처리를 했을 경우와 애플리케이션 자체의 로직에 의해 의도적으로 예외를 발생시키고자 하는 경우에 각각 다른 종류의 리턴 값을 돌려주는 것
- 예외 상황에 대한 리턴 값을 명확하게 코드화하고 잘 관리하지 않으면 혼동의 가능성
- 결과 값을 확인하는 조건문이 자주 등장
- 정상적인 흐름을 따르는 코드는 그대로 두고, 애플리케이션 자체의 로직에 의해 의도적으로 예외를 발생시키고자 하는 경우에는 비즈니스적인 의미를 띤 예외를 던지도록 만드는 것
- 이때 사용하는 예외는 의도적으로 체크 예외로 만든다.