본문 바로가기

ComputerScience/Java

[Java] Java에서 예외 처리

예외와 에러

프로그래밍에서 예외(Exception)란 입력 값의 처리가 불가능하거나 참조된 값이 잘못된 경우 등 애플리케이션이 정상적으로 동작하지 못하는 상황을 의미합니다. 예외는 개발자가 직접 처리할 수 있으므로 미리 코드 설계를 통해 처리할 수 있습니다.

 

에러(Error)란 예외와 비슷한 의미지만 엄연히 다른 용어로, 에러는 주로 자바의 가상머신(JVM)에서 발생시킵니다.
따라서 예외와 달리 애플리케이션에서 처리할 수 있는 것이 없습니다.
대표적인 예로 메모리 부족(OutOfMemory), 스택 오버플로(StackOverFlow)가 있습니다. 이러한 에러는 발생 시점에 처리하는 것이 아니라 미리 애플리케이션의 코드를 살펴보면서 문제가 발생하지 않도록 예방해서 원천적으로 차단해야 합니다.

예외 클래스

예외 클래스의 상속 구조, 이미지 출처: https://www.tcpschool.com/lectures/img_java_exception_class_hierarchy.png

자바에서 모든 예외 클래스는 Throwable 클래스를 상속받습니다. Exception 클래스는 크게 Checked Exception, UncheckedException이 있습니다. 위에서 파란 점선 내부에 있는 Exception이 Checked Exception, 주황색 점선 내부에 있는 Exception이 UncheckedException 입니다.

Checked Exception과 Unchecked Exception

Checked Exception은 컴파일 단계에서 확인 가능한 예외 상황입니다. 이러한 예외는 IDE에서 캐치해서 반드시 예외 처리를 할 수 있게 표시해줍니다.
반면 Unchecked Exception은 런타임 단계에서 확인되는 예외 상황을 나타냅니다. 즉, 문법상 문제는 없지만 프로그램이 동작하는 도중 예기치 않은 상황이 생겨 발생하는 예외를 의미합니다.
RuntimeException을 상속받는 Exception 클래스는 Unchecked Exception이고 그렇지 않은 Exception 클래스는 Checked Exception입니다.

예외 처리 방법

예외가 발생했을 때 이를 처리하는 방법은 크게 세 가지가 있습니다.

  • 예외 복구
  • 예외 처리 회피
  • 예외 전환

예외 복구

예외 복구 방법은 예외 상황을 파악해서 문제를 해결하는 방법입니다. 대표적인 방법이 try/catch구문입니다. try 블록에는 예외가 발생할 수 있는 코드를 작성합니다. 그리고 catch블록을 거치면서 예외 유형과 매칭되는 블록을 찾아 예외 처리 동작을 수행합니다.

public class UserInputExample {

    public static void main(String[] args) {
        String userInput = "123abc"; // Simulate user input that might not be a number

        try {
            int age = Integer.parseInt(userInput); // Attempt conversion
            System.out.println("Your age is: " + age);
        } catch (NumberFormatException e) {
            System.err.println("Error: Invalid age format. Please enter a number.");
        }
    }
}

예외 처리 회피

이 방법은 예외가 발생한 시점에서 바로 처리하는 것이 아니라 예외가 발생한 메서드를 호출한 곳에서 에러 처리를 할 수 있게 전가하는 방식입니다. 이때 throw키워드를 사용해 어떤 예외가 발생했는지 호출부에 내용을 전달할 수 있습니다.

public class ValidateAge {

    public static void checkAge(int age) throws InvalidAgeException {
        if (age < 0) {
            throw new InvalidAgeException("Age cannot be negative!");
        }
        System.out.println("Your age is valid.");
    }

    public static void main(String[] args) {
        try {
            checkAge(-5); // This will throw an InvalidAgeException
        } catch (InvalidAgeException e) {
            System.err.println(e.getMessage());
        }
    }
}

class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message); // Call superclass constructor to set the message
    }
}

예외 전환

이 방법은 앞의 두 방식을 적절하게 섞은 방식입니다. 예외가 발생했을 때 어떤 예외가 발생했느냐에 따라 호출부로 예외 내용을 전달하면서 좀 더 적합한 예외 타입으로 전달할 필요가 있습니다. 또는 애플리케이션에서 예외 처리를 좀 더 단순하게 하기 위해 래핑(wrapping)해야 하는 경우도 있습니다. 이런 경우에는 try/catch 방식을 사용하면서 catch 블록에서 throw키워드를 사용해 다른 예외 타입으로 전달하면 됩니다.

참고

  • 스프링 부트 핵심 가이드 "스프링 부트를 활용한 애플리케이션 개발 실무" , 장정우, 2022