[JAVA] 효과적인 예외 처리 전략
Java에서 예외 처리는 프로그램의 안정성과 유지보수성에 큰 영향을 미친다.
이 글에서는 효과적인 예외 처리 전략과 실제 적용 방법에 대해 알아본다.
체크 예외 vs 언체크 예외
체크 예외 (Checked Exception)
- 컴파일 시점에 처리가 강제되는 예외
- 복구가 가능한 상황에서 사용
- 예: IOException, SQLException
public void readFile(String path) throws IOException {
try (FileReader reader = new FileReader(path)) {
// 파일 읽기 로직
} catch (IOException e) {
// 구체적인 에러 처리
throw new FileProcessingException("파일 처리 중 오류 발생", e);
}
}
언체크 예외 (Unchecked Exception)
- RuntimeException을 상속
- 프로그램 오류를 나타내는 경우 사용
- 예: NullPointerException, IllegalArgumentException
public void processUser(User user) {
if (user == null) {
throw new IllegalArgumentException("사용자 정보가 null일 수 없습니다.");
}
// 처리 로직
}
커스텀 예외 설계
비즈니스 로직에 특화된 예외는 커스텀 예외로 만드는 것이 좋다.
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public BusinessException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public ErrorCode getErrorCode() {
return errorCode;
}
}
// 사용 예시
public class OrderService {
public void placeOrder(Order order) {
if (order.getAmount() <= 0) {
throw new BusinessException(
ErrorCode.INVALID_ORDER_AMOUNT,
"주문 금액은 0보다 커야 합니다."
);
}
}
}
예외 처리 비용
예외 처리는 성능에 영향을 미칠 수 있으므로 적절한 사용이 중요하다.
나쁜 예시
public boolean isNumeric(String str) {
try {
Integer.parseInt(str);
return true;
} catch (NumberFormatException e) {
return false;
}
}
좋은 예시
public boolean isNumeric(String str) {
if (str == null || str.isEmpty()) {
return false;
}
return str.matches("\\d+");
}
로깅 전략
효과적인 로깅은 문제 해결의 핵심이다.
로그 레벨 가이드라인
public void processOrder(Order order) {
logger.debug("주문 처리 시작: {}", order.getId()); // 상세 디버깅용
try {
validateOrder(order);
logger.info("주문 검증 완료: {}", order.getId()); // 중요 비즈니스 이벤트
processPayment(order);
logger.info("결제 처리 완료: {}", order.getId());
} catch (PaymentException e) {
logger.error("결제 처리 실패: {}", order.getId(), e); // 심각한 오류
throw e;
}
}
예외 처리 체인
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger =
LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(
BusinessException e) {
logger.warn("비즈니스 예외 발생: {}", e.getMessage());
return ResponseEntity
.badRequest()
.body(new ErrorResponse(e.getErrorCode(), e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
logger.error("예상치 못한 오류 발생", e);
return ResponseEntity
.internalServerError()
.body(new ErrorResponse(
ErrorCode.INTERNAL_SERVER_ERROR,
"서버 내부 오류가 발생했습니다."
));
}
}
실전 예외 처리 전략
- 예외 계층 구조 설계
- 최상위 비즈니스 예외 정의
- 세부 예외는 이를 상속
- 일관된 예외 처리 가능
- 예외 전환
- 하부 기술의 예외를 비즈니스 예외로 변환
- 추상화 계층 유지
- 예외 처리 일원화
- 리소스 정리
- try-with-resources 적극 활용
- 명시적인 리소스 해제
- 메모리 누수 방지
'개발&프로그래밍' 카테고리의 다른 글
[Python] NumPy 배열 연산 마스터하기 (0) | 2024.11.18 |
---|---|
[JAVA] ThreadLocal 제대로 사용하기 (0) | 2024.11.17 |
[JAVA] Java의 String Pool과 문자열 최적화 (2) | 2024.11.15 |
[JAVA] 가비지 컬렉션 동작 원리와 모니터링 방법 (1) | 2024.11.14 |
[IntelliJ IDEA] 코드 리팩토링을 도와주는 플러그인 TOP 5 (0) | 2024.11.05 |
댓글