Java에서 예외(Exception) 처리를 할 때 흔히 사용하는 try~catch 문.
이걸 사용하면 아무래도 강제 종료의 위험성은 떨어진다.
하지만 남용할 때의 부작용은 없을까?
1. 예외(Exception)이란?
예외(Exception)는 프로그램 실행 중에 발생할 수 있는 비정상적인 상황을 나타내는 객체이다.
프로그램의 일반적인 흐름을 방해하며, 적절히 처리하지 않으면 프로그램이 비정상적으로 종료될 수 있다.
그렇기에 Java에서는 예외를 처리하기 위해 try~catch~finally 블록을 사용한다.
예외는 크게 두 가지로 분류할 수 있다.
1-(1) Checked Exception
Checked Exception은 컴파일 시점에서 체크되는 예외로,
반드시 try~catch 블록을 사용하여 처리하거나 throws 키워드를 사용해 선언해야 한다.
IOEException, SQLException, ClassNotFoundException 등과 같이 파일, 네트워크 등과의 입출력에서 발생하는 예외가 여기에 해당된다.
1-(2) Unchecked Exception
Unchecked Exception은 런타임 시점에서 발생하는 예외로, 컴파일러가 체크하지 않는다.
NullPointerException, ArrayIndexoutOfBoundsException, illegalArgumentException 등과 같이 프로그래밍 오류에서 발생하는 예외가 여이게 해당된다.
2. try~catch~finally 사용법
- try : 예외가 발생할 가능성이 있는 코드를 포함
- catch : 발생한 예외를 처리
- finally : 예외 발생 여부와 관계 없이 항상 실행되는 코드
try {
// 예외가 발생할 가능성이 있는 코드
int result = 10 / 0;
} catch (ArithmeticException e) {
// 예외 처리 코드
System.out.println("ArithmeticException 발생: " + e.getMessage());
} finally {
// 항상 실행되는 코드
System.out.println("Finally 블록 실행");
}
프로그래밍을 하다보면 예외 처리를 필수로 해야하는 경우가 있다.
대표적인 예가 바로 예외의 전파이다.
메서드 내에서 예외가 발생하면, 해당 예외는 호출한 메서드로 전파될 수 있다.
throws 키워드를 사용하여 예외를 선언한 메서드를 호출하면 반드시 try~catch로 예외를 처리해야 한다.
public void readFile() throws IOException {
FileInputStream file = new FileInputStream("test.txt");
// 파일 읽기 코드
}
try {
readFile();
} catch (IOException e) {
e.printStackTrace();
}
3. 예외 처리가 중요한 이유
예외 처리는 프로그램의 안정성을 높이고, 예기치 않은 상황에서도 프로그램이 올바르게 동작할 수 있도록 돕는다.
- 프로그램의 비정상 종료 방지: 예외 발생 시 프로그램이 비정상적으로 종료되는 것을 방지
- 디버깅 용이성: 예외 메시지와 스택 트레이스를 통해 문제의 원인을 쉽게 파악 가능
- 사용자 경험 향상: 예외 발생 시 사용자에게 적절한 메시지를 제공함으로써 사용자 경험을 향상
위와 같은 이점으로 볼 때, 적절한 예외 처리를 통해 프로그램의 안정성과 신뢰성을 높일 수 있다.
4. 예외 처리가 중요한 건 알겠어. 근데 남용해도 되는거야?
예외 처리는 프로그램의 안정성을 높이는 중요한 기법이지만, 남용할 경우 성능 저하 및 코드 가독성 문제가 일어날 수 있다. 또한 catch 문에서 적절한 대응을 해주지 않은 경우, 문제 원인을 파악하기 어려워진다.
- 성능 저하: 예외 발생 시 스택 트레이스를 생성하고 예외 객체를 생성하는 과정은 상당한 비용이 든다. 빈번한 예외 발생은 성능 저하를 초래할 수 있습니다.
- 코드 가독성 감소: 지나치게 많은 try-catch 블록은 코드의 가독성을 낮추고 유지보수를 어렵게 만든다.
- 오류 은폐: 모든 예외를 포괄적으로 처리하면 실제 문제의 원인을 파악하기 어려워질 수 있다. 특히, 예외를 잡고 아무런 조치를 취하지 않거나 로그만 남기는 경우.
5. 그럼 예외처리는 어떻게 사용해야 적절할까?
5-(1) 예외는 예외적인 상황에서만 사용할 것
일반적인 흐름 제어를 위해 예외를 사용하면 안된다.
예외는 진짜 예외적인 상황에서만 사용해야 한다.
// 나쁜 예: 예외를 사용한 흐름 제어
try {
int value = Integer.parseInt("abc"); // NumberFormatException 발생
} catch (NumberFormatException e) {
// 예외 발생 시 다른 값 사용
value = 0;
}
5-(2) 입력 검증을 통한 예외 방지
가능하다면 예외가 발생하기 전에 입력을 검증하여 예외를 방지할 것!
// 좋은 예: 입력 검증을 통한 예외 방지
String input = "abc";
if (input.matches("\\d+")) {
int value = Integer.parseInt(input);
} else {
value = 0; // 기본값 사용
}
5-(3) 특정 예외만 잡기
포괄적인 Exception을 잡기보다는 특정 예외를 잡아 처리해야 한다.
이렇게 하면 실제로 발생한 예외의 원인을 더 잘 파악할 수 있다.
Exception 하나만으로 퉁치는 예외처리는 가장 최악 !!
try {
// 코드 실행
} catch (NullPointerException e) {
// NullPointerException 처리
} catch (IOException e) {
// IOException 처리
} catch (Exception e) {
// 기타 예외 처리
}
5-(4) 예외 정보 로깅 및 재발생
예외 발생 시 로그를 남기고 필요한 경우 예외를 재발생시켜 호출자에게 문제를 알려야 한다.
try {
// 코드 실행
} catch (IOException e) {
// 예외 정보 로깅
logger.error("IOException 발생: " + e.getMessage());
// 예외 재발생
throw e;
}
5-(5) 자원 해제는 반드시 finally 또는 try-with-resources 사용
파일, 데이터베이스 연결 등 자원을 사용하는 경우, 예외 발생 여부와 관계없이 자원을 해제해야 한다.
이를 위해 finally 블록이나 try-with-resources 문법을 사용할 것.
// try-with-resources 사용 예
try (FileInputStream fileInputStream = new FileInputStream("test.txt")) {
// 파일 읽기 코드
} catch (IOException e) {
// 예외 처리
}
6. 결론
예외 처리는 프로그램의 안정성을 높이는 데 필수적이지만, 남용하면 성능 저하 및 코드 가독성 문제를 일으킬 수 있다.
따라서 예외 처리는 진짜 예외적인 상황에서만 사용하고, 입력 검증을 통해 예외를 미리 방지하며, 특정 예외를 잡아 처리하는 것이 좋다.
또한, 자원 해제는 반드시 보장되도록 finally 블록이나 try-with-resources 문법을 사용해야 한다.
이러한 가이드라인을 따르면 보다 안정적이고 유지보수하기 쉬운 코드를 작성할 수 있다.
'개발 > 개념 및 기법' 카테고리의 다른 글
Kotlin Multiplatform (KMP) (0) | 2025.01.24 |
---|---|
JVM이란? (1) | 2025.01.23 |
[개발 기본 개념]비트 연산자 (0) | 2023.05.26 |
참조 (1) | 2023.02.24 |
Interface(인터페이스) (0) | 2021.07.26 |