https://bdisappointed.tistory.com/205
[Java] 예외처리 - 예외 계층 구조, 예외 처리 기본 룰
이번에 스프링 예외 처리를 공부하던 중, 자바 기본 예외 처리를 복습하고자 글을 작성한다.예외처리가 왜 필요한지 간단하게 한줄 요약하자면, 자바 내부에서 로직을 처리하면서 다양한 예외
bdisappointed.tistory.com
이전에 말했듯, Exception 과 그 하위 예외는 모두 컴파일러가 체크하는 체크예외이다.
하지만 하위 예외중 런타임 예외는 제외한다.
체크예외
package exception.basic.checked;
//Exception을 상속받은 예외는 체크예외가 된다(컴파일러가 잡아줌)
public class MyCheckedException extends Exception{
public MyCheckedException(String message) {
super(message);
}
}
Exception을 상속 받으면 체크 예외가 된다. 이후 작동 구조는 다음과 같다.
package exception.basic.checked;
public class Client {
public void call() throws MyCheckedException {
throw new MyCheckedException("ex"); // 예외 발생!!! 나는 해결 못해!!! 밖으로 던져!!!
}
}
클라이언트 클래스 일부로 상황을 만들기 위해 위에 만든 체크 예외를 throw 를 예외를 발생시키고, throws를 통해 메서드 밖으로 던졌다.(던졌다는 의미는 호출한 곳으로 예외를 다시 전달한다는 뜻이다)
package exception.basic.checked;
public class Service {
Client client = new Client();
/**
예외를 잡아서 처리하는 코드
try = 예외를 잡다
catch = 잡은 예외를 어떻게 처리할 것인가?
*/
public void callCatch() {
try {
client.call();
} catch (MyCheckedException e) {
//예외처리 로직
System.out.println("예외 처리, message = " + e.getMessage());
}
System.out.println("정상 흐름");
}
/**
* 체크 예외를 밖으로 던지는 코드
* 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야한다
*/
public void callThrow() throws MyCheckedException {
client.call();
}
}
위에서 call 메서드를 호출 하면 예외를 터트리게 해놨다.
Service 클래스에서 callCatch와 callThrow가 존재하는데, callCatch는 예외를 잡아서 catch로 처리하는 로직이고, callThrow는 다시 Service를 호출한 곳으로 예외를 던지는 로직이다.
즉, 현재 클래스에서 callCatch 메서드 내부에서 catch로 예외를 잡게 되면, 애플리케이션이 정상 흐름으로 변경되어 정상적으로 애플리케이션을 종료할 수 있다.
이제 위 클래스들을 사용하는 main을 보자.
먼저 callCatch() 를 사용하여 예외를 잡는 예시이다.
package exception.basic.checked;
public class CheckedCatchMain {
public static void main(String[] args) {
Service service = new Service();
service.callCatch();
System.out.println("정상 종료");
}
}

예외를 위 Service 클래스에서 잡았기에, main의 모든 메서드가 정상 수행된다.

클라이언트에서 발생한 체크 예외를 service에서 잡힌 순서를 그림으로 표현했다.
그래서 위 코드의 흐름을 다시 정리해보자면,
1. try 블럭 안에서 발생하는 예외를 잡아서 catch로 넘긴다.
2. 만약 try에서 잡은 예외가 catch 블럭 안에 선언되어 있지 않다면, 예외를 잡을 수 없기에 호출한 상위 클래스로 예외를 던져야 한다.
3. 예외도 객체이기에 다형성을 적용할 수 있는데, 다음과 같이 작성하면 Exception의 하위 예외 까지 다 잡아서 처리할 수 있다.
public void callCatch() {
try {
client.call();
} catch (Exception e) {
//예외 처리 로직
}
System.out.println("정상 흐름");
}
예외를 처리하지 않고 던진다면?

만약 이처럼 아무도 예외에 책임을 지지 않으면 어떻게 될까?
package exception.basic.checked;
public class CheckedThrowMain {
public static void main(String[] args) throws MyCheckedException{
Service service = new Service();
service.callThrow();
System.out.println("강제 종료");
}
}
아까는 callCatch를 호출 했다면, 이번에는 callThrow를 통해 예외를 main까지 던지도록 수정했다.

아까 Service 코드의 callThrow 메서드를 기억해보면, throws 를 통해 호출하는 클래스까지 예외를 던졌다.
그래서 예외가 최초 혹은 최종 목적지인 main 클래스까지 올라오고, 결국에는 더이상 던질 수 있는 상위 클래스가 없기에 컴파일 오류 발생시키고 애플리케이션이 종료된다.
체크 예외의 장단점
당연히 예외를 처리하지 않으면 컴파일러가 이를 잡아주기 때문에, 개발자가 실수로 예외를 처리 하지 않아도 어떤 곳에서 어떤 예외가 발생했는지 컴파일러를 통해 쉽게 찾고 해결 할 수 있다.
다만 실제 애플리케이션 개발시 개발자가 몇백개의 예외를 이런식으로 처리하고 있다면 너무 번거로운 일이 되기 때문에, 비즈니스 로직에 집중하지 못하고 예외를 처리하는데 한세월이 걸릴 수 있다.
언체크 예외
Exception은 모두 체크 예외라고 했다.
다만 예외로 자식 클래스 중 RuntimeException은 제외인데, 말 그래도 컴파일러가 체크하지 않겠다는 뜻이다.
기본적인 구조는 체크 예외와 동일하나, 개발자가 직접 throws를 선언하지 않아도 생략할 수 있고, 생략된 경우 자동으로 예외를 던질 수 있다.
결정적인 차이는 throws 생략 유무 !
package exception.basic.unchecked;
/**
* RuntimeException을 상속받은 예외는 언체크 예외가 된다.
*/
public class MyUncheckedException extends RuntimeException {
public MyUncheckedException(String message) {
super(message);
}
}
이번에는 RuntimeException을 상속받아 언체크 예외를 만들었다.
package exception.basic.unchecked;
/**
* 언체크 예외는 throws로 던지지 않아도 알아서 밖으로 던져줌(해결 못하면)
*/
public class Client {
public void call() {
throw new MyUncheckedException("ex"); // 예외 발생!!! 폭탄 생성!!! 해결 불가!! 던져!!1
}
}
이번 코드를 잘 보면 call() 메서드로 터진 언체크 예외를 throw를 통해 직접 던지지 않았다.
package exception.basic.unchecked;
/**
* UnChecked 예외는
* 예외를 잡거나 던지지 않아도 된다
* 왜냐면 예외를 잡지 않아도 자동으로 밖으로 던져주기 때문이다
*/
public class Service {
Client client = new Client();
/**
* 필요하면 예외 잡기 가능
*/
public void callCatch() {
try {
client.call();
} catch (MyUncheckedException e) {
//예외 처리 로직
System.out.println("예외 처리, message = " + e.getMessage());
}
System.out.println("정상 로직");
}
/**
* 예외를 잡지 않아도 됨.
* 체크 예외와 다르게 throws 예외 선언 안해도 됨.
*/
public void callThrow() {
client.call();
}
}
service 코드를 보면 callCatch로 잡는 로직도 있지만 callThrow로 던지는 로직이 있다. 하지만 여기서도 throws로 예외를 명시하지 않았다. 이렇게 되면 자동으로 상위로 예외가 던져지게 된다.
실행 결과는 체크 예외의 결과와 똑같기 때문에 생략한다.
언체크 예외의 장단점
장점은 throws를 생략하여 신경 쓰고 싶지 않은 예외를 무시할 수 있다. 반면에 당연히 단점은 개발자가 실수로 누락한 예외를 생략하게 된다면 컴파일러로 예외를 잡지 못해 예외가 발생한 부분을 찾는데 오래걸릴 수 있다는 점이다.
'Java > Exception' 카테고리의 다른 글
| [Java] 예외처리 - 예외 계층 구조, 예외 처리 기본 룰 (0) | 2025.07.27 |
|---|---|
| [Java] 예외 처리 (Exception) 워밍업 (0) | 2024.08.15 |