8.2 try-catch와 finally 구문 (try-catch & finally Blocks)
예외 처리를 직접 구현할 때 가장 기본적이고 핵심이 되는 문법이 바로 try-catch 구문입니다. 자바는 에러가 발생했을 때 프로그램이 곧바로 뻗어버리는 대신, 이 구문을 통해 발생한 문제를 적절히 회피하거나 수습할 수 있는 안전망과 같은 역할을 제공합니다.
이번 장에서는 try-catch 문의 구조와 원리, 그리고 finally 블록의 쓰임새까지 알아보겠습니다.
1. try-catch 문의 기본 구조
예외 처리를 위한 try-catch 블록은 크게 예외가 발생할 가능성이 있는 try 블록 과 예외가 발생했을 때 그 예외를 전담해서 처리하는 catch 블록 으로 나뉩니다.
try {
// 예외가 발생할 가능성이 있는 코드들을 여기에 적습니다.
// ...
} catch (Exception1 e1) {
// Exception1 타입의 예외가 발생했을 때 수행될 코드
} catch (Exception2 e2) {
// Exception2 타입의 예외가 발생했을 때 수행될 코드
}
try블록: 코드가 정상적으로 실행되는지 자바가 모니터링하는 구간입니다.catch블록:try안에서 예외가 빵 터지면(throw), 자바는 즉시try내부 실행을 멈추고catch블록 내부의 코드를 실행하여 예외 상황을 수습합니다. 괄호 안의 참조 변수(예시의e1,e2)에는 발생한 예외 객체의 상세 정보가 담겨서 넘어옵니다.
✍️ 예제: 0으로 나누기 (ArithmeticException)
자바에서는 어떤 숫자를 0으로 나누면 수학적으로 불가능하기 때문에, 즉시 에러가 발생하면서 프로그램이 멈춰버립니다. 이를 try-catch로 우아하게 방어해 보겠습니다.
public class ExceptionExam {
public static void main(String[] args) {
System.out.println("프로그램을 시작합니다...");
int num = 100;
int result = 0;
try {
// 이 줄에서 0으로 나누려다가 에러 발생!
result = num / 0;
// 에러가 발생한 뒤의 코드는 실행되지 않고 바로 catch 블록으로 건너뜀
System.out.println("결과: " + result);
} catch (ArithmeticException e) {
// 발생한 에러가 ArithmeticException 타입이면 이 괄호 안이 실행됩니다.
System.out.println("예외 발생! 수학적으로 0으로 나눌 수 없습니다.");
System.out.println("에러 메시지: " + e.getMessage());
}
// 여기까지 무사히 도달했다는 것은, catch 구문 덕분에 프로그램이 비정상 종료되지 않았음을 의미합니다.
System.out.println("프로그램을 정상적으로 종료합니다.");
}
}
2. 여러 예외를 한 번에 처리하기 (다중 catch)
프로그램 코드가 길어지면 try 블록 안에서 0으로 나누는 문제뿐 아니라, 배열 범위를 넘어서거나 파일을 찾지 못하는 등의 여러 종류의 예외가 동시에 터질 가능성이 생깁니다. 이때는 여러 개의 catch 블록을 줄지어서 쓸 수 있습니다.
try {
int[] arr = new int[3];
arr[4] = 10; // 1. 여기서 배열 예외 발생! ArrayIndexOutOfBoundsException
int c = 10 / 0; // 이 코드는 위에서 이미 예외가 터져서 도달조차 못 함
} catch (ArithmeticException e) {
System.out.println("0으로 나눔 에러");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 범위를 벗어난 에러");
} catch (Exception e) {
// Exception 클래스는 모든 예외들의 최상위 부모이므로 "나머지 모든 에러 처리기" 역할 (가장 마지막에 둬야 함)
System.out.println("알 수 없는 알 수 없는 에러가 발생했습니다.");
e.printStackTrace();
}
주의 ⚠️: 다중
catch블록을 작성할 때는if-else if문처럼 위에서부터 순서대로 조건(예외의 종류)을 검사하므로, 부모 예외 클래스일수록 가장 아래쪽에 두어야 합니다. (모든 예외의 조상인Exception이 맨 위에 불독처럼 버티고 있으면, 그 밑에 있는catch블록들은 절대 실행될 기회를 얻지 못해 컴파일 에러가 납니다!)
3. finally 블록 - 무조건 실행되는 보험 코드
예외가 발생하든, 정상적으로 실행되든 상관없이 맨 마지막에 100% 무조건 실행되어야 하는 정리 코드 가 있다면 어떻게 해야 할까요? 가령 열어두었던 무거운 데이터베이스 연결이나 파일 객체를 닫아주어야 할 때가 그렇습니다. 이때 자바는 finally 블록을 제공합니다.
try {
// 예외가 발생할 가능성이 있는 코드
System.out.println("데이터베이스를 엽니다.");
} catch (Exception e) {
// 예외가 발생했을 때만 실행
System.out.println("에러 발생!");
} finally {
// 예외 발생 여부와 관계없이 항상 무조건 실행! 심지어 try나 catch 안에 return;이 있어도 실행됨.
System.out.println("데이터베이스 연결을 안전하게 닫습니다.");
}
try-catch-finally 구문은 자바로 만든 어떤 애플리케이션 프레임워크든 실무에서 가장 흔하게 마주칠 아주 중요한 방어 로직이니 꼭 숙지하시기 바랍니다.
다음 장에서는 우리가 직접 발생시키고자 하는 오류를 만드는 사용자 정의 예외 (Custom Exceptions) 에 대해 배워보겠습니다.