[JAVA] try-with-resources 정리

2025. 1. 19. 22:11·BackEnd/JAVA

1. try-with-resources란?

try-with-resources는 자원을 자동으로 관리하기 위한 구문으로, 자원을 명시적으로 닫지 않아도 자동으로 해제됩니다. 파일, 네트워크 소켓, 데이터베이스 연결과 같은 자원들은 반드시 사용 후 닫혀야 합니다. 자바 7에서 도입된 try-with-resources는 이러한 자원을 자동으로 해제하는 구조를 제공합니다.

1.1 자원(Resource)이란?

자바에서 자원이란 사용 후 반드시 해제해야 하는 외부 시스템과의 연결을 의미합니다. 파일, 데이터베이스 연결, 네트워크 소켓 등이 자원에 해당합니다. 자원을 닫지 않으면 메모리 누수나 시스템 리소스 고갈 문제가 발생할 수 있습니다.

1) 자원의 종류

  • 파일: 파일을 읽고 쓰기 위한 FileReader, BufferedReader 등
  • 데이터베이스 연결: Connection, Statement 등의 데이터베이스 관련 자원
  • 네트워크 소켓: 네트워크 통신을 위한 Socket 객체

자원은 사용 후에 반드시 닫아야 하며, 닫지 않으면 리소스 누수로 이어져 시스템 성능에 영향을 미칠 수 있습니다.

1.2 try-with-resources의 기본 규칙

  • try-with-resources 구문에 사용되는 자원은 반드시 AutoCloseable 또는 Closeable 인터페이스를 구현해야 합니다.
  • try 블록이 끝나면 자동으로 close() 메서드가 호출되어 자원을 해제합니다.
  • 한 번 선언된 자원은 try 구문이 끝날 때 자동으로 닫히기 때문에 수동으로 자원을 해제하는 과정에서 발생할 수 있는 오류를 방지할 수 있습니다.

2. try-catch-finallly와 try-with-resources 구문 비교

2.1 기존 자원 관리 방식 (try-catch-finally)

자바 7 이전에는 자원을 해제하려면 try-catch-finally 구문에서 자원을 수동으로 닫아야 했습니다. 자원을 해제하지 않으면 메모리 누수나 시스템 오류가 발생할 수 있기 때문에, finally 블록에서 반드시 close() 메서드를 호출해야 했습니다.

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(); // 자원을 반드시 닫아야 함
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}
  • 자원을 닫는 것을 잊을 수 있음: finally 블록에서 자원을 닫지 않으면 리소스 누수가 발생할 수 있습니다.
  • 코드 복잡도: 자원이 여러 개일 경우, 각각의 자원에 대해 null 확인 및 close() 호출을 반복해야 합니다.
  • 중첩된 예외 처리의 어려움: 자원 해제 중 발생한 예외와 원래 발생한 예외를 동시에 처리하기 어렵습니다.

2.2 try-with-resources 구조

자바 7부터 도입된 try-with-resources는 자원을 자동으로 닫아주는 구조입니다. try 구문 안에서 자원을 생성하면, try 블록이 끝나면 자동으로 자원을 닫습니다. 이 덕분에 finally 블록에서 자원을 닫는 작업을 수동으로 할 필요가 없습니다.

try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    // 파일을 읽는 로직
    String line = br.readLine();
    System.out.println(line);
} catch (IOException e) {
    // 예외 처리
    e.printStackTrace();
}
  • 자원의 자동 해제: try 블록이 끝나면 자원이 자동으로 닫힙니다.
  • 코드 간결성: finally 블록에서 수동으로 자원을 닫을 필요가 없어 코드가 간결해집니다.
  • 안정성: 자원 해제 중 발생하는 예외를 억제된 예외로 처리할 수 있습니다.

3. Closeable과 AutoCloseable의 관계

try-with-resources에서 사용할 수 있는 자원은 반드시 AutoCloseable 또는 Closeable 인터페이스를 구현해야 합니다. 두 인터페이스 모두 자원을 닫을 수 있는 메서드를 제공하지만, AutoCloseable은 자바 7에서 추가되었고, Closeable은 자바 5부터 존재합니다.

3.1 AutoCloseable 인터페이스

  • AutoCloseable은 자바 7에서 도입된 인터페이스로, 자원을 닫기 위한 close() 메서드를 정의합니다.
  • AutoCloseable을 구현한 객체는 try-with-resources 구문에서 사용할 수 있습니다.
public interface AutoCloseable {
    void close() throws Exception;
}

3.2 Closeable 인터페이스

  • Closeable은 자바 5에서 도입된 인터페이스로, I/O 자원을 닫기 위한 close() 메서드를 정의합니다.
  • 자바 7에서 AutoCloseable이 새롭게 등장한 후, 기존의 Closeable 인터페이스에 AutoCloseable을 상속받는 구조가 추가되었습니다.변경된 이유는 try-with-resources 구문을 사용하면서 기존에 있던 Closeable을 포함하여 모든 자원을 자동으로 닫을 수 있도록 하기 위해서입니다.
public interface Closeable extends AutoCloseable {
    void close() throws IOException;
}

3.3 차이점

  • AutoCloseable은 모든 자원에 적용 가능한 범용적인 인터페이스로 Exception을 던질 수 있습니다.
  • Closeable은 I/O 작업에 특화된 인터페이스로, IOException만 던질 수 있습니다. 주로 파일 입출력에 사용됩니다.

4. try-with-resources를 사용해야 하는 이유

4.1 자원 해제의 안정성

try-with-resources를 사용하면 자원을 안전하게 해제할 수 있습니다. 자원 해제 코드를 수동으로 작성할 때 발생할 수 있는 실수를 줄일 수 있으며, 예외가 발생하더라도 자원이 자동으로 해제됩니다.

1) 중첩된 예외 처리

자원 해제 시 발생한 예외와 원래의 예외를 모두 처리할 수 있습니다. 기존 finally 블록에서는 자원을 닫는 과정에서 새로운 예외가 발생하면 원래 예외가 가려지는 문제가 있었습니다.

try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
    String line = br.readLine();  // 예외 발생 가능
    System.out.println(line);
} catch (IOException e) {
    // 예외 처리
    e.printStackTrace();
} // 자원이 자동으로 해제됨

4.2 코드의 간결성

try-with-resources를 사용하면 자원을 닫는 finally 블록을 작성할 필요가 없으므로 코드가 간결해지고 가독성이 높아집니다. 여러 자원을 사용할 때도 하나의 try 구문에서 관리할 수 있습니다.

1) 여러 자원을 사용하는 경우

try (BufferedReader br = new BufferedReader(new FileReader("test.txt"));
     FileWriter fw = new FileWriter("output.txt")) {
    String line = br.readLine();
    fw.write(line);
} catch (IOException e) {
    e.printStackTrace();
}
// 두 자원 모두 자동으로 해제됨

5. 예시를 통해 알아보기

5.1 try-catch-finally를 사용할 경우 (자원 해제 중 예외가 발생할 때)

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryCatchFinallyExample {

    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("test.txt"));
            String line = br.readLine();  // 예외 발생 가능
            System.out.println(line);
        } catch (IOException e) {
            // 파일 읽기 중 발생한 예외 처리
            System.out.println("파일을 읽는 도중 오류 발생");
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();  // 자원을 닫는 과정에서 예외 발생 가능
                } catch (IOException e) {
                    // 자원 해제 중 발생한 예외 처리
                    System.out.println("자원을 닫는 도중 오류 발생");
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 파일 읽기 중 예외가 발생할 경우, catch 블록에서 해당 예외를 처리합니다.
  • 자원을 닫는 과정에서 예외가 발생할 수 있습니다. 이때 finally 블록에서 br.close()를 호출하여 자원을 닫으려고 하지만, 닫는 도중에 예외가 발생할 수 있습니다. 이렇게 되면 자원을 닫는 중 발생한 예외는 별도로 처리됩니다.
  • 문제점: 파일을 읽는 중 발생한 원래 예외와 자원을 닫는 중 발생한 예외가 서로 독립적으로 처리되기 때문에, 원래 예외는 가려질 수 있습니다.

5.2 try-with-resources를 사용할 경우 (자원 해제 중 예외가 발생할 때)

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesExample {

    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line = br.readLine();  // 예외 발생 가능
            System.out.println(line);
        } catch (IOException e) {
            // 파일 읽기 중 발생한 예외와 자원 해제 중 발생한 예외를 함께 처리
            System.out.println("파일 읽기 또는 자원 해제 중 오류 발생");
            e.printStackTrace();
        }
    }
}
  • try-with-resources에서는 자원이 자동으로 해제되므로, finally 블록을 수동으로 작성할 필요가 없습니다.
  • 중첩된 예외 처리:
    • 파일을 읽는 중 예외가 발생해도, 자원이 자동으로 닫힙니다.
    • 자원을 닫는 도중에 예외가 발생해도 원래 발생한 예외와 자원 해제 중 발생한 예외를 함께 처리할 수 있습니다.
    • 자바는 try-with-resources에서 첫 번째 예외를 그대로 남겨두고, 자원 해제 중 발생한 예외는 suppressed exception으로 관리합니다.

5.3 중첩된 예외 확인하기 (getSuppressed() 사용)

try-with-resources는 자원을 닫는 중 발생한 예외를 억제된 예외(suppressed exception)로 저장합니다. 이를 확인하는 예시입니다.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesSuppressedExample {

    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("non_existent_file.txt"))) {
            String line = br.readLine();  // 예외 발생 가능
        } catch (IOException e) {
            System.out.println("메인 예외: " + e.getMessage());

            // 억제된 예외 확인
            for (Throwable suppressed : e.getSuppressed()) {
                System.out.println("억제된 예외: " + suppressed.getMessage());
            }
        }
    }
}
  • getSuppressed() 메서드를 사용하면 자원을 닫는 중 발생한 억제된 예외들을 확인할 수 있습니다.
  • try-with-resources를 사용하면 자원 해제 중 발생한 예외가 메인 예외에 억제된 형태로 추가되어, 두 예외를 모두 처리할 수 있습니다.

6. 결론

  • try-with-resources는 자바 7에서 도입된 자원 관리 기능으로, 자원을 자동으로 닫아줍니다. 이로 인해, 자바에서 메모리 누수나 리소스 낭비 문제를 방지할 수 있습니다.
  • 자바 7 이전의 try-catch-finally 구문에서 자원을 수동으로 닫는 문제를 해결합니다.
  • I/O 자원 관리에서 매우 유용하며, 코드의 간결성과 가독성을 크게 향상시킵니다.
  • AutoCloseable 또는 Closeable 인터페이스를 구현한 객체만 사용할 수 있지만, 이를 통해 코드가 간결해지고 예외 처리의 안전성이 보장됩니다.

'BackEnd > JAVA' 카테고리의 다른 글

[JAVA] 스레드 (Thread) 정리  (3) 2025.01.25
[JAVA] 인터페이스 심화: 다중 상속, 다중 구현  (3) 2025.01.20
[JAVA] 자바 부모클래스 및 인터페이스 심화: 오버라이딩과 메서드 동작  (0) 2025.01.17
[JAVA] 인터페이스 정리  (0) 2025.01.12
[JAVA] 추상 클래스 (Abstract Class) 정리  (0) 2025.01.09
'BackEnd/JAVA' 카테고리의 다른 글
  • [JAVA] 스레드 (Thread) 정리
  • [JAVA] 인터페이스 심화: 다중 상속, 다중 구현
  • [JAVA] 자바 부모클래스 및 인터페이스 심화: 오버라이딩과 메서드 동작
  • [JAVA] 인터페이스 정리
개발자 동긔
개발자 동긔
배우고 느낀점들을 기록합니다. 열정 넘치는 백엔드 개발자로 남고싶습니다.
  • 개발자 동긔
    Donker Dev
    개발자 동긔
  • 전체
    오늘
    어제
    • Category (39)
      • BackEnd (23)
        • JAVA (15)
        • Spring & JPA (7)
      • Database (4)
      • Computer Science (2)
        • Network (0)
        • Security (0)
        • Web (1)
      • DevOps (6)
        • Docker (1)
        • Jenkins (0)
        • Monitoring (2)
        • CICD (1)
      • 트러블 슈팅 (3)
      • 성능 개선 (1)
      • Project (0)
  • 인기 글

  • 태그

    @RequestBody
    interface
    와일드카드
    master/slave db 이중화 처리
    spring cloud msa
    nginx
    restful api 설계
    docker compose
    docker
    Spring
    spring boot
    restful api
    JPA
    인터페이스
    SSH
    mysql master/slave replication
    java
    Jenkins
    Database
    CICD
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
개발자 동긔
[JAVA] try-with-resources 정리
상단으로

티스토리툴바