프로그래밍을 하다보면 오류는 피할 수 없는 존재이다. 오류가 발생하는 이유로는 내부적인 요인과 외부적인 요인이 있다.
오타, 코드 실수, 존재하지 않는 파일, 입력 실수, 메모리 공간 부족, 하드웨어 문제 등등 수 많은 경우가 존재한다.
이러한 오류는 발생 시점과 심각성을 기준으로 크게 분류할 수 있다.
🔍 오류의 분류 기준
1️⃣ 발생 시점에 따른 분류
| 분류 | 설명 | 예시 |
| 컴파일 에러 (Compile-Time Error) | 코드 작성 후 컴파일 단계에서 발생 | 문법 오류, 잘못된 타입 |
| 런타임 에러 (Run-Time Error) | 실행 도중 발생하는 오류 | 정수를 0으로 나누기, 배열 인덱스 초과 |
2️⃣ 심각성에 따른 분류
| 분류 | 설명 | 복구가능성 |
| 에러 (Error) | 시스템 차원의 치명적인 문제 (개발자가 복구 불가) | ❌ |
| 예외 (Exception) | 애플리케이션에서 발생 가능한 예외 상황 (개발자가 대처 가능) | ⭕ |
📚 자바 예외 클래스 구조
자바에서는 모든 오류와 예외가 Throwable 클래스를 상속받는다. (가장 상위에선 Object 클래스를 상속 받고 있다.)

- Throwable: 자바에서 throw할 수 있는 최상위 클래스로 오류나 예외에 대한 메시지를 담는 역할을 한다.
getMessage(), printStackTrace() 메서드로 담긴 메시지를 출력할 수 있다.
- Error: 시스템 레벨에서 발생하는 심각한 오류
예) OutOfMemoryError, StackOverflowError - Exception: 개발자가 처리할 수 있는 예외
- Checked Exception (컴파일 시점에 확인):
IOException, SQLException 등
→ 반드시 throws 혹은 try-catch로 처리해야 한다. - Runtime Exception (런타임 시점에 발생, Unchecked Exception이라고도 한다.):
NullPointerException, IndexOutOfBoundsException 등
→ 처리 선택은 개발자에게 맡겨진다.
- Checked Exception (컴파일 시점에 확인):
- Error: 시스템 레벨에서 발생하는 심각한 오류
📋 Java 예외 종류 정리
1️⃣ Checked Exception (= 컴파일 시 예외 처리 필수)
| 에외 클래스 | 설명 |
| IOException | 입출력 중 발생하는 일반적인 예외 (파일, 스트림 등 사용 시) |
| FileNotFoundException | 존재하지 않는 파일을 열려고 할 때 발생 |
| SQLException | 데이터베이스 작업 중 SQL 관련 문제가 발생했을 때 |
| ParseException | 문자열을 날짜나 숫자로 파싱할 때 잘못된 형식일 경우 |
| ClassNotFoundException | 동적으로 로드하려는 클래스가 클래스패스에 없을 때 |
2️⃣ Unchecked Exception (= RuntimeException)
| 예외 클래스 | 설명 |
| NullPointerException | null 객체를 참조하려 할 때 발생 |
| ArrayIndexOutOfBoundsException | 배열 범위를 벗어난 인덱스 접근 시 발생 |
| ArithmeticException | 0으로 나누기 같은 수학적 오류 발생 시 |
| IllegalArgumentException | 메서드에 잘못된 인자를 전달했을 때 |
| NumberFormatException | 숫자로 변환할 수 없는 문자열을 파싱할 때 |
| ClassCastException | 잘못된 형변환 시 발생 |
⚠️ 예외(Exception)는 어떻게 처리할까?
앞서 언급했듯 에러(Error)는 개발자가 제어할 수 없는 문제지만
예외(Exception)는 충분히 예측하고 처리할 수 있는 문제이다.
따라서 Java에서는 다양한 방법으로 예외 상황을 정교하게 제어할 수 있도록 하고 있다.
✅ 1. try-catch(-finally) 문
가장 기본적이고 널리 사용되는 예외 처리 방식이다.
try {
// 예외 발생 가능 코드
} catch (ExceptionType e) {
// 예외가 발생했을 때의 처리
} finally {
// 생략 가능, 무조건 실행되는 영역 (리소스 정리 등)
}
try 영역에서 오류가 발생하면 catch 영역으로 넘어가게 되어 예외 처리에 관한 구문을 실행시킬 수 있다.
이때 finally 영역은 예외가 발생하여도 반드시 실행되어야 하는 부분이 있다면 넣어준다.
🔍 예시:
![]() |
![]() |
![]() |
✅ 2. throw / throws 키워드
예외를 직접 발생시키거나 메서드가 예외를 던질 수 있음을 명시할 때 사용한다.
throw / throws 차이점
| 구분 | throw | throws |
| 역할 | 직접 예외 객체를 발생시킬 때 사용 | 예외를 호출한 쪽으로 전달하겠다고 선언 |
| 위치 | 메서드 내부 | 메서드 선언부 |
| 예외 처리 | 예외를 발생시킴 | 발생 가능성만 알림 (실제 발생은 아님) |
| 사용 대상 | 예외 객체 단일 | 여러 예외도 나열 가능 |
🔍 예시 1: throw
public class Main {
public static void main(String[] args) {
int age = -5;
if (age < 0) {
throw new IllegalArgumentException("나이는 음수가 될 수 없습니다.");
}
System.out.println("정상적인 나이입니다.");
}
}
- 메서드 내부에서 throw를 선언하였다.
- throw는 예외 객체를 생성해서 던진다.
🔍 예시 2: throws
import java.io.*;
public class Main {
public static void main(String[] args) {
try {
readFile(); // 예외 발생 가능
} catch (IOException e) {
System.out.println("파일 처리 중 예외 발생: " + e.getMessage());
// 파일 처리 중 예외 발생: test.txt (지정된 파일을 찾을 수 없습니다)
}
}
// 여기서 예외를 처리하지 않고 throws로 위임
public static void readFile() throws IOException {
FileInputStream fis = new FileInputStream("test.txt"); // 파일이 없으면 예외 발생
fis.read();
fis.close();
}
}
- readFile() 메서드는 IOException을 바로 처리하지 않고 호출한 쪽(main)에 맡긴다.
- 호출부에서는 반드시 try-catch 또는 다시 throws를 선언하여 예외를 처리해야 한다.
☑️ 언제 어떤 방식으로 써야할까?
| 상황 | 사용 방식 | 설명 |
| 예외 직접 발생 | throw | 개발자가 인위적으로 예외를 만들고 던질 때 |
| 예외를 위로 전파 | throws | 메서드에서 예외가 발생할 수 있음을 알릴 때 |
| 즉시 처리 | try-catch | 예외가 발생했을 때 그 자리에서 바로 처리 |
🔁 예외 변환(Exception Translation)이란?
하나의 예외를 catch 한 뒤, 다른 예외(ex. checked->uncheked)로 바꾸어 던지는 것이다.
🎯 왜 예외를 변환할까?
| 목적 | 설명 |
| 명확성 | 원래 예외보다 호출자에게 더 직관적인 예외를 전달할 수 있음 |
| 코드 간결화 | 매번 try-catch 또는 throws를 쓰는 번거로움 제거 |
| 캡슐화 | 하위 계층(예: DAO, Repository)의 세부 예외를 상위 계층에 노출하지 않음 |
💡 예시: IOException → RuntimeException으로 변환
import java.io.*;
public class FileService {
public String readFile(String filePath) {
try {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
return reader.readLine();
} catch (IOException e) {
// 예외를 직접 처리하지 않고, unchecked 예외로 감싸서 던짐
throw new RuntimeException("파일 읽기 중 오류 발생", e);
}
}
}
장점
- 호출하는 쪽에서 throws IOException 없이도 호출 가능
- 예외가 발생하면 RuntimeException으로 전파됨 → 나중에 전체적으로 처리 가능
✅ 커스텀 예외(Custom Exception)란?
자바에서 기본 제공되는 예외 클래스 외에 개발자가 직접 정의한 예외 클래스이다.
- 커스텀 예외를 사용하면 상황 파악, 서비스 로직에 맞는 예외를 구성 가능하고 Spring의 @ControllerAdvice와 결합할 수 있다.
1️⃣ Checked Exception으로 만들기
public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
- Exception을 상속하면 Checked Exception이 된다.
- 사용 시 throws 또는 try-catch로 반드시 처리해야 한다.
2️⃣ Unchecked Exception으로 만들기
public class InvalidUserInputException extends RuntimeException {
public InvalidUserInputException(String message) {
super(message);
}
public InvalidUserInputException(String message, Throwable cause) {
super(message, cause);
}
}
- RuntimeException을 상속하면 Unchecked Exception이 된다.
- try-catch 없이도 호출 가능하고 필요할 때만 처리할 수 있다.
💡 커스텀 예외 사용 예시


'JAVA' 카테고리의 다른 글
| [JAVA] 객체 지향 프로그래밍의 정의와 특징 (5) | 2025.08.13 |
|---|---|
| [Java] equals 재정의 할 때 HashCode도 재정의 해야 하는 이유 (0) | 2025.07.16 |
| [JAVA] ORM 이란? (0) | 2025.05.31 |
| [JAVA] JDBC 정리 (0) | 2025.05.27 |
| [JAVA] 스레드(Thread) 정리 (0) | 2025.03.01 |



