Java 체크 예외(checked)와 언체크 예외(unchecked) — 올바른 예외 처리 선택 가이드
검증 환경
본 절은 원문 사례·문서에 등장한 버전/도구를 정리한 것이다. 별도 실험실 재현이 명시되지 않은 항목은 일반화하지 않는다.
- 본문에 명시된 오류 메시지·프레임워크 버전을 기준으로 원인을 추적했다.
- 문서 정리일: 2026-09-03
- OS/CI 세부 값은 프로젝트마다 다르므로, 적용 전 로컬에서 동일 오류 메시지를 재확인한다.
1. 문제 정의
Java에서 예외 처리 코드를 처음 작성하면 아래와 같은 혼란이 생깁니다.
| |
위 코드의 NumberFormatException은 체크 예외일까요? 그리고 아래처럼 파일을 열 때 FileNotFoundException을 잡는 코드는 무엇을 해야 할까요?
| |
같은 “예외"라는 이름이지만 체크 예외와 언체크 예외는 컴파일러의 강제 여부와 의도가 다릅니다. 이 차이를 이해하지 못하면 예외를 삼키거나, 전혀 엉뚱한 레이어에서 잡거나, 더 나쁘게는 예외 때문에 프로그램을 종료해버리는 실수를 하게 됩니다.
2. 먼저 확인할 것 — 체크/언체크 예외 판별 기준
예외를 잡기 전에 먼저 내가 마주한 예외가 어느 종류인지 판별해야 합니다. 판별 기준은 RuntimeException을 상속했는가 한 가지입니다.
| |
판별은 소스에서 상속 관계를 확인하면 됩니다.
| 예외 | 직접 상위 타입 | RuntimeException 상속? | 컴파일러 강제(catch/throws) | 종류 |
|---|---|---|---|---|
NumberFormatException | IllegalArgumentException | ✔ 예 | 아니오 | 언체크 예외 |
FileNotFoundException | IOException | ✘ 아니오 | 예 | 체크 예외 |
IOException | Exception | ✘ 아니오 | 예 | 체크 예외 |
NullPointerException | RuntimeException | ✔ 예 | 아니오 | 언체크 예외 |
ArithmeticException | RuntimeException | ✔ 예 | 아니오 | 언체크 예외 |
증상 fingerprint
- 에러 메시지:
compiler error: unreported exception <Type>; must be caught or declared to be thrown(체크 예외에서 발생)- 발생 단계: 컴파일 타임 (checked) / 런타임 (unchecked)
- 관련 도구: javac, IDE (IntelliJ/Eclipse)
- 흔한 오해: “
try-catch로 잡으면 전부 체크 예외다” → 이는 오류입니다. catch는 체크 예외도 언체크 예외도 모두 할 수 있으며, 체크/언체크는 컴파일러가 처리 강제 여부로 구분합니다.NumberFormatException도 코드에 따라 catch할 수 있지만 그것은 “잡곘다"는 개발자의 선택일 뿐입니다.
3. 원인별 분기표 — “내 상황이면 무엇을 해야 하나?”
예외 처리 선택은 코드가 어느 레이어에 있는지와 복구 가능성에 따라 나뉩니다.
| |
| 상황 | 예외 종류 | 처리 위치 | 해결 방향 |
|---|---|---|---|
사용자가 숫자를 잘못 입력, Long.parseLong 실패 | NumberFormatException(언체크) | UI 레이어 | catch 후 사용자에게 경고 메시지 표시 |
| 파일이 없는 경로로 파일 열기 | FileNotFoundException(체크) | UI 레이어 | catch 후 “파일 경로를 다시 입력” 요청 |
| 파일 열기 실패 | FileNotFoundException(체크) | 서비스 레이어 | catch하지 않고 그대로 위로 전파 |
parseLong 뒤 로직 파라미터를 잘못 넘김 | 언체크(프로그래밍 오류) | 어디든 | 버그이므로 catch 하지 말고 수정 |
핵심 분기 기준
- 이 예외가 “복구 가능한 상황”(recoverable condition)을 나타내는가? → 예라면 체크 예외를 사용하고, 그 상황을 해결하는 게 맞습니다.
- 이 예외가 “프로그래밍 실수”(programming error)를 나타내는가? → 이렇다면 RuntimeException(언체크)입니다. Joshua Bloch의 《Effective Java》(2판 Item 58)의 권장과 동일합니다.
- 내 코드가 어느 레이어인가? → UI 레이어면 catch해서 경고, 서비스 레이어면 catch 하지 말고 전파(bubble), 그리고 절대로 삼키지 않습니다.
4. 상황별 해결책 — 코드로 구현
상황 A) UI 레이어에서 복구 가능한 언체크 예외 잡기
사용자 입력이 잘못된 숫자여서 발생한 NumberFormatException은 복구 가능합니다. UI 레이어에서는 잡아서 경고를 보여주면 됩니다.
| |
상황 B) UI 레이어에서 체크 예외 잡고 재요청
파일을 찾을 수 없을 때, UI에서 사용자에게 다시 경로를 요청하는 것이 자연스럽다면 이렇게 처리합니다.
| |
상황 C) 서비스 레이어에서 catch 하지 말고 전파(bubble) 하기
서비스 로직에서 파일이 없는 경우, 그 코드에서는 사용자에게 뭘 보여줄지 결정할 수 없습니다. 이때는 관련 정보를 손실하지 않고 그대로 위로 전파합니다. 예외를 삼키지 않으면서 상위 레이어(UI)에 결정을 넘기는 것이 블로그 접근입니다.
| |
FileNotFoundException이 checked이므로 throws 선언은 필수입니다. 이것은 불필요한 강제가 아니라 “이 메서드를 호출하는 쪽이 파일 없음 상황을 알아야 한다"라는 계약입니다.
예외를 못 할 때 취할 수 있는 옵션 3가지
대부분의 경우 예외가 발생하면 아래 중 하나를 선택합니다.
- 로그를 남기고(log it) 정상 경로로 return
- 메서드에
throws를 선언해 재throw(rethrow) - 현재 예외를 생성자에 넘겨 새 예외로 감싸기
| |
잘못된 해결책 (wrong fix)
System.exit(0)으로 프로그램을 종료시키기, 예외를 잡고 아무것도 하지 않고 빈 catch를 남기기(swallowing)는 모두 피해야 합니다. 예외를 삼키면 상위 레이어는 실패 원인을 전혀 알 수 없어 디버깅이 불가능해지고,System.exit은 전체 애플리케이션(서버 포함) 실행환경을 종료시켜 버립니다. 원하는 것은 “파일 하나 못 열었을 뿐"인데도 프로그램이 죽는 결과를 만듭니다.
5. 검증 명령
아래 코드가 예상대로 통과하면 체크/언체크 구분이 올바르게 이해된 것입니다.
| |
컴파일을 통과하기 위해 아래 구조 마저 만들어 둡니다.
| |
| |
체크 예외 throws를 선언에서 제거하면 javac가 오류를 내므로, 자신의 판단이 맞는지 바로 확인할 수 있습니다.
6. 예방 조치 — 재발 방지를 위한 습관
- 예외를 삼키지 않는다 — 빈
catch블록은 절대 두지 않습니다. - 레이어별 책임을 정한다 — UI 레이어는 사용자에게 경고, 서비스 레이어는 전파(bubble)만 담당.
- 복구 가능한 곳에서만 잡는다 — 복구 UI가 있는 곳(주로 UI 레이어)에서만 catch하고, 그 외에는 버블링.
- effective Java 원칙 기억하기 — “checked exception은 복구 가능한 상황, RuntimeException은 프로그래밍 실수”.
- catch 순서 주의 — 체크 예외와 언체크 예외를 함께 잡을 때는 구체 예외(하위)를 먼저, 일반 예외(상위)를 나중에 둡니다.
DevTrace verdict 복구 가능한 상황은 UI에서 catch 경고하고, 프로그래밍 오류는 언체크로 두되 절대 삼키지 말고 확실히 결정된 레이어에서 처리한다는 점이다.
출처
- Stack Overflow: Understanding checked vs unchecked exceptions in Java
- Joshua Bloch, 《Effective Java》 2nd Edition, Item 58 — Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
DevTrace 결론
핵심은 체크 예외는 복구 가능한 상황에서, 언체크 예외는 프로그래밍 실수에 사용하고, 어느 레이어에서 잡느냐에 따라 처리 방식을 달리 한다는 점이다.
원문 출처는 문제 발견의 단서이며, 위 판단과 점검 항목은 DevTrace의 독자 분석이다.