[Java] 예외 처리 (Exception Handling)

2026. 4. 23. 10:46·Language/Java

* 개념 복습과 학습 정도를 파악하고자 포스팅합니다

 

예외(Exception)

1. 예외란 무엇인가

프로그램 실행 중 발생할 수 있는 예기치 않은 상황을 의미합니다.

예외가 발생하면 프로그램이 비정상적으로 종료될 수 있으므로, 이를 적절히 처리하여 프로그램의 안정성을 높여야 합니다.

 

예외가 발생할 수 있는 대표적인 상황

1. 존재하지 않는 파일을 열려고 할 때
2. 0으로 나누기를 시도했을 때
3. 배열의 범위를 벗어난 인덱스에 접근할 때
4. null 객체의 메소드를 호출할 때

2. 예외 계층 구조

Java의 모든 예외는 Throwable클래스를 상속받으며, 크게 Error와 Exception으로 나뉩니다.

Error는 시스템 레벨의 심각한 오류로 복구가 불가능합니다. 반면 Exception은 프로그램에서 처리할 수 있는 오류입니다.


3. try-catch-finally 구문

예외를 처리하는 가장 기본적인 방법입니다.

try 블록에서 예외가 발생하면 catch 블록이 실행되고, finally 블록은 예외 발생여부와 관계없이 항상 실행됩니다.

public class ExceptionBasic {
    public static void main(String[] args) {
        // 기본 try-catch 예제
        try {
            int result = 10 / 0;  // ArithmeticException 발생!
            System.out.println("결과: " + result);
        } catch (ArithmeticException e) {
            System.out.println("오류: 0으로 나눌 수 없습니다!");
            System.out.println("메시지: " + e.getMessage());
        }

        System.out.println("프로그램 계속 실행됩니다.");
    }
}
실행결과
오류: 0으로 나눌 수 없습니다!
메시지: / by zero
프로그램 계속 실행됩니다.

4. 다중 catch 블록

하나의 try 블록에서 여러 종류의 예외가 발생할 수 있을 때, 각각의 예외를 다르게 처리할 수 있습니다.

public class MultiCatchExample {
    public static void main(String[] args) {
        String[] names = {"김철수", "이영희", "박민수"};

        try {
            // 배열 인덱스 초과
            System.out.println(names[5]);

            // null 참조
            String text = null;
            System.out.println(text.length());

        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("배열 범위 오류: " + e.getMessage());

        } catch (NullPointerException e) {
            System.out.println("null 참조 오류: " + e.getMessage());

        } catch (Exception e) {
            // 가장 상위 예외는 마지막에!
            System.out.println("알 수 없는 오류: " + e.getMessage());
        }
    }
}

catch 블록의 순서가 중요합니다!

구체적인 예외를 먼저 처리하고, 상위 예외 클래스(Exception)는 마지막에 위치해야 합니다.

순서를 잘못 배치하면 컴파일 에러가 발생해요.


5. Multi-catch

Java 7 부터는 파이프( | ) 기호를 사용해서 여러 예외를 한 번에 처리할 수 있습니다.

public class MultiCatchJava7 {
    public static void main(String[] args) {
        try {
            int[] numbers = {1, 2, 3};
            System.out.println(numbers[10]);

        } catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
            // 파이프(|)로 여러 예외를 한 번에 처리
            System.out.println("배열 또는 null 관련 오류: " + e.getMessage());
        }
    }
}

6. finally 블록

예외 발생이 없을 때

  1. RUN: try 블록 실행
  2. SKIP: catch 블록 건너뜀
  3. ALWAYS: finally 블록 실행

예외 발생했을 때

  1. RUN: try 블록 실행 중 예외 발생
  2. CATCH: catch 블록 실행
  3. ALWAYS: finally 블록 실행
import java.io.*;

public class FinallyExample {
    public static void main(String[] args) {
        FileReader reader = null;

        try {
            reader = new FileReader("data.txt");
            int data = reader.read();
            System.out.println("데이터: " + (char)data);

        } catch (FileNotFoundException e) {
            System.out.println("파일을 찾을 수 없습니다");

        } catch (IOException e) {
            System.out.println("파일 읽기 오류");

        } finally {
            // 리소스 정리는 항상 finally에서!
            if (reader != null) {
                try {
                    reader.close();
                    System.out.println("파일을 닫았습니다.");
                } catch (IOException e) {
                    System.out.println("파일 닫기 실패");
                }
            }
        }
    }
}

7. throws 키워드

메소드에서 발생할 수 있는 예외를 호출자에게 전달하는 키워드입니다.

메소드 시그니처에 선언하여 "이 메소드는 이런 예외를 던질 수 있다"고 명시합니다.

 

Flow

  1. 메소드 A 호출: 호출자가 메소드 A를 실행합니다.
  2. 메소드 A에서 예외 발생: 메소드 A에서 IOException이 발생하고, throws로 선언되어 있습니다.
  3. 호출자에게 예외 전달: 메소드 A는 예외를 처리하지 않고 호출자에게 전달합니다.
  4. 호출자가 try-catch로 처리: 호출자는 try-catch 블록으로 예외를 처리합니다.
import java.io.*;

public class ThrowsExample {

    // 예외를 던지는 메소드 (처리 책임을 호출자에게)
    public static void readFile(String fileName) throws IOException {
        FileReader reader = new FileReader(fileName);
        BufferedReader br = new BufferedReader(reader);

        String line = br.readLine();
        System.out.println(line);

        br.close();
        // IOException 처리 안함 → 호출자에게 전달
    }

    public static void main(String[] args) {
        try {
            // throws 선언된 메소드는 반드시 try-catch 또는 throws 필요
            readFile("test.txt");

        } catch (IOException e) {
            System.out.println("파일 읽기 실패: " + e.getMessage());
        }
    }
}

8. throw vs throws

throw

예외를 직접 발생시킬 때 사용합니다.
메소드 내부에서 사용하며, 한 번에 하나의 예외만 던질 수 있습니다.

ex) throw new Exception();

 

throws

예외를 메소드 밖으로 던짐을 선언할 때 사용합니다.
메소드 시그니처에서 사용하며, 여러 예외를 선언할 수 있습니다.

ex) void method() throws Exception
public class ThrowVsThrows {

    // throws: 메소드 선언부에 사용
    public static void method1() throws Exception {
        // throw: 예외를 직접 발생
        throw new Exception("의도적으로 예외 발생");
    }

    public static void validateAge(int age) throws IllegalArgumentException {
        if (age < 0) {
            throw new IllegalArgumentException("나이는 0보다 커야 합니다");
        }
    }
}

9. 커스텀 예외 (Custom Exception)

Java의 기본 예외만으로는 비즈니스 로직의 모든 예외 상황을 표현하기 어렵습니다.

커스텀 예외를 만들어서 더 명확하고 의미있는 예외 처리가 가능해요!

 

제가 생각했을 때 커스텀 예외를 작성할 때는 아래 수칙이 지켜지면 좋을 것 같습니다.

  • Exception 또는 RuntimeException 상속
  • 클래스 이름은 ~Exception으로 끝내기!
  • 기본 생성자와 메시지를 받는 생성자 제공
  • 필요시 원인 예외(cause)를 받는 생성자 추가하기
// Exception을 상속 → Checked Exception
public class InsufficientBalanceException extends Exception {

    private int balance;
    private int amount;

    // 기본 생성자
    public InsufficientBalanceException() {
        super("잔액이 부족합니다");
    }

    // 메시지를 받는 생성자
    public InsufficientBalanceException(String message) {
        super(message);
    }

    // 상세 정보를 포함하는 생성자
    public InsufficientBalanceException(int balance, int amount) {
        super("잔액 부족: 현재 " + balance + "원, 필요 " + amount + "원");
        this.balance = balance;
        this.amount = amount;
    }

    // 원인 예외를 포함하는 생성자
    public InsufficientBalanceException(String message, Throwable cause) {
        super(message, cause);
    }

    public int getBalance() { return balance; }
    public int getAmount() { return amount; }
}
public class BankAccount {
    private String accountNumber;
    private int balance;

    public BankAccount(String accountNumber, int initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance;
    }

    // 출금 메소드 - Checked Exception 사용
    public void withdraw(int amount) throws InsufficientBalanceException {
        if (amount > balance) {
            throw new InsufficientBalanceException(balance, amount);
        }
        balance -= amount;
        System.out.println(amount + "원 출금 완료. 잔액: " + balance + "원");
    }

    // 입금 메소드 - Unchecked Exception 사용
    public void deposit(int amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("입금액은 0보다 커야 합니다");
        }
        balance += amount;
        System.out.println(amount + "원 입금 완료. 잔액: " + balance + "원");
    }

    public int getBalance() { return balance; }
}

public class BankMain {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("1234-5678", 10000);

        try {
            account.deposit(5000);
            account.withdraw(3000);
            account.withdraw(20000);  // 예외 발생!

        } catch (InsufficientBalanceException e) {
            System.out.println("[오류] " + e.getMessage());
            System.out.println("부족한 금액: " + (e.getAmount() - e.getBalance()) + "원");
        }

        System.out.println("\n최종 잔액: " + account.getBalance() + "원");
    }
}
실행결과
5000원 입금 완료. 잔액: 15000원
3000원 출금 완료. 잔액: 12000원
[오류] 잔액 부족: 현재 12000원, 필요 20000원
부족한 금액: 8000원

최종 잔액: 12000원

'Language > Java' 카테고리의 다른 글

[Java] 제네릭(Generic) 완전히 파헤치기  (0) 2026.04.23
[Java] 컬렉션 프레임워크  (0) 2026.04.23
[Java] String 파헤치기  (0) 2026.04.23
[Java]내부 클래스 정리  (0) 2026.04.22
[Java] 배열에 대해서  (0) 2026.04.22
'Language/Java' 카테고리의 다른 글
  • [Java] 제네릭(Generic) 완전히 파헤치기
  • [Java] 컬렉션 프레임워크
  • [Java] String 파헤치기
  • [Java]내부 클래스 정리
BackendInho
BackendInho
항상 열정적으로 부딪히고 깨닫는 주니어 백엔드 개발자의 여정을 기록합니다!
  • BackendInho
    Inho.devlog
    BackendInho
  • 전체
    오늘
    어제
    • 분류 전체보기 (41)
      • TIL (0)
      • Language (21)
        • Java (21)
      • Backend (8)
        • Spring (8)
      • Database (0)
        • SQL (0)
      • CS (8)
        • Computer Structure (2)
        • Network (2)
        • OS (1)
        • Data Structure & Algorithms (3)
      • Devops (0)
        • Docker & Kubernetes (0)
      • Coding-Test (1)
        • Programmers (3)
        • LeatCode (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 공지사항

  • 인기 글

  • 태그

    API
    CS
    HTTP
    java
    REST
    REST API
    Spring
    Spring Boot
    Web
    공간복잡도
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
BackendInho
[Java] 예외 처리 (Exception Handling)
상단으로

티스토리툴바