CS

CS 스터디 (Java) 1 - try with resources를 설명해주세요.

gentle-tiger 2025. 5. 12. 18:26

try-with-resources문법은 Java 7에서 안전한 자원관리를 위해 도입된 기능으로, try 블록이 종료될 때 해당 자원이 AutoCloseable 인터페이스를 구현하고 있으면 자동으로 close() 메서드가 호출되어 자원이 해제됩니다.

 

try-with-resources 문법은 어느 정도 이해했지만, 두 가지 궁금증이 생겼습니다.

 

1. 자원관리의 중요성

 

2. 이전 지원관리 방법과 그 차이점

 

1. 자원관리의 중요성

자원관리란? 

: 자원관리란, 애플리케이션 실행 중에 사용하는 시스템 자원을 할당하고, 사용이 끝난 뒤 이를 적절하게 해제하여 시스템 자원의 낭비나 누수를 방지하는 작업을 의미합니다.

 

1). Garbage Collection의 한계

- JavaGC(Garbage Collection)는 heap 메모리 객체만 수집하기 때문에, 외부 자원의 경우 GC 대상이 아니기에 명시적으로 해제해야 합니다. 

 

외부 자원의 종류 

  • 파일 핸들 (FileInputStream, FileReader 등)
  • 네트워크 소켓 (Socket, ServerSocket 등)
  • JDBC 커넥션, Statement, ResultSet
  • 쓰레드 풀, Timer, ExecutorService
  • NIO 버퍼, 채널 등

 

2). Java 플랫폼과 운영체제 종속 자원 사이의 간극 

- Java는 플랫폼 독립적인 언어이지만, 실제로 다루는 자원들은 대부분 운영체제에 종속되어 있습니다다. 

 

  • 파일 핸들: OS의 파일 디스크 자원
  • DB 커넥션: DB 서버와의 TCP 커넥션
  • 스레드: OS 쓰레드 풀 자원

- 이 자원들은 운영체제 수준에서 개수 제한이 있고, 점유 시간이 길수록 시스템 안정성 저하로 이어질 수 있습니다. 

📌 파일 핸들을 닫지 않으면 java.io.FileNotFoundException: Too many open files
📌 DB 커넥션을 반환하지 않으면 Timeout waiting for connection from pool

※ 락(lock) 사용 경험이 있다면, 자원 점유 시간이 길어졌을 때 발생할 수 있는 시스템 안정성 문제(예: 데드락)를 고려해본 적이 있을 것입니다.

 

 

3). 장시간 실행되는 서버 애플리케이션 특성

- Java는 실행 환경이 장시간 이어기지는 환경(웹서버, API 게이트웨이, 배치 처리, 메시징 서버 등)으로 자원 누수가 누적되면 시스템 장애로 직결됩니다. 

 

 

4). 멀티스레드 환경에서의 자원 정리 복잡성

- Java는 멀티스레드 처리를 지원하기 때문에, 자원을 여러 쓰레드에서 공유하는 구조가 일반적입니다. 이때 하나의 쓰레드에서 예외가 발생해 자원이 닫히지 않으면, 다른 쓰레드까지 영향을 받을 수 있습니다. 따라서 병렬 실행 환경에서는 자원 해제 로직이 반드시 필요합니다.

 

 

2. 이전 Java에서 자원관리 방법과 그 차이점

  try-finally 방식(Java 6 이하) try-with-resources 방식 (Java 7 이상)
자원 해제 수동 자동
예외 관리 원본 예외 정보가 사라질 수 있음. 원래 예외와 함께 전달
코드 가독성 중첩 try-catch 작성으로 가독성 낮음. 선언부에 자원을 나열하므로,
boilerplate 코드가 줄어들어
상대적으로 가독성 높음.
지원 범위 제약 없음 AutoCloseable 인터페이스 구현체만
사용 가능
블록 스코프 try 블록 외의 변수도 참조 가능 try 블록 내의 변수만 참조 가능

 

1) try-finally 방식 (Java 6 이하)

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    // 처리 로직
} catch (IOException e) {
    // 예외 처리
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // close() 중 발생한 예외 처리
        }
    }
}

- close() 중 예외가 발생하면 원래 발생한 예외가 덮여집니다. (suppressed exception 손실)

 

 

2) try-with-resources 방식

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    String line = reader.readLine();
    // 처리 로직
} catch (IOException e) {
    // 예외 처리
}

- AutoCloseable 또는 Closeable 구현체만 사용할 수 있습니다. 

- try 블록이 끝날 때 자원이 자동으로 close() 호출됩니다. 

 

 

기존에 Closeable 구현체를 try–finally로도 관리할 수 있었지만, 자동 자원 해제 외에 어떤 기능이 필요했을까? 

 

AutoCloseable 과 Closeable 의 차이

  Closeable AutoCloseable
자원 관리 I/O 스트림 대상(IOExcetion 한정) 모든 종류의 외부 자원(Exception)
(데이터베이스 커넥션, 스레드 풀, 사용자 정의 리소스 등)
예외 서명 Closeable.close() throws IOException AutoCloseable.close() throws Exception

- 자원 관리 범위에 차이가 있지만 Closeable은 AutoCloseable의 하위 인터페이스이기 때문에, Close 구현체도 당연히 자동으로 close()가 호출됩니다. 

- 즉, 두 인터페이스 예외 서명과 관리 범위만 다를 뿐, 자동 자원 해제 측면에서는 동일하게 작동합니다. 

 

 

AutoCloseable 한계점 

  • AutoCloseable 구현체만 지원합니다. 다른 유형의 자원에는 적용할 수 없습니다.
    → implements AutoCloseable을 해야만 자동 해제 대상이 됩니다.
  • 리소스 변수는 try 헤더 안에서만 유효합니다. 블록 외부에서 재사용하기 어렵습니다.
    → finally 바깥이나 다른 블록에서 재사용하려면 수동 선언·해제 방식이 필요합니다.
  • suppressed 예외를 명시적으로 꺼내 처리하지 않으면 누락될 수 있습니다.
    → 자동 해제는 되지만, getSuppressed()를 호출해야만 close() 중 발생한 예외를 확인할 수 있습니다.
  • 복잡한 해제 로직(조건부 또는 순서 제어 등)을 구현할 때는 여전히 수동 try–finally가 필요합니다.
    → close 순서를 상황별로 제어하거나, 특정 조건일 때만 해제하려면 기존 방식이 더 유연합니다.

 

 

결론 

Java는 GC의 한계, 운영체제 종속성, 장시간 실행되는 서버 환경, 멀티스레드 특성 등으로 자원 관리가 매우 중요하며, Java 7에서 안전한 자원 관리를 위해 try-with-resources 문법이 추가되었습니다. 이 문법은 try 블록이 종료될 때 해당 자원이 AutoCloseable 인터페이스를 구현하고 있으면 자동으로 close() 메서드를 호출해 외부 자원 등 모든 자원을 해제합니다.

 

 

참고 

https://f-lab.kr/insight/java-resource-management-20241209

https://mangkyu.tistory.com/217