Java 예외 처리 핵심 정리

예외의 본질

프로그램 실행 중 JVM이 수행 불가능한 연작을 감지하면 런타임 에러가 발생한다. Java에서는 이를 예외(Exception)라는 객체로 포장해 던진다. 예외는 정상적인 실행 흐름을 방해하는 비정상 상황을 표현하며, 적절히 대응하지 않으면 프로세스는 비정상 종료된다.

예외 계층 구조

모든 예외는 java.lang.Throwable을 거점으로 전개된다. 두 개의 직접 계열이 존재한다:

  • Error: 메모리 부족, 스택 오버플로우 등 복구 불가능한 치명적 장애
  • Exception: 프로그램 로직으로 수습 가능한 일반적 예외

예외의 분류

검사 예외(Checked Exception)

RuntimeException 계열을 제외한 모든 예외. 컴파일러가 처리 여부를 강제한다. 메서드 선언부에 throws로 명시하거나 try-catch로 감야 한다.

// 데이터베이스 연결 시 발생 가능
java.sql.SQLException

// 파일 입출력 시 발생 가능
java.io.IOException

비검사 예외(Unchecked Exception)

RuntimeException 및 그 하위 클래스. 컴파일 단계에서는 확인되지 않고 실행 시점에 노출된다. 주로 개발자의 실수나 잘못된 인자 전달로 인해 발생한다.

// 배열 인덱스 초과
int[] data = {1, 2, 3};
int value = data[5]; // ArrayIndexOutOfBoundsException

예외 처리 구문

기본 try-catch

try {
    // 위험 코드
    int quotient = divide(10, 0);
} catch (ArithmeticException ex) {
    // 구체적인 예외 먼저 처리
    System.err.println("0으로 나눌 수 없음: " + ex.getLocalizedMessage());
} catch (Exception ex) {
    // 범용 예외는 후순위
    System.err.println("알 수 없는 오류 발생");
}

finally 블록

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("config.txt"));
    String line = reader.readLine();
    processConfig(line);
} catch (IOException ex) {
    log.error("설정 파일 읽기 실패", ex);
} finally {
    // 리소스 해제는 반드시 수행
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException ignored) {}
    }
}

예외 정보 추출 메서드

메서드반환용도
getMessage()String상세 메시지 문자열
toString()String예외 유형과 메시지의 간략 조합
printStackTrace()void콘솔에 스택 추적 출력

throw와 throws의 차이

// throws: 메서드 선언부에 예외 전파 선언
public void transferFunds(Account from, Account to, BigDecimal amt) 
        throws InsufficientBalanceException {
    
    if (from.getBalance().compareTo(amt) < 0) {
        // throw: 메서드 내부에서 예외 객체 직접 생성 및 발생
        throw new InsufficientBalanceException("잔액 부족: " + from.getBalance());
    }
    // 이하 생략...
}

사용자 정의 예외

public class PaymentProcessingException extends Exception {
    
    private final String transactionRef;
    private final LocalDateTime failureTime;
    
    // 기본 생성자
    public PaymentProcessingException() {
        this("결제 처리 중 오류", null, LocalDateTime.now());
    }
    
    // 메시지 포함 생성자
    public PaymentProcessingException(String msg) {
        this(msg, null, LocalDateTime.now());
    }
    
    // 완전 생성자
    public PaymentProcessingException(String msg, String ref, LocalDateTime time) {
        super(msg);
        this.transactionRef = ref;
        this.failureTime = time;
    }
    
    public String getTransactionRef() {
        return transactionRef;
    }
}

실무 적용 원칙

  1. 최소 범위 감시: try 블록은 예외가 발생할 가능성이 있는 코드만 감싼다
  2. 구체적 → 일반적 순서: catch 절은 하위 예외부터 상위 예외 순으로 배치
  3. 무의미한 전파 금지: catch 내부에서 단순히 다시 던지기만 하지 고, 의미 있는 처리나 로깅 수행
  4. 자원 해제 확보: finally 또는 try-with-resources로 외부 리소스 반납 보장
  5. 우아한 복구: 예외 발생 시에도 애플리케이션의 핵심 기능은 유지되도록 설계

try-with-resources 문법

Java 7부터 도입된 자동 자원 관리 메커니즘. AutoCloseable 구현체에 적용된다.

public String readFirstLine(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
    // br.close()가 자동 호출됨, 예외 발생 여부와 무관
}

태그: java Exception Handling RuntimeException Checked Exception Try-with-resources

5월 31일 21:53에 게시됨