본문으로 건너뛰기
개발 머꼬
개발 노트Java
hohyeon.dev19

try-with-resources에서 예외가 사라진 것처럼 보이는 이유

  • #Engineering Note
  • #Exception Handling
  • #Java

문제 발생

파일을 읽다가 예외가 발생했는데, 로그에는 다른 예외(리소스를 닫을 때 발생한 예외)만 찍히고 원래 원인이 보이지 않는 경우가 있습니다.

try (FileInputStream fis = new FileInputStream(path)) {
    // 여기서 IOException 발생
    process(fis);
} // close()에서도 IOException 발생 가능

원인 분석

try-with-resources는 블록이 끝날 때 자동으로 close()를 호출합니다. 만약 try 블록 본문에서 예외가 발생한 뒤, close() 호출에서도 예외가 발생하면 어느 쪽을 던질지 결정해야 합니다.

Java는 try 블록 본문에서 발생한 예외를 "주 예외(primary exception)"로 던지고, close()에서 발생한 예외는 그 주 예외에 **Suppressed Exception(억제된 예외)**로 첨부합니다. catch 블록에서 e.printStackTrace()나 기본 스택 트레이스 출력은 suppressed exception도 함께 보여주지만, 로깅 프레임워크나 커스텀 예외 처리 코드에서 e.getMessage()만 뽑아 쓰면 이 정보가 조용히 사라집니다.

try {
    // ...
} catch (Exception e) {
    log.error(e.getMessage()); // suppressed exception 정보 유실
}

해결 방안

  1. 예외 객체 전체를 로깅 프레임워크에 넘깁니다 (log.error("...", e) 형태) — 대부분의 로깅 프레임워크는 스택 트레이스와 suppressed exception을 함께 출력합니다.
  2. 직접 suppressed exception을 확인해야 한다면 Throwable.getSuppressed()로 배열을 순회할 수 있습니다.
catch (Exception e) {
    log.error("주 예외: {}", e.getMessage());
    for (Throwable suppressed : e.getSuppressed()) {
        log.error("억제된 예외: {}", suppressed.getMessage());
    }
}
  1. close()가 예외를 던지는 리소스(파일, 네트워크 연결 등)를 다룰 때는 애초에 이 메커니즘이 있다는 걸 인지하고 있는 것만으로도 디버깅 시간이 크게 줄어듭니다 — "예외가 두 개 났는데 로그엔 하나만 보인다"는 사실 자체가 흔한 원인 불명 버그의 원천입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.