애플리케이션에서 예기치 못한 상황(에러)이 발생하더라도 애플리케이션이 중단되지 않고 실행될 수 있도록 제어.
예시 상황: 사용자가 잘못된 데이터를 입력하거나 네트워크가 불안정할 경우.
리소스 낭비 방지:
에러 발생 시 조치를 취하지 않으면 애플리케이션이 메모리 누수나 과부하로 인해 서버가 중단될 가능성이 있음.
코드 가독성 향상:
정상 흐름과 예외 흐름을 분리하여 코드의 가독성 및 유지보수성을 높임.
디버깅 및 로그:
발생한 에러를 기록(logging)하여 디버깅에 도움을 줌.
로그를 통해 문제의 원인을 추적 가능.
2. 예외 처리 예제
(1) 기본적인 예외 처리: 정상 흐름 유지
코드 예시
public class ExceptionExample {
public static void main(String[] args) {
try {
int result = divide(10, 0); // 0으로 나누기 시도
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: Division by zero is not allowed."); // 에러 처리
}
System.out.println("Program continues...");
}
public static int divide(int a, int b) {
return a / b; // ArithmeticException 발생 가능
}
}
실행 결과
Error: Division by zero is not allowed.
Program continues...
(2) 에러 발생 시 작업 누적의 문제: 메모리 누수 예제
설명: 예외를 처리하지 않고 계속 실행하면 메모리 누수가 발생할 수 있음.
코드 예시
import java.util.ArrayList;
import java.util.List;
public class MemoryLeakExample {
public static void main(String[] args) {
List<String> memory = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
try {
memory.add("Data: " + i);
if (i % 1000 == 0) {
throw new RuntimeException("Simulated exception");
}
} catch (RuntimeException e) {
System.out.println("Exception caught: " + e.getMessage());
}
}
System.out.println("Completed without fixing the memory issue.");
}
}
실행 결과
Exception caught: Simulated exception
Exception caught: Simulated exception
...
Completed without fixing the memory issue.
문제점: 리스트에 데이터가 계속 쌓이지만, 에러 발생 후 제거되지 않아 메모리 누수가 발생할 가능성이 있음.
(3) 서버 과부하 및 메모리 누적 문제
상황: 서버가 며칠마다 죽는 문제 발생 → 로그 확인 시 메모리 누적 또는 디스크 용량 부족.
코드 예시
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class ServerExample {
public static void main(String[] args) {
while (true) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("logs.txt", true))) {
writer.write("Processing request...\n");
simulateError(); // 에러 발생 가능
} catch (IOException e) {
System.out.println("I/O error occurred: " + e.getMessage());
} catch (RuntimeException e) {
System.out.println("Application error occurred: " + e.getMessage());
}
}
}
public static void simulateError() {
if (Math.random() > 0.9) { // 10% 확률로 에러 발생
throw new RuntimeException("Simulated runtime error");
}
}
}
(4) 정상 흐름과 예외 흐름의 혼합 문제
문제: 정상 흐름과 예외 흐름이 섞이면 코드 이해가 어려워짐.
반환값으로 에러를 처리하는 코드
public class ReturnValueExample {
public static void main(String[] args) {
String result = divideWithReturn(10, 0);
if (result.equals("Error")) {
System.out.println("Division failed.");
} else {
System.out.println("Result: " + result);
}
}
public static String divideWithReturn(int a, int b) {
if (b == 0) {
return "Error";
}
return String.valueOf(a / b);
}
}
예외 처리를 활용한 코드
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Error: Division by zero is not allowed.");
}
}
public static int divide(int a, int b) {
return a / b;
}
}
차이점:
반환값 방식: 에러와 정상 데이터가 동일한 데이터 흐름을 공유하여 혼란 발생.
예외 처리 방식: 에러 흐름과 정상 흐름을 분리하여 가독성 향상.
3. 예외 처리에서 로그 활용
로그 기록의 중요성:
애플리케이션 실행 중 발생한 에러를 기록하여 디버깅에 도움을 줌.
로그는 운영 환경에서 문제를 진단하는 주요 도구.
코드 예시: 로그 기록
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
public class LoggingExample {
private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());
public static void main(String[] args) {
try {
FileHandler fileHandler = new FileHandler("application.log", true);
fileHandler.setFormatter(new SimpleFormatter());
logger.addHandler(fileHandler);
int result = divide(10, 0); // 에러 발생
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
logger.severe("Error: Division by zero. " + e.getMessage());
} catch (IOException e) {
logger.severe("Failed to set up logging. " + e.getMessage());
}
}
public static int divide(int a, int b) {
return a / b;
}
}