1. 소개
프로그래밍에서는 메서드가 오류를 만났지만 이를 어떻게 올바르게 처리할지 모르는 상황이 자주 생깁니다. 예를 들어 메서드가 파일을 읽는데 파일이 없으면 어떻게 해야 할까요? 사용자에게 물을까요? 프로그램을 종료할까요? 다른 파일을 시도할까요? 이런 경우 메서드는 호출한 쪽에 “책임을 넘기기”를 선택할 수 있습니다 — 즉 예외를 호출 체인 상위로 “전파”합니다.
예외 전파는 메서드가 오류를 스스로 처리하지 않고 호출 측 코드에 “문제가 생겼어, 네가 결정해!”라고 알려주는 메커니즘입니다.
비유: 당신이 콜센터 직원이라고 상상해 보세요. 고객이 당신이 모르는 질문을 합니다. 추측으로 답하는 대신 “잠시만요, 전문가에게 연결해 드릴게요.”라고 말하고 질문을 더 높은 담당자에게 넘깁니다. 당신은 질문을 “전파”한 것입니다.
키워드 throws: 어떻게 동작하는가
Java에서는 메서드 선언부에 throws 키워드를 사용해 예외 전파를 표시합니다.
문법:
tip_vozvrashchaemogo_znacheniya imyaMetoda(...) throws ExceptionType
{
// 메서드 코드
}
throws 뒤에는 해당 메서드에서 발생할 수 있는 예외 타입을 나열합니다. 이는 다른 개발자를 위한 경고와 같습니다: “주의! 이 메서드는 이런 타입의 예외를 던질 수 있습니다. 대비하세요!”
예시:
public void readFile(String filename) throws FileNotFoundException
{
FileReader reader = new FileReader(filename); // FileNotFoundException을 던질 수 있음
// ...
}
여기서 readFile 메서드는 오류를 직접 처리하지 않고 이렇게 알립니다: “나는 FileNotFoundException을 던질 수 있어 — 나를 호출한 쪽이 무엇을 할지 결정해.”
2. 호출 측 메서드는 어떻게 대응해야 할까?
throws로 선언된 메서드를 호출한다면 두 가지 선택지가 있습니다:
- try-catch로 예외를 처리한다
- 자신의 메서드에서도 예외를 전파한다(throws를 선언에 추가)
옵션 1: try-catch로 처리
public static void main(String[] args)
{
try
{
readFile("data.txt");
}
catch (FileNotFoundException e)
{
System.out.println("파일을 찾을 수 없음: " + e.getMessage());
}
}
여기서는 예외를 잡아 우리가 직접 대응합니다(예: 사용자에게 메시지 출력).
옵션 2: 더 위로 전파
public static void main(String[] args) throws FileNotFoundException
{
readFile("data.txt");
}
이제 오류 처리 책임은 main을 호출하는 쪽(보통 JVM)으로 넘어갑니다 — 예외가 처리되지 않으면 프로그램은 오류 메시지와 함께 종료됩니다.
3. 예시: 예외 전파와 함께 파일 읽기
IOException과 main 수준에서의 처리를 포함한 전체 예시:
import java.io.*;
public class FileDemo
{
// 이 메서드는 IOException을 던질 수 있음을 선언합니다
public static void printFirstLine(String filename) throws IOException
{
BufferedReader reader = new BufferedReader(new FileReader(filename));
String line = reader.readLine();
System.out.println("첫 번째 줄: " + line);
reader.close();
}
public static void main(String[] args)
{
try
{
printFirstLine("nofile.txt");
}
catch (IOException e)
{
System.out.println("파일 읽기 중 오류: " + e.getMessage());
}
}
}
메서드 printFirstLine은 파일이 없을 때 무엇을 해야 할지 모릅니다 — 단지 예외를 전파할 뿐입니다. main에서는 오류를 잡아 메시지를 출력합니다.
4. 유용한 포인트
예외 전파는 언제, 왜 사용할까?
- 메서드가 오류 처리 방식을 결정할 수 없거나 결정하면 안 되는 경우(예: 라이브러리 코드).
- 오류 처리가 문맥에 따라 달라지는 경우(한 경우에는 프로그램 종료, 다른 경우에는 다른 파일 시도).
- 불필요한 try-catch로 코드를 어지럽히지 않기 위해.
모범 사례: 의미 있게 처리할 수 없다면 예외를 전파하세요. 형식상으로만 예외를 잡지는 마세요!
여러 예외를 전파할 수 있습니다
public void process() throws IOException, SQLException
{
// ...
}
Checked와 unchecked 예외: 상기
Checked exceptions(예: IOException, SQLException) — 컴파일러가 처리 또는 전파를 요구합니다.
Unchecked exceptions(예: NullPointerException, IllegalArgumentException) — 컴파일러가 처리를 요구하지 않으므로 throws에 명시하지 않아도 됩니다.
5. 예외 전파에서 흔한 실수
오류 1: checked 예외에 대한 throws 선언을 잊음.
메서드가 checked 예외를 던질 수 있는데 throws에 선언하지도 않고 try-catch로 처리하지도 않으면 — 컴파일러가 오류를 보고합니다.
오류 2: 예외를 잡지만 아무 처리도 하지 않음.
catch (Exception e) {}처럼 작성하는 것은 좋지 않은 관행입니다! 무엇을 해야 할지 모른다면 예외를 전파하는 편이 낫습니다.
오류 3: 너무 일반적인 타입을 전파함.
메서드가 FileNotFoundException만 던질 수 있다면 throws Exception이라고 쓰지 마세요 — 코드를 이해하기 어렵게 만듭니다.
오류 4: unchecked 예외를 throws에 명시함.
throws NullPointerException라고 쓰는 것은 의미가 없습니다 — 컴파일러가 이를 요구하지도 않고, 오류 처리에도 도움이 되지 않습니다.
GO TO FULL VERSION