IllegalStateException이란?

Java에서 IllegalStateException은 런타임 예외이다. 이는 메서드가 부적절하거나 잘못된 시점에 호출되었음을 나타낸다. 즉, 객체가 요청된 작업을 수행하기에 올바른 상태가 아니라는 의미다. 이 문제는 객체의 생명주기나 상태에 맞지 않게 객체를 사용할 때 자주 발생한다.

주요 원인과 해결 방법

IllegalStateException이 발생하는 일반적인 시나리오와 해결 방법을 살펴보자.

1. Iterator의 잘못된 사용

가장 흔한 원인 중 하나는 Iterator에서 next()를 호출하기 전에 remove()를 호출하는 것이다.

문제 코드

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");

        Iterator<String> iterator = list.iterator();
        
        try {
            // next()를 먼저 호출하지 않았기 때문에 IllegalStateException 발생
            iterator.remove(); 
        } catch (IllegalStateException e) {
            System.err.println("예외 발생: " + e.getMessage());
        }
    }
}

remove() 메서드는 next() 호출 한 번당 한 번만 호출할 수 있다. next() 호출 없이 remove()를 호출하면 예외가 발생한다.

해결 방법

remove()를 호출하기 전에 항상 next()를 먼저 호출해야 한다. 이렇게 하면 이터레이터가 제거할 유효한 요소에 위치하게 된다.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorSolution {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");

        Iterator<String> iterator = list.iterator();
        
        if (iterator.hasNext()) {
            iterator.next(); // 첫 번째 요소로 이동
            iterator.remove(); // 이제 안전하게 제거 가능
        }
        
        System.out.println("제거 후 리스트: " + list); // 출력: [B]
    }
}

2. 닫힌 리소스에 대한 작업

이미 닫힌 ScannerStream과 같은 리소스를 사용하려고 시도하는 경우에도 이 예외가 발생할 수 있다.

문제 코드

import java.util.Scanner;

public class ScannerExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        scanner.close();
        
        try {
            // 스캐너가 닫혔기 때문에 IllegalStateException 발생
            scanner.nextLine(); 
        } catch (IllegalStateException e) {
            System.err.println("예외 발생: " + e.getMessage());
        }
    }
}

close()가 호출되면 Scanner 객체는 더 이상 입력을 읽을 수 있는 상태가 아니다.

해결 방법

리소스를 닫기 전에 필요한 모든 작업을 수행해야 한다. try-with-resources 블록을 사용하는 것이 리소스 생명주기를 효과적으로 관리하는 가장 좋은 방법이다.

import java.util.Scanner;

public class ScannerSolution {
    public static void main(String[] args) {
        try (Scanner scanner = new Scanner(System.in)) {
            System.out.print("이름을 입력하세요: ");
            String name = scanner.nextLine();
            System.out.println("안녕하세요, " + name);
        } 
        // 스캐너는 여기서 자동으로 닫힌다.
        // 더 이상 작업이 필요 없다.
    }
}

3. 사용자 정의 클래스의 잘못된 상태

메서드가 특정 순서로만 호출되어야 하는 클래스를 정의할 수 있다. 순서가 위반되면 IllegalStateException을 발생시켜 계약을 강제할 수 있다.

문제 코드

public class ConnectionManager {
    private boolean connected = false;

    public void connect() {
        this.connected = true;
        System.out.println("연결되었습니다.");
    }

    public void sendData(String data) {
        if (!connected) {
            throw new IllegalStateException("연결되지 않았습니다. 데이터를 보낼 수 없습니다.");
        }
        System.out.println("데이터 전송: " + data);
    }

    public static void main(String[] args) {
        ConnectionManager manager = new ConnectionManager();
        try {
            // connect()를 호출하지 않았기 때문에 IllegalStateException 발생
            manager.sendData("안녕하세요");
        } catch (IllegalStateException e) {
            System.err.println("예외 발생: " + e.getMessage());
        }
    }
}

해결 방법

메서드를 호출하기 전에 항상 객체가 올바른 상태인지 확인해야 한다. 이 경우 sendData() 전에 connect()를 호출한다.

public class ConnectionManagerSolution {
    private boolean connected = false;

    public void connect() {
        this.connected = true;
        System.out.println("연결되었습니다.");
    }

    public void sendData(String data) {
        if (!connected) {
            throw new IllegalStateException("연결되지 않았습니다. 데이터를 보낼 수 없습니다.");
        }
        System.out.println("데이터 전송: " + data);
    }

    public static void main(String[] args) {
        ConnectionManagerSolution manager = new ConnectionManagerSolution();
        manager.connect(); // 먼저 연결을 설정한다
        manager.sendData("안녕하세요, 세상!"); // 이제 데이터를 안전하게 보낼 수 있다
    }
}

결론

IllegalStateException은 예방적 성격의 예외이다. 객체의 상태에 따라 메서드가 올바르게 사용되도록 강제하여 프로그래밍 오류를 조기에 발견하는 데 도움을 준다. 이 예외를 피하려면 상태 기반의 전제 조건이 있는 메서드를 호출하기 전에 항상 객체가 적절한 상태인지 확인하는 습관을 들여야 한다.

Leave a comment