Recent Posts
Recent Comments
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
관리 메뉴

ㅇ.ㅇ

[Java] try-with-resources (자원 관리 최적화) 본문

Today I Learned

[Java] try-with-resources (자원 관리 최적화)

yun_ 2025. 3. 11. 15:13
반응형

 

 

자바에서 파일, 소켓, 데이터베이스 연결 같은 자원을 사용할 때는 반드시 사용 후에 반환해야 한다. 그렇지 않으면 메모리 누수나 시스템 리소스 낭비로 이어질 수 있다. 작년부터 파일 업로드, 다운로드 기능에 대한 리팩토링을 시간 날 때 조금씩 진행하고 있는데, 그 부분에서 try-with-resources 문법을 사용하였고 이번에 간단히 정리해보려고 한다. 

 

1. try-with-resources란?

try-with-resources는 자원을 자동으로 닫아주는 기능을 제공하는 문법이다. (자바 7부터 제공) 
try 블록에서 사용한 리소스를 finally 없이 자동으로 정리해준다.

기존 try-catch-finally 방식의 문제점 (리소스 수동 닫기)

  • finally에서 close()를 수동으로 호출해야 함.
  • close()에서도 예외가 발생할 가능성이 있어 다시 try-catch가 필요함.
  • 코드가 복잡하고 가독성이 떨어짐.
BufferedReader br = null;

try {
    br = new BufferedReader(new FileReader("test.txt"));
    String line = br.readLine();
    System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (br != null) {
        try {
            br.close();  // 명시적으로 close() 호출
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

try-with-resources 적용 (자동 리소스 정리)

  • finally에서 close()를 호출할 필요 없음 → 코드가 간결해짐
  • 예외가 발생하더라도 try 블록을 벗어나면 자동으로 자원 해제
  • 여러 개의 리소스를 동시에 사용할 수도 있음
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line = br.readLine();
    System.out.println(line);
} catch (IOException e) {
    e.printStackTrace();
}

 

2. 여러 개의 리소스 사용하기

여러 개의 리소스를 try-with-resources에서 처리

  • try 문 안에 여러 개의 리소스;로 구분하여 선언 가능
  • 모든 리소스가 자동으로 닫힘 (닫는 순서는 생성된 역순)
try (
    FileInputStream fis = new FileInputStream("input.txt");
    FileOutputStream fos = new FileOutputStream("output.txt")
) {
    int data;
    while ((data = fis.read()) != -1) {
        fos.write(data);
    }
} catch (IOException e) {
    e.printStackTrace();
}

 

3. try-with-resources 동작 방식

try-with-resources가 어떻게 자동으로 close()를 호출하는지 확인해보자.

AutoCloseable 인터페이스

자바의 AutoCloseable 인터페이스를 구현하면 try-with-resources에서 사용할 수 있다.

class MyResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("리소스가 닫혔습니다.");
    }

    public void use() {
        System.out.println("리소스를 사용 중...");
    }
}

public class Main {
    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            resource.use();
        }
    }
}

// 출력 결과

리소스를 사용 중... 
리소스가 닫혔습니다.
  • AutoCloseable 또는 Closeable을 구현하면 try-with-resources에서 자동으로 닫아줌.
    • 자바의 여러 기본 클래스들은 이미 구현하고 있어서 try-with-resources 사용 가능~! (File, Buffered, Socket, Connection 등등) 개발자가 직접 close()를 호출할 필요 없음.

 

4. 예외 발생 시 처리 방식

try-with-resources의 예외 흐름

class MyResource implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("리소스 닫는 중...");
        throw new RuntimeException("close()에서 예외 발생");
    }

    public void use() {
        System.out.println("리소스 사용 중...");
        throw new RuntimeException("use()에서 예외 발생");
    }
}

public class Main {
    public static void main(String[] args) {
        try (MyResource resource = new MyResource()) {
            resource.use();
        } catch (Exception e) {
            System.out.println("예외 발생: " + e.getMessage());
        }
    }
}

// 출력 결과

리소스 사용 중...
리소스 닫는 중...
예외 발생: use()에서 예외 발생
  • try 블록에서 예외가 발생하면 close()가 실행된 후 catch로 전달됨.
  • close()에서도 예외가 발생하면 Suppress된 예외로 관리됨.

 

숨겨진 예외 (Suppressed된 예외)

  • 자바 7부터 try-with-resources는 close()에서 발생한 예외를 Suppress된 예외로 보관하여 원래 예외를 덮어버리지 않음.
    • 자바의 try-catch-finally에서는 finally 블록에서 예외가 발생하면, try 블록에서 발생한 예외를 덮어버리는(무시하는) 문제가 있다. 즉, finally에서 발생한 예외가 마지막에 실행되므로, 원래 발생했던 예외 정보가 사라지는 것.
    • 자바 7의 try-with-resources는 finally에서 발생한 예외를 덮어버리는 것이 아니라, Suppressed Exception으로 따로 보관한다. 즉, 원래 예외를 유지하면서 close()에서 발생한 예외도 함께 저장하는 방식이다. 
      • Suppressed Exception으로 저장하여 원래 예외를 유지하면서도 getSuppressed()로 확인할 수 있다. 추적이 가능하다는 것!

 

그럼 try-with-resources가 항상 최적의 선택일까?

      • try-with-resources가 항상 최적의 선택일까? 대부분의 경우 코드 가독성과 안정성을 높여주지만, 특정한 상황에서는 기존 방식이 더 적절할 수 있다.

1) try-with-resources 사용하면 좋은 경우

      1. 자원 해제가 필수적인 경우 : 파일 입출력, 데이터베이스 연결, 네트워크 소켓처럼 사용 후 반드시 닫아야 하는 자원을 다룰 때는 try-with-resources가 매우 유용하다.
      2. 예외 처리가 간단한 경우 : 자원을 닫을 때 특별한 처리가 필요하지 않은 경우, try-with-resources를 사용하면 코드가 깔끔해진다.
      3. 여러 개의 자원을 사용할 때 : 다중 자원을 사용할 경우 try-with-resources가 특히 유용하다.

2) try-with-resources 적합하지 않은 경우

      1. 자원을 제어해야 하는 경우 : 어떤 자원은 특정 조건에서만 닫아야 할 수도 있다.
      2. 자원 해제 순서를 직접 제어해야 하는 경우 : try-with-resources는 생성된 역순으로 close()를 호출한다. 하지만 특정한 상황에서는 순서를 명확히 제어해야 할 수도 있다.
      3. 자원이 AutoCloseable을 구현하지 않은 경우 : try-with-resources는 AutoCloseable 인터페이스를 구현한 클래스에서만 동작한다. 만약 AutoCloseable을 구현하지 않은 클래스라면 기존 방식으로 처리해야 한다.
      4. close() 호출 시 예외가 발생해도 무조건 실행해야 하는 경우 : try-with-resources는 close()에서 예외가 발생하면 이를 Suppress된 예외로 관리한다. 하지만 어떤 경우든 close() 실행이 보장되어야 한다면 기존 방식이 더 안전할 수 있다

 

정리

  • try-with-resources는 자원 관리를 자동화하는 기능으로, 코드 가독성을 높이고 예외 처리를 간결하게 해준다.
  • AutoCloseable 또는 Closeable을 구현한 객체에서 사용할 수 있다.
  • 여러 개의 리소스를 동시에 관리할 수 있으며, 생성된 역순으로 close()가 호출됨.
  • 예외가 발생해도 자원이 자동으로 닫히며, close()의 예외는 Suppressed Exception으로 관리됨.

 

반응형

'Today I Learned' 카테고리의 다른 글

[Git] Stash  (0) 2025.03.23
[Java] META-INF 디렉토리와 MANIFEST.MF  (0) 2025.03.12
[DEV] SBOM에 대하여  (0) 2025.03.09
[Oracle] Oracle에서 NULL 값 다루기  (0) 2025.03.09
[DEV] SSO  (0) 2025.03.02