

직접 그린 구조라 틀린 부분이 있다면 피드백 환영합니다.
먼저 Java의 예외는 크게 Error와 Exception으로 나뉩니다.
Error는 JVM 수준에서 발생하는 비정상적인 상황입니다.
예를 들어 OutOfMemoryError처럼 힙 메모리가 고갈된 경우, 이미 시스템은 정상적으로 동작할 수 없는 상태에 놓이게 됩니다.
따라서 이를 try-catch로 처리하더라도 문제를 복구할 수 없으며,
오히려 잘못된 상태로 계속 실행될 위험이 있습니다.
즉, Error는 애플리케이션 레벨에서 해결할 수 있는 대상이 아니기 때문에
빠르게 실패하도록 두는 것이 바람직합니다.
반면 Exception은 개발자가 구현한 로직에서 발생하는 예외입니다. 예측 가능하고 별도로 구현하여 처리할 수 있습니다.
Exception은 다시 두 가지로 나뉩니다.
여기서 기준이 되는 것이 바로 RuntimeException입니다.
Exception (RuntimeException 제외)
→ Checked Exception
RuntimeException 및 하위 클래스
→ Unchecked Exception
즉, 단순한 구조적 구분이 아니라
→ RuntimeException을 기준으로 예외의 동작 방식이 나뉘게 됩니다.
그렇다면 여기서 한 가지 의문이 생길 수 있습니다.
왜 굳이 RuntimeException을 따로 두었을까?
RuntimeException은 단순히 구조적으로 분리된 것이 아니라,
예외의 성격이 다르기 때문에 구분된 개념입니다.
Java는 예외를 다음과 같이 바라봅니다.
Checked Exception
→ 정상적인 상황에서도 발생할 수 있는 실패
→ 반드시 대비해야 하는 예외
RuntimeException
→ 잘못된 코드나 로직에서 발생하는 문제
→ 처리하기보다 수정해야 하는 대상
만약 모든 예외를 Checked Exception으로 강제했다면,
간단한 코드에서도 불필요한 try-catch가 반복되며
코드가 과도하게 복잡해졌을 것입니다.
그래서 Java는 RuntimeException을 통해
이 예외는 굳이 강제하지 않고 개발자에게 맡기겠다.
라는 선택을 한 것입니다.
Spring/Java로 개발하다 보면 Checked Exception을 직접 다루는 경우가 많지 않습니다.
그렇다고 해서 사용되지 않는 것이 아니라 프레임워크가 대신 처리하고 있는 경우가 대부분입니다.
Checked Exception의 목적은 다음과 같습니다.
이 상황은 반드시 대비해야 한다는 것을 컴파일 단계에서 강제하기 위함
예를 들어 파일을 읽는 경우 다음과 같은 상황이 발생할 수 있습니다.
이러한 상황은 코드의 버그가 아니라 정상적인 실행 과정에서도 발생할 수 있는 실패입니다.
그래서 Java는 이를 Checked Exception으로 정의하고 처리를 강제합니다.
하지만 실제 개발에서는 이러한 강제가 잘 체감되지 않습니다.
그 이유는 프레임워크가 Checked Exception을 내부에서 처리하기 때문입니다.
예를 들어 Spring에서는 JDBC의 SQLException과 같은 Checked Exception을 내부에서 처리한 뒤
RuntimeException으로 변환하여 다시 던집니다.
이로 인해 개발자는 Checked Exception을 직접 다루지 않아도 되며
복잡한 try-catch나 throws 전파 없이 비즈니스 로직에 집중할 수 있습니다.
처음에는 Checked Exception과 Unchecked Exception의 차이가 잘 와닿지 않았습니다.
특히 Spring으로 개발하면서 Checked Exception을 직접 다룬 경험이 거의 없었기 때문입니다.
그래서 단순히
Checked는 외부, Unchecked는 내부 정도로만 막연하게 이해하고 있었던 것 같습니다.
하지만 이번에 정리하면서 알게 된 것은 이 두 개념의 큰 차이는
컴파일러가 예외 처리를 강제하느냐의 여부
라는 점이었습니다.
또한 RuntimeException이 따로 존재하는 이유도 이해할 수 있었습니다.
모든 예외를 강제로 처리하게 만들면 코드가 지나치게 복잡해지기 때문에
Java는 일부 예외에 대해서는 개발자에게 책임을 맡기는 방향을 선택한 것입니다.
그리고 Spring을 사용하면서 Checked Exception을 거의 보지 못했던 이유 역시
프레임워크가 내부에서 이를 처리하고 RuntimeException으로 변환해주기 때문이었습니다.
원래는 예외를 단순히 발생할 것 같은 비즈니스 로직에 추가하는 것 정도로만 생각했지만
하지만 이번에 예외 구조를 깊게 정리해보면서 단순한 처리 이상의 의미가 있다는 것을 알게 되었습니다.