Java (예외 처리 Exception Handling)

최병현·2026년 1월 14일

Java

목록 보기
36/38

이 내용은 Backend / OOP logic in Java 영역에서 “프로그램을 비정상 종료시키지 않고, 예외 상황을 제어 가능한 흐름으로 바꾸는 방법”을 다루는 Exception Handling(예외 처리) 정리이다. 기존 코드에서 잘못 사용된 예외 타입(에러 코드 개념)을 수정하고, 표준적인 구조로 처음부터 다시 정리한다.


1) Error vs Exception

Java에서 “에러”와 “예외”는 명확히 구분된다.

  • Error: 시스템 레벨 문제(JVM, 메모리 부족 등)로, 개발자가 코드로 복구하기 어려운 상황
  • Exception: 개발자가 예측 가능하며, try / catch 등으로 흐름을 제어할 수 있는 상황

즉, 우리가 다루는 대상은 대부분 Exception이며, 목적은 하나다. “프로그램이 예외 때문에 강제 종료되지 않도록 제어하는 것”.


2) 왜 예외 처리를 하는가?

예외 처리는 단순히 “에러를 숨기기”가 아니라,

  • 프로그램의 정상 종료 보장
  • 사용자에게 의미 있는 메시지 제공
  • 파일, DB, 네트워크 등 리소스 정리(finally)

을 위해 반드시 필요한 구조다.


3) 예외 처리 방식

3-1) if문으로 방어하는 고전적 방식

예외가 발생하기 전에 조건으로 차단하는 방식이다. 단순하지만 모든 예외를 if로 처리하면 코드가 복잡해진다.

int a = 10;
int b = 0;

if (b == 0) {
    System.out.println("0으로 나눌 수 없습니다.");
} else {
    int result = a / b;
    System.out.println("결과: " + result);
}

3-2) 표준 방식: try / catch / finally

예외가 “발생할 수 있는 코드”를 try로 감싸고, 발생 시 catch에서 처리한다. finally는 예외 발생 여부와 관계없이 항상 실행된다.

int a = 10;
int b = 0;

try {
    int result = a / b;
    System.out.println("결과: " + result);
} catch (ArithmeticException e) {
    System.out.println("0으로 나눌 수 없습니다: " + e.getMessage());
} finally {
    System.out.println("예외 여부와 관계없이 마지막에 실행됨");
}

4) 에러 코드(예외 타입) 선택

Java에서 예외 처리는 “출력 메시지”가 아니라 어떤 예외 클래스 타입을 catch 하느냐에 의해 동작한다. 즉, 예외 처리는 문자열 비교가 아니라 타입 기반 분기이며, 타입이 맞지 않으면 catch 블록이 실행되지 않는다.


4-1) List 인덱스 접근 시 발생하는 예외

List.get(index)에서 범위를 벗어난 인덱스를 접근하면 발생하는 예외는 IndexOutOfBoundsException이다.

배열(int[])과 컬렉션(List, ArrayList 등)은 내부 구현이 다르기 때문에, 인덱스 오류에 사용되는 예외 타입도 서로 다르다.

  • 배열: ArrayIndexOutOfBoundsException
  • List / 컬렉션: IndexOutOfBoundsException

4-2) 인덱스 예외 처리 구조

아래 코드는 List에서 존재하지 않는 인덱스를 접근했을 때 발생하는 예외를 IndexOutOfBoundsException 타입으로 받아 처리하는 구조이다.

import java.util.List;

public class ExceptionSummary {
    public static void main(String[] args) {
        List<Integer> nums = List.of(1, 2, 3, 4, 5);

        try {
            System.out.println(nums.get(5));
        } catch (IndexOutOfBoundsException e) {  
            System.out.println("Index error: " + e.getMessage());
        } finally {
            System.out.println("프로그램 정상 종료");
        }
    }
}

4-3) 예외 타입이 중요한 이유

catch는 “메시지”가 아니라 예외 클래스 타입으로 분기된다. 따라서 타입이 맞지 않으면:

  • 해당 catch 블록이 실행되지 않고
  • 예외가 상위로 전달되며
  • 프로그램이 그대로 종료될 수 있다

즉, 예외 처리는 “에러 문구 출력”이 아니라 어떤 상황을 어떤 타입으로 분류하느냐(에러 코드 개념)가 핵심이다.


4-4) 정리

  • 배열 인덱스 오류 → ArrayIndexOutOfBoundsException
  • List 인덱스 오류 → IndexOutOfBoundsException
  • 예외 처리는 문자열이 아니라 타입 기반으로 동작한다

이 구조를 기준으로 예외 타입을 선택해야, try/catch가 실제로 “프로그램 흐름 제어” 역할을 수행한다.


5) main에서 예외 처리(실행 흐름)

login()은 예외를 “던지기만” 하고, 실제 처리는 호출자인 main에서 담당한다. 이는 비즈니스 로직과 출력(UI) 로직을 분리하는 구조다.

public class Exception2 {
    public static void main(String[] args) {
        String inputId = "admin";
        String inputPassword = "1234qwer";

        try {
            login(inputId, inputPassword);
        } catch (LoginFailedException e) {
            System.out.println("로그인 실패: " + e.getMessage());
        } finally {
            System.out.println("프로그램 정상 종료");
        }
    }
}

실행 흐름:

  • 비밀번호 불일치
  • login()에서 throw
  • main의 catch에서 메시지 출력
  • finally에서 종료 메시지 출력

6) 핵심 정리

  • 예외 처리는 “프로그램을 죽이지 않고 흐름을 통제”하기 위한 구조
  • catch는 여러 개 가능하며, 구체적인 예외 → 상위 예외 순서가 필수
  • List 인덱스 오류는 ArrayIndexOutOfBoundsException이 아니라 IndexOutOfBoundsException
  • Custom Exception은 “비즈니스 규칙을 코드 구조로 표현”하는 가장 깔끔한 방법

느낀 점

예외 처리는 단순한 에러 방어가 아니라, “어디서 처리하고 어디로 넘길 것인가(throws)”를 설계하는 과정이다. 특히 예외 타입(에러 코드)을 정확히 쓰지 않으면, try/catch를 작성해도 의미가 없어진다. Custom Exception을 활용하면 로직 자체가 상황을 설명하게 되어, 유지보수성과 가독성이 크게 향상된다.

profile
No Pain No Gain

0개의 댓글