
1. 프로그램의 비정상 종료 방지
2. 에러 발생 시의 적절한 대응
3. 코드의 가독성 및 유지보수성 향상
4. 프로그램의 신뢰성 향상
5. 디버깅 및 문제 해결에 도움
6. 프로그램 흐름 제어

Throwable 클래스를 중심으로 계층 구조를 형성하고 있습니다. 이 계층 구조는 크게 두 가지 주요 하위 클래스인 Error와 Exception으로 나눌 수 있으며, 각각의 용도와 처리 방법이 다릅니다.Throwable, Error, Exception, RuntimeException, 등 자바에서 기본적으로 제공하는 예외 클래스들은 java.lang에 소속되어 있기 때문에, 별도의 import구문 없이 사용하실 수 있습니다.Throwable 클래스Error, ExceptionError 클래스Error는 프로그램의 비정상적인 상태를 나타내므로, 이를 처리하기 위해 예외 처리 코드를 작성하는 것은 권장되지 않습니다. Error가 발생하면 프로그램을 종료하고 로그를 남기는 것이 일반적입니다.Exception 클래스RuntimeException, IOException, SQLException, 등Exception의 하위 클래스는 크게 두 종류로 분류할 수 있습니다.RuntimeException 클래스RuntimeException을 제외한 모든 하위 클래스RuntimeException 클래스RuntimeException과 그 하위 클래스들은 체크되지 않은 예외(unchecked exception)입니다. 컴파일러가 예외 처리 여부를 검사하지 않으며, 개발자가 선택적으로 처리할 수 있습니다.RuntimeException은 주로 프로그래머의 실수에 의해 발생하는 예외로, 예외 처리를 하지 않아도 컴파일 오류가 발생하지 않습니다. Checked Exception)RuntimeException을 제외한 Exception 클래스의 모든 하위 클래스는 체크 예외입니다. 컴파일러는 이러한 예외가 발생할 가능성이 있는 메서드에 대해 예외 처리를 강제합니다.try-catch 블록)로 처리하거나, 메서드 시그니처에 throws 키워드를 사용해 상위 메서드로 예외를 전달해야 합니다. try-catch 블록: 예외 발생 가능성이 있는 코드를 try 블록 내에 작성하고, 발생한 예외를 catch 블록에서 처리합니다. catch 블록을 사용할 수 있습니다.try {
// 예외 발생 가능 코드
} catch (IOException e) {
// IOException 처리
} catch (SQLException e) {
// SQLException 처리
}
finally 블록: try 또는 catch 블록이 완료된 후 반드시 실행되는 블록입니다. try {
// 예외 발생 가능 코드
} catch (Exception e) {
// 예외 처리
} finally {
// 리소스 해제 또는 정리 작업
}
throw 키워드를 사용합니다.throw 키워드를 이용하면 오류를 발생시킬 수 있습니다. (주로 조건문과 함께 쓰입니다.)throws 키워드를 사용해 명시해야 합니다.public void readFile(String filePath) throws IOException {
if (filePath == null) {
throw new IOException("파일 경로가 null입니다.");
}
// 파일 읽기 작업
}
catch 블록에서 예외를 처리한 후, 같은 예외 또는 새로운 예외를 다시 던질 수 있습니다. try {
// 예외 발생 가능 코드
} catch (IOException e) {
// 예외 로깅 등 추가 작업
throw e; // 동일한 예외 다시 던지기
}
Exception 또는 RuntimeException을 상속하여 구현합니다.public class InvalidUserInputException extends Exception {
public InvalidUserInputException(String message) {
super(message);
}
}
import java.io.*;
public class FileManager {
// 파일을 읽는 메서드
public void readFile(String filePath) throws IOException {
File file = new File(filePath);
// 파일이 존재하지 않으면 IOException 발생
if (!file.exists()) {
throw new IOException("파일이 존재하지 않습니다: " + filePath);
}
// 파일 읽기 작업
BufferedReader reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
try {
// 체크 예외 처리: IOException을 처리하기 위해 try-catch 사용
fileManager.readFile("example.txt");
} catch (IOException e) {
// 예외 처리: 오류 메시지 출력
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
}
}
}
FileManager 클래스에서 파일을 읽는 readFile 메서드를 구현한 것입니다. IOException이라는 체크 예외를 던질 수 있습니다. throws 키워드: readFile 메서드는 throws 키워드를 사용해 IOException을 호출자에게 넘깁니다. main 메서드에서 readFile을 호출할 때, try-catch 블록을 사용해 IOException을 처리합니다. catch 블록에서는 오류 메시지를 출력하여 예외를 처리합니다.throws를 통한 예외 전파throws 키워드를 사용하여 발생할 수 있는 예외를 호출자에게 넘겨줍니다. 이를 통해 예외 처리를 메서드를 호출하는 쪽에서 처리할 수 있게 합니다.public void readFile(String filePath) throws IOExceptiontry-catch 블록을 통한 예외 처리try-catch 블록을 사용해 처리합니다. try { /* 예외 발생 가능 코드 */ } catch (IOException e) { /* 예외 처리 */ }RuntimeException 클래스를 상속하는 모든 예외를 포함합니다. RuntimeException은 Exception 클래스의 서브클래스이므로, 언체크 예외도 Throwable 계층 구조의 일부입니다.public class UncheckedExceptionExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
try {
// ArrayIndexOutOfBoundsException 발생 가능
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
// 예외 처리
System.err.println("배열 인덱스가 잘못되었습니다: " + e.getMessage());
}
// NullPointerException 발생 가능
String text = null;
try {
System.out.println(text.length());
} catch (NullPointerException e) {
// 예외 처리
System.err.println("널 포인터 예외 발생: " + e.getMessage());
}
}
}
ArrayIndexOutOfBoundsException과 NullPointerException이라는 두 가지 언체크 예외를 보여줍니다. RuntimeException의 하위 클래스이며, 발생 시점에 예외를 처리하지 않아도 컴파일러가 이를 강제하지 않습니다.ArrayIndexOutOfBoundsException: 배열의 잘못된 인덱스에 접근하려 할 때 발생합니다. 예제에서는 배열의 길이를 초과하는 인덱스에 접근하려고 시도하여 예외가 발생합니다.NullPointerException: 객체가 null일 때 해당 객체의 메서드를 호출하려 할 때 발생합니다. 예제에서는 null인 문자열 변수에 대해 length() 메서드를 호출하려고 시도하여 예외가 발생합니다.try-catch 블록을 사용할 수도 있습니다.finally 블록과 try-with-resources 구문을 제공합니다.finally 블록은 예외가 발생하더라도 반드시 실행되는 블록입니다. try-catch 구문과 함께 사용되며, 주로 리소스를 해제하거나 중요한 정리 작업을 수행하는 데 사용됩니다. finally 블록은 항상 실행됩니다.import java.io.*;
public class FileManager {
public void readFile(String filePath) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
} finally {
// 리소스 해제
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.err.println("리소스를 해제하는 동안 오류가 발생했습니다: " + e.getMessage());
}
}
}
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
fileManager.readFile("example.txt");
}
}
try 블록: 리소스를 사용해 파일을 읽습니다.catch 블록: 파일 읽기 중 발생한 예외를 처리합니다.finally 블록: 파일을 읽은 후 BufferedReader 리소스를 닫아야 합니다. 이 블록은 예외 발생 여부와 상관없이 항상 실행되므로, 리소스 해제에 적합합니다.finally 블록을 사용할 때는 개발자가 리소스를 직접 관리해야 하며, 코드가 복잡해질 수 있습니다.try-with-resources 구문은 자바 7에서 도입된 기능으로, AutoCloseable 인터페이스를 구현한 리소스(예: 파일, 소켓, 데이터베이스 연결)를 자동으로 닫아주는 기능을 제공합니다. finally 블록을 사용하지 않고도 리소스를 자동으로 해제할 수 있어 코드가 더 간결하고 안전해집니다.AutoCloseable 인터페이스는 java.lang에 포함되어 있는 인터페이스이기 때문에, 별도의 import구문 없이 사용하실 수 있습니다.import java.io.*;
public class FileManager {
public void readFile(String filePath) {
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("파일을 읽는 동안 오류가 발생했습니다: " + e.getMessage());
}
// try-with-resources 구문은 자동으로 리소스를 해제합니다.
}
public static void main(String[] args) {
FileManager fileManager = new FileManager();
fileManager.readFile("example.txt");
}
}
try-with-resources 구문: try 블록에 선언된 리소스는 블록이 종료되면 자동으로 닫힙니다. BufferedReader가 자동으로 닫히므로, finally 블록을 사용할 필요가 없습니다.try-with-resources 구문을 사용하려면 리소스가 AutoCloseable 인터페이스를 구현(implements)해야 합니다. try 구문 바로 뒤에 괄호()를 통해 try-with-resources구문을 이용할 수 있습니다.// CustomResource.java
public class CustomResource implements AutoCloseable {
public CustomResource() {
System.out.println("CustomResource가 열렸습니다.");
}
public void doSomething() {
System.out.println("CustomResource에서 작업을 수행 중입니다.");
}
@Override
public void close() {
System.out.println("CustomResource가 닫혔습니다.");
}
}
// Main.java
public class Main {
public static void main(String[] args) {
// try-with-resources 구문에서 커스텀 리소스 사용
try (CustomResource resource = new CustomResource()) {
resource.doSomething();
} catch (Exception e) {
System.err.println("예외 발생: " + e.getMessage());
}
}
}
CustomResource가 열렸습니다.
CustomResource에서 작업을 수행 중입니다.
CustomResource가 닫혔습니다.
CustomResource 클래스: 이 클래스는 AutoCloseable 인터페이스를 구현하여 close() 메서드를 제공합니다. 이 메서드는 리소스를 닫을 때 자동으로 호출됩니다.doSomething() 메서드: CustomResource 클래스의 메서드로, 리소스를 사용하여 어떤 작업을 수행하는 역할을 합니다.try-with-resources 구문: CustomResource 객체를 try 블록 내에서 생성하고 사용합니다. 블록이 종료되면 자동으로 close() 메서드가 호출되어 리소스가 안전하게 닫힙니다.Unchecked Exception)와 예외 계층을 적절히 활용하는 방향으로 예외 처리 전략이 진화해왔습니다.RuntimeException을 상속하는 예외로, 컴파일러가 예외 처리를 강제하지 않습니다. throws 선언을 추가해야 하는 불편함이 있습니다.DatabaseException이라는 부모 클래스를 만들고, 이를 상속하는 ConnectionException, QueryException 등을 정의할 수 있습니다. InsufficientFundsException과 같은 커스텀 예외를 정의하여, 특정 비즈니스 로직에서 발생하는 예외를 처리할 수 있습니다.RuntimeException과 Exception 같은 상위 클래스를 사용하여 다양한 예외를 포괄적으로 처리하는 방법을 보여줍니다. RuntimeException은 언체크 예외를, Exception은 체크 예외와 언체크 예외를 모두 처리할 수 있습니다.public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
processInput(null); // NullPointerException 발생 가능
divideNumbers(10, 0); // ArithmeticException 발생 가능
accessArrayElement(new int[]{1, 2, 3}, 5); // ArrayIndexOutOfBoundsException 발생 가능
simulateCheckedException(); // IOException 발생 가능 (체크 예외)
} catch (RuntimeException e) {
// RuntimeException과 그 하위 예외를 포괄적으로 처리
System.err.println("런타임 예외 발생: " + e.getClass().getSimpleName() + " - " + e.getMessage());
} catch (Exception e) {
// Exception과 그 하위 예외를 포괄적으로 처리
System.err.println("예외 발생: " + e.getClass().getSimpleName() + " - " + e.getMessage());
}
}
public static void processInput(String input) {
if (input == null) {
throw new NullPointerException("입력 값이 null입니다.");
}
}
public static int divideNumbers(int a, int b) {
return a / b; // 0으로 나누면 ArithmeticException 발생
}
public static int accessArrayElement(int[] array, int index) {
return array[index]; // 잘못된 인덱스 접근 시 ArrayIndexOutOfBoundsException 발생
}
public static void simulateCheckedException() throws Exception {
throw new Exception("체크 예외 발생!");
}
}
런타임 예외 발생: NullPointerException - 입력 값이 null입니다.
processInput(null)에서 NullPointerException이 발생하여 RuntimeException 블록에서 처리됩니다. try 블록processInput(null)은 NullPointerException을 발생시킬 수 있습니다.divideNumbers(10, 0)은 ArithmeticException을 발생시킬 수 있습니다.accessArrayElement(new int[]{1, 2, 3}, 5)는 ArrayIndexOutOfBoundsException을 발생시킬 수 있습니다.simulateCheckedException()은 Exception을 던질 수 있습니다.catch 블록RuntimeException e: 모든 언체크 예외(RuntimeException과 그 하위 클래스)를 처리합니다. NullPointerException, ArithmeticException, ArrayIndexOutOfBoundsException을 포괄적으로 처리할 수 있습니다.Exception e: RuntimeException으로 처리되지 않은 모든 예외를 처리합니다. Exception을 직접 던지는 체크 예외(simulateCheckedException에서 발생)와 모든 체크 예외를 처리할 수 있습니다.예외 계층: 자바의 예외는 Throwable가 최상위 클래스이며, Error와 Exception으로 나뉩니다. Error는 복구 불가능한 시스템 오류를, Exception은 프로그램에서 처리 가능한 예외를 나타냅니다.
체크 예외: 컴파일러가 예외 처리를 강제하는 예외로, 파일 I/O나 네트워크 연결 등 외부 자원에 의존하는 작업에서 주로 발생합니다. throws 키워드를 통해 상위 메서드로 전파하거나, try-catch 블록으로 처리해야 합니다.
언체크 예외: RuntimeException을 상속하는 예외로, 컴파일러가 예외 처리를 강제하지 않습니다. 주로 프로그래머의 실수로 발생하며, 필요에 따라 예외 처리를 하거나 방어적인 코드를 작성해 예방할 수 있습니다.
리소스 반환: 리소스 누수를 방지하기 위해 finally 블록이나 try-with-resources 구문을 사용해 리소스를 안전하게 반환해야 합니다. try-with-resources는 자바 7 이후 도입된 기능으로, AutoCloseable을 구현한 리소스를 자동으로 닫아줍니다.
실무에서의 예외 처리: 실무에서는 체크 예외와 언체크 예외를 상황에 맞게 조합해 사용하며, 필요에 따라 커스텀 예외를 정의해 예외 계층을 설계합니다. 예외를 통해 프로그램 흐름을 제어하는 것은 피해야 하며, 예외는 예외적인 상황에서만 사용해야 합니다.