예외와 에러의 차이

허세진·2026년 1월 15일

backend

목록 보기
9/20
post-thumbnail

Error

Error는 JVM 수준에서 발생하는 심각한 문제로, 애플리케이션이 복구할 수 없는 상황이다.

특징

  • java.lang.Error를 상속한다.
  • catch로 처리하지 않고 그대로 종료시키는 것이 원칙이다.

대표 예시

  • OutOfMemoryError: JVM 힙 메모리 고갈
  • StackOverflowError: 스택 메모리 초과 (무한 재귀 등)
  • NoClassDefFoundError: 클래스 로딩 실패

Exception

Exception은 애플리케이션 로직에서 발생할 수 있는 문제로, 적절히 처리하면 복구 가능한 상황이다.

특징

  • java.lang.Exception을 상속한다.
  • 개발자가 예측하고 처리해야 한다.
  • 비즈니스 로직의 일부로 간주한다.

Checked Exception

특징

  • Exception을 상속하지만 RuntimeException은 상속하지 않는다.
  • 컴파일 타임에 체크됨 - 반드시 처리하거나 throws로 선언해야 한다.
  • 복구 가능한 외부 요인으로 인한 예외 상황이다.

대표 예시

// 1. IOException - 파일 입출력 시 발생
public String readFile(String path) throws IOException {  // ✅ throws 선언 필수
    BufferedReader reader = new BufferedReader(new FileReader(path));
    try {
        return reader.readLine();
    } finally {
        reader.close();
    }
}

// 호출하는 쪽에서 반드시 처리해야 함
public void caller() {
    try {
        String content = readFile("/path/to/file.txt");
    } catch (IOException e) {  // ✅ 반드시 catch 또는 throws
        // 파일이 없거나, 권한이 없거나, 디스크 문제 등
        // 대체 파일 사용, 재시도, 사용자에게 알림 등 복구 시도
        logger.error("Failed to read file", e);
    }
}

// 2. SQLException - 데이터베이스 작업 시 발생
public User findUser(Long id) throws SQLException {
    Connection conn = dataSource.getConnection();
    try {
        PreparedStatement stmt = conn.prepareStatement(
            "SELECT * FROM users WHERE id = ?"
        );
        stmt.setLong(1, id);
        ResultSet rs = stmt.executeQuery();
        // ... 결과 처리
    } finally {
        conn.close();
    }
}

// 3. ClassNotFoundException - 동적 클래스 로딩 시 발생
public Object createInstance(String className) throws ClassNotFoundException {
    Class<?> clazz = Class.forName(className);  // 체크드 예외 발생 가능
    // 설정 파일에 명시된 클래스가 classpath에 없을 수 있음
}

Checked Exception이 발생하는 이유

  1. 외부 리소스(파일, 네트워크, 데이터베이스)는 항상 실패할 수 있음
  2. 개발자가 이런 실패 케이스를 명시적으로 인지하고 처리하도록 강제
  3. "예상 가능한 실패"를 코드에 명시

Unchecked Exception

특징

  • RuntimeException을 상속한다.
  • 컴파일러가 체크하지 않음 - 처리가 선택사항
  • 프로그래밍 오류나 로직 결함을 나타낸다.

대표 예시

// 1. NullPointerException - 가장 흔한 언체크드 예외
public void processUser(User user) {
    // user가 null이면 NPE 발생
    String name = user.getName();  // ❌ 프로그래밍 오류

    // 올바른 방법
    if (user == null) {
        throw new IllegalArgumentException("User cannot be null");
    }
    String name = user.getName();  // ✅
}

// 2. IllegalArgumentException - 잘못된 인자
public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException(
            "Invalid age: " + age + ". Must be between 0 and 150"
        );
    }
    this.age = age;
}

// 3. IllegalStateException - 잘못된 상태에서 메서드 호출
public class OrderProcessor {
    private Order order;
    private boolean processed = false;

    public void process() {
        if (processed) {
            throw new IllegalStateException("Order already processed");
        }
        // 주문 처리
        processed = true;
    }
}

// 4. ArrayIndexOutOfBoundsException - 배열 인덱스 범위 초과
public int getElement(int[] array, int index) {
    // index가 범위를 벗어나면 발생
    return array[index];  // 프로그래머가 검증해야 함
}

// 5. NumberFormatException - 문자열을 숫자로 변환 실패
public int parseUserInput(String input) {
    try {
        return Integer.parseInt(input);
    } catch (NumberFormatException e) {
        // 사용자 입력이 숫자가 아님
        return 0;  // 기본값 반환
    }
}

Unchecked Exception이 발생하는 이유

  1. 개발자의 실수나 검증 누락
  2. 비즈니스 규칙 위반
  3. 호출자가 사전 조건을 지키지 않음

처리 방식의 차이점

Checked Exception

// 방법 1: 직접 처리
public void method1() {
    try {
        Files.readAllLines(Paths.get("config.txt"));
    } catch (IOException e) {
        // 복구 로직: 기본 설정 사용
        useDefaultConfiguration();
    }
}

// 방법 2: 호출자에게 전파 (throws)
public void method2() throws IOException {
    Files.readAllLines(Paths.get("config.txt"));
    // 호출자가 처리하도록 위임
}

// 방법 3: 비즈니스 예외로 래핑 
public void method3() {
    try {
        Files.readAllLines(Paths.get("config.txt"));
    } catch (IOException e) {
        throw new ConfigurationException("Failed to load config", e);
    }
}

Unchecked Exception

// 보통 catch하지 않음 - 프로그램 버그이므로 수정해야 함
public void processOrder(Order order) {
    // order가 null이면 NPE 발생 → 버그 수정 필요
    order.validate();
}

// 하지만 외부 입력 검증 시에는 catch할 수 있음
public void handleUserInput(String input) {
    try {
        int value = Integer.parseInt(input);
        processValue(value);
    } catch (NumberFormatException e) {
        // 사용자에게 메시지
        showError("Please enter a valid number");
    }
}
profile
로그를 파고드는 시간을 즐기는 백엔드 개발자, 허세진입니다.

0개의 댓글