예외처리(Exception Handling) 은 프로그래밍에 안정성을 높일 때 가장 중요한 기능이라고 할 수 있다. 이번 포스팅에서는 예외 처리가 왜 필요한지, 자바에서는 어떻게 예외 계층을 관리해주고 있는지, Check Exception 과 Unchecked Exception 에 대해서 설명하고자 한다.
왜 예외 처리가 필요할까?
예외 처리가 필요한 이유는 I/O 작업과 같이 외부 시스템에 의존하는 작업에서 예상하지 못하는 오류가 발생했을 때, 빠르게 대응하기 위함이다. 예를 들어 네트워크 통신 과정에서 (1) 메시지를 전송 도중 (2) 네트워크 연결 과정에서 등등 실패할 수 있는 상황들이 다양하다.
package exception.ex1;
public class NetworkService {
public void sendMessage(String data) {
String address = "http://example.com/";
NetworkClient client = new NetworkClient(address);
client.initError(data);
String connectResult = client.connect();
if (isError(connectResult)) {
System.out.println("[네트워크 오류 발생] 오류 코드: " + connectResult);
} else {
String sendResult = client.send(data);
if (isError(sendResult)) {
System.out.println("[네트워크 오류 발생] 오류 코드: " + sendResult);
}
}
client.disconnect();
}
private static boolean isError(String result) {
return !result.equals("success");
}
}
위 코드에서 "connect()" 와 "send()" 의 성공 여부를 확인하고 적절한 오류를 처리하는 방법을 소개해주고 있다. 이렇게 코드를 작성하면 코드가 복잡해지고, 가독성이 떨어지며, 오류 흐름과 정상 흐름이 혼재되어 디버그가 매우 어려운 상황에 놓이게 될 가능성이 높다. 이런 상황에서 예외 클래스를 사용하면 매우 도움이 된다.
예외 처리의 장점
자바 예외 처리 기능을 사용하면 아래와 같은 장점을 확인해볼 수 있다.
- 정상 흐름과 오류 흐름을 분리해서 코드를 작성할 수 있음
- 가독성 향상
- 오류 발생 시, 프로세스의 중단을 막고 클라이언트에게 적절한 응답 코드를 전달해 줄 수 있다
예외 기본 규칙
자바에서 예외는 객체로 취급되며, 최상위 클래스는 `Throwable`이다. Throwable을 상속하는 두 주요 클래스는 Exception과 Error이다. 일반적으로 개발자는 Error를 처리하지 않으며, Exception 하위에서 체크 예외와 언체크 예외를 구분한다.
예외 기본 규칙
- 예외는 잡아서 처리하거나 던져야 한다
- 예외를 잡거나 던질 때, 지정한 예외뿐만 아니라 그 예외의 하위 클래스들도 함께 처리할 수 있다
체크 예외 (Checked Exception)
체크 예외는 Exception을 상속받은 예외를 의미한다. 예를 들어, 커스텀 예외 MyCheckedException을 아래와 같이 정의할 수 있다.
public class MyCheckedException extends Exception {
public MyCheckedException(String message) {
super(message);
}
}
public class Client {
public void call() throws MyCheckedException {
throw new MyCheckedException("체크 예외 발생");
}
}
여기서 Client.call() 메서드는 throws 키워드를 사용하여 MyCheckedException을 던질 것임을 명시한다. 호출하는 측에서는 이 메서드에서 발생할 수 있는 예외를 처리해야 한다.
체크 예외 처리 예제
public void callWithExceptionHandling() {
Client client = new Client();
try {
client.call();
} catch (MyCheckedException e) {
System.out.println("예외 처리 로직: " + e.getMessage());
}
System.out.println("정상 흐름");
}
위와 같이 예외가 발생하면 Catch block 에서 (1) 예외를 밖으로 전달할 지 (2) 바꿔서 던질 지 (3) 무시할 지 결정할 수 있다
언체크 예외(Unchecked Exception)
언체크 예외는 RuntimeException을 상속받은 예외로, 반드시 처리하거나 throws 키워드를 사용하지 않아도 된다. 예를 들어, NullPointerException과 같은 예외가 언체크 예외의 예시이다.
public void riskyMethod() {
throw new NullPointerException("널 포인터 예외 발생");
}
public void handleRiskyMethod() {
try {
riskyMethod();
} catch (NullPointerException e) {
System.out.println("예외 처리 로직: " + e.getMessage());
}
System.out.println("정상 흐름");
}
실무에서는 체크 예외보다는 언체크 예외를 훨씬 많이 사용한다. 이는 언체크 예외가 개발자의 실수를 줄이는 데 도움이 되고, 예외 처리가 덜 번거롭기 때문이다. 예외 처리에 대한 이해는 프로그램의 안정성과 가독성을 높이는데 기여한다. 항상 적절한 예외 처리를 통해 안전한 코드를 작성하도록 노력해야한다.
'Java' 카테고리의 다른 글
Java Thread & Runnable 정리 (0) | 2025.02.07 |
---|---|
Process & Thread 역할 정리 (0) | 2025.02.07 |
Java 에서 중첩 클래스 (1) | 2025.02.03 |
Java 에서 Time 정리 (0) | 2025.02.03 |
Java 에서 Enum (1) | 2025.02.02 |