일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 자바의 정석 기초편 ch1
- @Aspect
- 자바의 정석 기초편 ch5
- 자바의 정석 기초편 ch14
- 스프링 mvc2 - 타임리프
- 자바로 키오스크 만들기
- 자바 중급1편 - 날짜와 시간
- 자바의 정석 기초편 ch2
- 스프링 mvc1 - 스프링 mvc
- 스프링 mvc2 - 검증
- 자바 중급2편 - 컬렉션 프레임워크
- 스프링 mvc2 - 로그인 처리
- 2024 정보처리기사 시나공 필기
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch11
- 자바의 정석 기초편 ch4
- 자바 기초
- 스프링 트랜잭션
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch12
- 데이터 접근 기술
- 자바 고급2편 - 네트워크 프로그램
- 자바 고급2편 - io
- 자바의 정석 기초편 ch6
- 2024 정보처리기사 수제비 실기
- 자바로 계산기 만들기
- 자바의 정석 기초편 ch7
- 람다
- 자바의 정석 기초편 ch9
- 스프링 입문(무료)
- Today
- Total
개발공부기록
자바의 예외, RuntimeException, CheckedException, UncheckedException의 차이점, RuntimeException이 필수 처리 예외가 아닌 이유, 예외 처리 방안 본문
자바의 예외, RuntimeException, CheckedException, UncheckedException의 차이점, RuntimeException이 필수 처리 예외가 아닌 이유, 예외 처리 방안
소소한나구리 2025. 3. 24. 13:25자바의 예외
자바는 프로그램 실행 중에 발생할 수 있는 예상치 못한 상황을 처리하기 위한 메커니즘을 제공하는게 이것이 예외(예외 객체)이다.
예외처리는 프로그램의 안정성과 신뢰성을 높이는데 매우 중요한 역할을 한다.
예외가 발생하면 잡아서 처리하거나, 만약 처리할 수 없다면 밖으로 던질 수 밖에 없다.
잡아서 처리하고자 할 때는 try - catch - finally 구분을 사용하거나, Java 7에서 도입된 자동으로 외부 자원을 해제해주는 try - with - resources를 이용해서 처리할 수 있고 throws로 상위 계층으로 던질 수도 있다
계속 처리되지 않고 예외를 상위 계층으로 올라가면 결국 프로그램을 실행하는 main 메서드까지 예외가 올라가게 되고 여기서도 처리하지 못한다면 프로그램이 종료되면서 예외에 관한 정보가 출력된다.
(이 정보는 조절할 수 있고 커스텀도 가능하다)
프로그램 내부에서 'throw new 예외'와 같은 문법으로 원하는 예외를 발생시킬 수도 있다.
자바 예외 계층도

자바는 기본형을 제외한 모든 것이 객체이기 대문에 에외도 객체이다. 그렇기 때문에 예외 객체의 최상위 부모도 일반적인 클래스와 마찬가지로 Object를 상속받는다.
각 예외의 설명
Throwable
- 모든 예외의 최상위 예외이며 하위에 Exception과 Error가 있다
Error
- 메모리 부족이나 심각한 시스템 오류와 같이 애플리케이션에서 복구가 불가능한 시스템 예외이다
- Error관련된 예외가 발생하면 애플리케이션 레벨에서 해결할 수 있는 여지 자체가 없기 때문에 개발자가 프로그램 로직 내에서 이 예외를 잡아서 정상흐름로 돌려놓아서는 절대 안된다.
- 차라리 예외가 애플리케이션 밖으로 던져진 다음 사용자에게 안내 문구를 보여주는 것이 좋다
Exception - 체크 예외
- 애플리케이션 로직에서 사용할 수 있는(처리할 수 있는) 실질적인 최상위 예외이다
- Exception과 그 하위 예외는 모두 컴파일러가 체크하는 예외이지만 RuntimeException은 제외된다
RuntimeException - 언체크 예외, 런타임 예외
- 컴파일러가 체크 하지 않는 언체크 예외이며 RuntimeException과 그 자식 예외는 모두 언체크 예외이다
- RuntimeException의 이름을 따라서 RuntimeException과 그 하위 언체크 예외를 런타임 예외라고 많이 부른다
RuntimeException, CheckedException, UncheckedException의 차이점
RuntimeException이 UncheckedException이며 해당 예외들은 컴파일러가 체크해 주지 않기 때문에 IDE에서 예외를 잡아주지 않는다.
RuntimeException을 제외한 Exception과 그 하위의 예외가 모두 CheckedException으로 컴파일러가 예외를 체크를 해주기 때문에 개발자가 해당 예외를 무조건 처리하거나 밖으로 던지는 코드를 명시적으로 처리해주어야 한다.
체크 예외를 처리하지 않으면 컴파일 예외가 발생하여 가장 개발자가 오류를 빨리 찾을 수 있는 매우 좋은 예외이다.
체크 예외 특징
- 개발자가 실수로 예외를 누락하지 않도록 컴파일러를 통해 문제를 잡아주는 매우 훌륭한 안전장치이다
- 이를 통해 개발자는 어떤 체크 예외가 발생하는지 쉽고 빠르게 코드를 작성하는 단계에서 파악할 수 있게 된다
- 그러나 개발자는 모든 체크 예외를 반드시 잡거나 던지도록 처리해야하기 때문에 크게 신경쓰고 싶지 않은 예외까지 모두 챙겨야 해서 코드가 지저분해지거나 매우 번거롭게 된다.
런타임 예외, 언체크 예외 특징
- 체크 예외가 처리할 수 없는 예외를 throws로 밖으로 계속 던져야 하는 반면 런타임 예외는 신경쓰고 싶지 않은 예외를 무시할 수 있어 선택적으로 예외를 처리할 수 있게 해준다
- 하지만 컴파일러가 체크해주지 않기 때문에 개발자가 실수로 예외를 누락할 수 있다
이러한 특징 때문에 과거에는 컴파일러에서 오류를 검증할 수 있는 체크 예외를 라이브러리나 애플리케이션 개발 시 많이 사용했다고 한다
그러나 현대 개발에서는 실용적인 이유로 체크 예외를 거의 사용하지 않는다고 한다
UnCheckedException - 런타임 예외 만들기
public class UnCheckedException extends RuntimeException {
public UnCheckedException(String message) {
super(message);
}
}
RuntimeException을 상속받으면 컴파일러가 체크해주지 않는 언체크드예외(런타임 예외)를 만들 수 있다.
CheckedException - 체크 예외 만들기
package exception;
public class CheckedException extends Exception {
public CheckedException(String message) {
super(message);
}
}
체크 예외인 Exception을 상속받으면 컴파일러가 체크해주는 체크 예외를 만들 수 있다
런타임 예외 발생 시켜 보기
Repository 클래스에서 저장을 할 때 어떤 이유로 런타임 예외가 발생한다고 가정하도록 throw new UnCheckedException()으로 언체크 예외를 발생시키면 컴파일러에서도 아무런 표시 없이 정상적인 코드처럼 보여지는 것을 알 수 있다.

하지만 이를 실행해보면 아래처럼 예외 로그가 예외를 발생시켰을 때 작성한 메시지와 함게 출력되는 것을 확인할 수 있다.
예외 로그 트레이스를 보면 Repository에서 예외가 발생했는데, Service -> Controller -> main 까지 쭉 예외가 올라온 것을 확인할 수 있는데, 런타임 예외(UnCheckedException)은 결과에서 처럼 throws 키워드로 예외를 밖으로 던지는 명시적인 코드를 적지 않아도 알아서 자동으로 상위로 예외가 올라오는 것을 확인할 수 있다.

체크 예외 발생시켜 보기
반면 체크 예외가 발생했다고 가정하면 IDE에서 이미 빨간줄(설정마다 다름)로 이미 오류가 발생했으니 이를 처리하라고 알려주기 때문에 개발자는 발생한 체크예외를 꼭 try - catch로 처리하거나 throws로 밖으로 던지는 코드를 무조건! 작성해야 한다

이를 무시하고 그대로 실행해보면 아래와 같이 체크예외인 CheckedException은 반드시 잡거나 던져 지도록 선언해야 한다는 오류 메시지가 나타나는 것을 확인할 수 있다.

아래의 이미지처럼 체크 예외가 코드에서 발생된다면 throws로 상위로 던지거나, try - catch로 잡아야 컴파일 에러가 발생하지 않는 것을 확인할 수 있다.


RuntimeException이 필수 처리 예외가 아닌 이유
런타임 예외와 그 자식들은 대부분 애플리케이션 레벨에서 처리할 수 없는 예외들로 모아서 구성되어있어 애초에 애플리케이션에서 계속 번거롭게 예외를 던지는 코드를 작성하지 않도록 throws 키워드를 생략할 수 있게 설계 되어있다
즉, 예외가 던져지는 중간 계층의 코드들에서 발생한 런타임 예외에 대해서 전혀 몰라도 된다
Repository에서 발생한 CheckedException 를 throws로 상위로 던진다고 가정해보면, 지금 코드의 구조의 계층은 ExceptionMain - Controller - Service - Repository로 구성되어있는데 체크 예외는 무조건 잡거나, 처리해야하기 때문에 중간 단계에서 해당 에러를 처리할 수 없음에도 이 예외와 관련된 코드를 던지거나 잡는 코드를 무조건 입력해주어야 한다.
아래 이미지를 보면 Repository에서 발생한 예외를 throws로 상위로 던졌더니 Repository의 메서드를 사용하는 Service의 save()메서드에서 컴파일 에러가 발생한 것을 확인할 수 있다.


그래서 Service에서 해당 예외를 잡거나 다시 던져야 하는 코드를 필수로 작성해주어야 한다.
잡지 않고 던지면 이 던지는 코드는 main()까지 계속 던져진다.
이렇게 계속 던지면 이 던지는 코드는 결국 main()메서드에까지 예외를 던지는 throws를 던지는 코드를 작성하게 된다.



그럼 이게 무엇이 문제냐라고 하면 실제 실무에서는 이렇게 처리해야 체크 예외가 한 두가지가 아니라는 점이다.
위에서 설명했던 것 처럼 과거의 라이브러리들은 컴파일 시점에서 처리해야하는 체크 예외를 자주 사용해야 했기에 뭐만하면 다 체크예외가 발생하여 해서 수 많은 체크 예외들을 중간의 모든 계층에서 throws로 던져야하는 체크 예외 지옥에 빠져버리곤 했다고 한다.
데이터베이스에 문제가 생겨서 발생하는 SQLException을 예시로들면 데이터베이스에 발생한 문제가 해결되지 않으면 애플리케이션에서 아무리 정상흐름으로 복구해봤자 애플리케이션이 정상적으로 동작하지 않는다
또한 어떤 라이브러리에서 발생하는 체크 예외를 모든 계층에 던지는 코드를 작성하고 이 라이브러리를 다른 라이브러리로 바꾸었을 때 발생하는 체크 예외가 있다면 기존에 발생하는 체크 예외를 처리했던 코드를 모두 지우고 새롭게 발생하는 체크 예외를 또 모든 계층에 작성해 주어야 한다.
라이브러리가 50개면 50개 다 작성해야 하는 것이다.
복구가 가능한 예외도 존재하지만 대부분의 예외는 지금의 예시처럼 애플리케이션 레벨에서 복구가 불가능하여 이런 문제들은 일관성있게 공통으로 처리하는 것이 좋은데, 결국 사용자에게 안내 메시지를 남기거나 개발자가 오류를 파악할 수 있도록 로그를 남기는 것이다.
CheckedException -> UnCheckedException 변경
이런 부분을 RuntimeException(UnCheckedException)이 해결해주는데, 아래 이미지 처럼 발생한 체크 예외를 런타임 예외로 바꿔서 던져주면 throws 키워드 없이 예외를 상위 계층으로 모두 던질 수 있게되어 중간 계층에서 이 예외를 아무런 신경을 쓰지 않아도 된다.
그리고 이렇게 개발자가 발생한 예외에 대해서 인지할 수 있도록 로그를 남겨주고 런타임 예외는 코드 중간에 런타임 예외가 발생한다는 표시가 없기 때문에 개발자들이 인지할 수 있도록 문서화를 해두는 것이 중요하다
지금은 예제이기 때문에 출력문으로 작성했지만 실제로는 저장할 수 있는 로그 라이브러리를 사용한다.

예제이므로 간단하게 표시했지만 마지막에서 이렇게 처리할 수 없는 예외들을 모아서 고객에게 안내 메시지를 보여주는 등의 처리를 공통으로 처리하면 된다.
이 부분은 스프링 프레임워크에서 제공하는 다양한 기능이 많으므로 스프링을 공부하면 우아하게 처리할 수 있는 다양한 방법이 많이 있다.

결론
체크 예외는 개발자들이 가장 빠르게 오류를 인지하게 할 수 있는 좋은 예외로 처음 자바를 설계할 당시에는 이런 체크 예외가 더 나은 선택이라고 했기 때문에 자바가 기본으로 제공하는 기능들에는 체크 예외가 많았다.
그러나 시간이 흐르면서 복구 할 수 없는 예외가 너무 많아졌고 특히 라이브러리들을 점점 더 많이 사용하게 되면서 처리하는 예외도 많아져서 과거 개발자들은 throws Exception이라는 절대 해서는 안되는 극단적인 방법도 자주 사용하게 되었다고 한다.
Exception은 일반적으로 처리할 수 있는 예외의 최상위 예외이기 때문에 모든 체크 예외를 밖으로 다 던져버려서 결과적으로 다른 체크 예
외를 컴파일 단계에서 체크할 수 있는 기능이 무효화되어 중요한 체크 예외를 다 놓치게 된다.
throws Exception하면 모든 예외를 다 던질 수 있으니 좋을거라고 생각할 수 있지만 이는 바로 컴파일 시점에 오류를 알 수 있게 해주는 체크 예외의 본질을 어긋나는 처리 방법이므로 절대로 사용해서는 안된다
이러한 체크 예외의 문제점 때문에 최근의 라이브러리, 프레임워크들은 대부분 런타임 예외를 기본으로 제공한다
런타임 예외도 필요하면 잡을 수 있기 때문에 애플리케이션 중간에서 잡아야 한다면 잡아서 처리하면되고 그렇지 않다면 자연스럽게 던져두고 이런 공통예외를 처리하는 부분을 가장 마지막에 만들어서 처리해주면 된다.
참고한 자료 - 김영한님 강의 정리 내용
'이론 직접 정리 > 자바' 카테고리의 다른 글
Shallow Copy와 Deep Copy의 차이?, 자바에서 Deep Copy를 하기 위해서는 무엇을 사용해야 할까? (0) | 2025.03.13 |
---|---|
Java는 Call By Value? Call By Reference? (0) | 2025.03.13 |
Managed - Unmanaged 언어의 차이는 무엇이고 어떤 장, 단점이 있는가 (0) | 2025.03.13 |
불변 객체는 무엇이며 Java에서 어떻게 구현하는가, String 클래스가 불변인 이유 (0) | 2025.03.05 |
Java Final 키워드에 대해서 설명, 각각의 쓰임에 따라 어떻게 동작하는가?(Class, Method, Variable) (0) | 2025.03.04 |