프로그래밍 언어/Java

[Java] 33. 예외(Exception) 처리 하기

WooZzing 2025. 4. 21. 16:05

 

 

 

 

예외(Exception)란?

프로그램이 실행 중에 예상치 못한 문제가 발생하는 것을 말합니다.

 

예를 들어 배달 앱에서, 고객 주소가 비어 있으면 앱이 ‘어떻게 배달하지?’ 라며 멈출 수 있어요.
이런 상황이 바로 예외입니다.

 

그래서 자바는 '예외(Exception)'라는 기능을 제공해서,
개발자가 문제를 감지하고, 처리할 수 있게 도와줍니다.

 

 

 


예외가 발생하는 이유

예외는 마치 우리가 예상하지 못한 상황을 만났을 때처럼 작동합니다.

  • 음식 주문 앱에서 고객이 주소를 입력하지 않음 → “배달할 곳이 없음!”
  • 은행 앱에서 잔액이 0원인데 출금 요청 → “돈이 없는데 출금?”

 

자바에서 예외가 발생하는 대표적인 상황

상황 설명 발생하는 예외
0으로 나누기 수학적으로 불가능 ArithmeticException
배열 범위 벗어나기 없는 인덱스에 접근 ArrayIndexOutOfBoundsException
null 값 사용 존재하지 않는 객체에 접근 NullPointerException
파일이 없음 없는 파일을 열려고 함 FileNotFoundException

 

 

 

예시

0으로 나누기

public class Main {
    public static void main(String[] args) {
        int result = 10 / 0;
        System.out.println("결과: " + result);
    }
}

- 실행 결과 -

Exception in thread "main" java.lang.ArithmeticException: / by zero

➡️ 프로그램은 여기서 멈춰버립니다!

 

 

배열 인덱스 오류

int[] nums = {1, 2, 3};
System.out.println(nums[5]);  // 존재하지 않는 인덱스

➡️ ArrayIndexOutOfBoundsException 발생!

 

 

예외는 우리가 예상하지 못한 일들이 벌어질 때 발생합니다.

자바는 이런 상황을 자동으로 감지해서 '예외'로 알려주고,
개발자가 처리할 수 있도록 도움을 줍니다.
하지만, 예외를 처리하지 않으면 프로그램은 그대로 멈추게 됩니다!

 

 

 


예외 종류

자바 예외는 두 가지로 나뉩니다!

구분 Checked Exception Unchecked Exception
예외 처리 반드시 try-catch 또는 throws 필요 선택 사항 (처리 안 해도 컴파일됨)
주로 언제? 파일, DB, 네트워크 등 외부 자원 처리 개발 실수, 논리 오류
예시 IOException, SQLException NullPointerException, IllegalArgumentException
  • Checked Exception → "꼭 처리해!"
  • Unchecked Exception → "필요하면 처리해!"

 

 

컴파일 전에 발생하는 예외 (Checked Exception)

import java.io.FileReader;

public class CheckedExample {
    public static void main(String[] args) {
        // FileReader는 Checked Exception을 반드시 처리해야 함
        FileReader reader = new FileReader("hello.txt"); // 컴파일 에러 발생!
    }
}
  • FileReader는 파일을 읽을 때 파일이 없을 수 있음을 컴파일러가 감지합니다.
  • 그래서 IOException을 반드시 try-catch로 감싸거나 throws로 넘겨야 합니다.
  • 그렇지 않으면 컴파일 자체가 안 됩니다.

수정 예

import java.io.FileReader;
import java.io.IOException;

public class CheckedExample {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("hello.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

 

 

컴파일 후 실행 중에 발생하는 예외 (Unchecked Exception)

public class UncheckedExample {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length()); // 컴파일은 되지만, 실행 시 NullPointerException
    }
}
  • str은 null이지만, 컴파일러는 그걸 알 수 없기 때문에 컴파일은 통과합니다.
  • 하지만 실행 도중 null.length()를 호출하게 되어 NullPointerException이 발생합니다.

 

 

 


예외처리 기본 문법

1) try-catch — 예외를 직접 처리하는 기본 구조

try {
    // 문제가 생길 수 있는 코드
} catch (예외타입 변수명) {
    // 문제가 생겼을 때 처리할 코드
}

 

 

예시 : 0으로 나누기

public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // 문제 발생!
        } catch (ArithmeticException e) {
            System.out.println("0으로 나눌 수 없어요!");
        }
    }
}

- 출력 결과 -

0으로 나눌 수 없어요!

10 / 0은 수학적으로 불가능해서 ArithmeticException이 발생합니다.
하지만, catch 블록에서 예외를 잡아주기 때문에 프로그램이 멈추지 않아요!

 

 

2) throws — 예외를 호출한 곳에 "전가"하기

public void readFile() throws IOException {
    // 파일을 읽는 코드
}

이 메서드는 IOException이 날 수 있기 때문에
직접 처리하지 않고 호출한 쪽에서 처리하라고 "던지는" 방식이에요.

→ 주로 Checked Exception을 처리할 때 사용합니다.

 

 

3) finally — 예외 발생 여부와 관계없이 무조건 실행

try {
    System.out.println("파일 읽기 시도 중...");
} catch (Exception e) {
    System.out.println("에러 발생!");
} finally {
    System.out.println("무조건 실행: 파일 닫기 등 자원 정리");
}

에러가 나든 안 나든, finally 블록은 항상 실행됩니다.
파일 닫기, DB 연결 해제 같은 정리 작업에 자주 쓰입니다.

 

 

4) 여러 개의 예외를 처리하는 방법

try {
    // 어떤 작업
} catch (IOException e) {
    // 파일 관련 예외 처리
} catch (NumberFormatException e) {
    // 숫자 변환 관련 예외 처리
}

// 또는 자바 7부터는 이렇게도 가능

catch (IOException | NumberFormatException e) {
    // 여러 예외 한 번에 처리
}

 

 

 


대표적인 예외 예제

1) NullPointerException

➡ 아무것도 없는(null) 상태에서 무언가 하려고 할 때 발생

String text = null;
System.out.println(text.length());  // ❌ 오류!

- 예외 메시지 -

java.lang.NullPointerException

 

예방 팁: null 체크를 먼저 해주세요!

if (text != null) {
    System.out.println(text.length());
}

 

 

2) ArrayIndexOutOfBoundsException

➡ 배열의 범위를 벗어난 인덱스에 접근할 때 발생

int[] nums = {1, 2, 3};
System.out.println(nums[5]);  // ❌ 오류!

- 예외 메시지 -

java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3

예방 팁: 항상 배열의 길이를 체크하세요.

if (index < nums.length) {
    System.out.println(nums[index]);
}

 

 

3) NumberFormatException

➡ 문자열을 숫자로 바꿀 때 형식이 잘못되면 발생

String str = "abc";
int num = Integer.parseInt(str);  // ❌ 오류!

- 예외 메시지 -

java.lang.NumberFormatException: For input string: "abc"

예방 팁: 숫자인지 먼저 확인하거나 try-catch로 감싸기

 

 

4) ArithmeticException

➡ 수학 연산이 잘못되었을 때 (ex. 0으로 나누기)

int result = 10 / 0;  // ❌ 오류!

- 예외 메시지 -

java.lang.ArithmeticException: / by zero

예방 팁: 나누기 전에 0인지 확인하기

if (divisor != 0) {
    int result = 10 / divisor;
}

 

 

5) FileNotFoundException

➡ 존재하지 않는 파일을 열려고 할 때 발생

FileReader reader = new FileReader("not_exist.txt");

- 예외 메시지 -

java.io.FileNotFoundException: not_exist.txt (No such file or directory)

예방 팁: 반드시 try-catch 또는 throws로 처리해야 함

try {
    FileReader reader = new FileReader("not_exist.txt");
} catch (FileNotFoundException e) {
    System.out.println("파일이 없습니다!");
}

 

 

 


실습 예제

실습 1: 예외를 try-catch로 잡아보자

public class Main {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
            System.out.println("결과: " + result);
        } catch (ArithmeticException e) {
            System.out.println("0으로 나눌 수 없습니다!");
        }

        System.out.println("프로그램이 멈추지 않았습니다.");
    }
}

- 출력 결과 -

0으로 나눌 수 없습니다!
프로그램이 멈추지 않았습니다.

➡ 예외가 발생해도, 프로그램은 정상 흐름을 이어갈 수 있습니다.

 

 

실습 2: 배열 인덱스 예외

public class Main {
    public static void main(String[] args) {
        int[] nums = {1, 2, 3};

        try {
            System.out.println(nums[5]);  // 존재하지 않는 인덱스
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("배열 인덱스를 잘못 사용했습니다.");
        }
    }
}

- 출력 결과 -

배열 인덱스를 잘못 사용했습니다.

➡ 배열은 항상 0부터 시작, 범위를 벗어나면 예외 발생합니다.

 

 

실습 3: finally는 언제나 실행된다

public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("파일 읽는 중...");
            throw new RuntimeException("가짜 에러!");
        } catch (RuntimeException e) {
            System.out.println("예외 발생!");
        } finally {
            System.out.println("무조건 실행되는 finally 블록");
        }
    }
}

- 출력 결과 -

파일 읽는 중...
예외 발생!
무조건 실행되는 finally 블록

➡ 에러가 나든 말든, finally는 항상 실행됩니다. (자원 정리에 유용)

 

 

실습 4: throws로 예외를 넘겨보기

import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            readFile();  // 예외 발생 가능
        } catch (IOException e) {
            System.out.println("파일을 읽는 데 문제가 발생했습니다.");
        }
    }

    public static void readFile() throws IOException {
        FileReader reader = new FileReader("없는파일.txt");
        reader.read();
    }
}

- 출력 결과 -

파일을 읽는 데 문제가 발생했습니다.

➡ throws는 예외를 직접 처리하지 않고 위임하는 방식입니다.

 

 

 


요약 정리

  • 예외는 프로그램을 멈추게 하지만, try-catch로 막을 수 있습니다.
  • finally는 항상 실행되어 자원 정리에 좋습니다.
  • throws는 예외를 호출한 쪽으로 넘기는 선언입니다.
  • 예외 클래스 이름만 봐도 어떤 문제인지 알 수 있습니다.

 

 

 

 

읽어주셔서 감사합니다 😊

 


소스 코드 바로가기