본문 바로가기
Java

Java 중급 1 - 예외처리(2)

by KongJiHoon 2025. 5. 4.

1. 예외처리의 중요성

  • 코드가 예상하지 못한 상황을 만나도 안정적으로 종료되거나 정확한 오류 메시지를 출력할 수 있도록 하기 위함
  • 정상 흐름과 예외 흐름을 분리하여 코드 가독성 향상

 

예외 계층 구조

 

  • Exception을 상속하면 체크 예외 (Checked)
  • RuntimeException을 상속하면 언체크 예외 (Unchecked)
  • Error는 JVM 내부 문제. 직접 다루지 말 것

 

2. 예외 기본 규칙

 

  • 예외가 발생하면 반드시 try-catch로 잡거나, throws로 던져야 한다
  • 던질 때는 throws 예외를 명시적으로 선언
public void call() throws MyCheckedException {
    throw new MyCheckedException("ex");
}

 

 

 

3.  체크 예외(Checked Exception)

  • Exception을 상속한 예외
  • 컴파일러가 예외 처리 강제
  • 잡거나 던지지 않으면 컴파일 오류 발생

✅ 예제

public class MyCheckedException extends Exception {
    public MyCheckedException(String message) {
        super(message);
    }
}

 

package practice.exception.basic.checked;

public class Client {


    public void call() throws MyCheckedException {

        // 문제 상황 발생
        throw new MyCheckedException("ex");
    }
}

 

package practice.exception.basic.checked;

public class Service {

    Client client = new Client();

    /**
     * 예외를 잡아서 처리하는 코드
     */
    public void callCatch() {

        try {

            client.call();
        } catch (MyCheckedException e) {

            // 예외 처리 로직
            System.out.println("예외 처리, message : " + e.getMessage());
        }
        // 예외를 잡으면 정상 흐름으로 복귀

        System.out.println("정상 흐름");
    }

    /**
     * 체크 예외를 밖으로 던지는 코드
     * 체크 예외는 예외를 잡지 않고 밖으로 던지려면 throws 예외를 메서드에 필수로 선언해야 한다.
     */
    public void catchThrow() throws MyCheckedException {

        client.call();
    }

}

 

package practice.exception.basic.checked;

public class CheckedCatchMain {

    public static void main(String[] args) throws MyCheckedException {
        Service service = new Service();
        service.callCatch();
        System.out.println("정상 종료");
        service.catchThrow();
    }
}

 

실행결과

1) 예외를 잡는 경우

예외 처리, message = ex
정상 흐름

 

2) 예외를 던지는 경우

Exception in thread "main" practice.exception.basic.checked.MyCheckedException: ex
	at practice.exception.basic.checked.Client.call(Client.java:9)
	at practice.exception.basic.checked.Service.catchThrow(Service.java:31)
	at practice.exception.basic.checked.CheckedCatchMain.main(CheckedCatchMain.java:9)
  • 예외 로그 출력 후 프로그램 종료
  • 흐름: MyCheckedException 호출 후 예외 던짐 -> 이 흐름 반복후 마지막 메인에서 던지고 로그 출력

 

4. 언체크 예외 (Unchecked Exception)

 

  • RuntimeException을 상속한 예외
  • 컴파일러가 체크하지 않음
  • throws 생략 가능, 필요 시 명시해도 됨

 

✅ 예제

package practice.exception.basic.unchecked;

/**
 * RuntimeException을 상속받은 예외는 언체크 예외가 된다.
 */
public class MyUncheckedException extends RuntimeException {

    public MyUncheckedException(String message) {
        super(message);

    }
}

 

 

package practice.exception.basic.unchecked;

public class Client {

    public void call() {
        throw new MyUncheckedException("ex");
    }

}

 

package practice.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("정상 로직");

    }

    /**
     * 예외를 잡지 않아도 된다. 자연스럽게 상위로 넘어감
     * 체크 예외와 다르게 throw 선언을 하지 않아도 된다.
     */
    public void callThrow() {
        client.call();
    }
}

 

package practice.exception.basic.unchecked;

public class UncheckedCatchMain {

    public static void main(String[] args) {
        Service service = new Service();

        service.callCatch();
        System.out.println("정상 종료");

    }
}

 

package practice.exception.basic.unchecked;

public class UncheckedThrowMain {

    public static void main(String[] args) {
        Service service = new Service();
        service.callThrow();
        System.out.println("정상 종료");
    }
}

 

실행 결과

예외 처리, message = ex
정상 로직

 

예외를 던짐 (throws 생략 가능)

MyUncheckedException: ex
→ 예외 로그 출력 후 종료

 

✅ 체크 vs 언체크 예외 비교

항목 체크 예외 언체크 예외
상속 대상 Exception RuntimeException
컴파일 시 체크 ✅ (필수) ❌ (선택)
throws 필요
실무 사용 비율 낮음 (복잡한 외부 작업) 높음 (버그, 개발자 실수 등)

 

  • 체크 예외는 복잡하고 중요한 외부 리소스 처리에 적합 (예: JDBC, File I/O)
  • 언체크 예외는 버그나 잘못된 호출을 잡는 데 적합 (예: NullPointerException, IllegalArgumentException)

 

출처 : 김영한 자바 중급 1편