티스토리 뷰

Java

[Java] 왜 try-with-resources를 사용할까?

Jaehee Jeon 2023. 3. 27. 21:30
반응형

try-finally의 단점

 

InputStream, OutputStream과 java.sql.Connection과 같은 자원은 close 메서드를 사용자가 직접 호출해 닫아주어야 한다.

이런 자원을 직접 닫아주지 않아도 gc가 알아서 처리해주긴 하지만 그 시점을 정확히 알 수 없기 때문에 직접 닫아주는 것이 가장 안전하다.

 

Java7 이전에는 자원의 닫힘을 처리하기 위해 try-finally가 사용되었다.

BufferedReader를 사용해 한 줄의 콘솔 입력을 받는 메서드는 아래와 같다.

 

String readLine() throws IOException {
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    try {
    	return br.readLine();
    } finally {
    	br.close();
    }
}

 

단순히 하나의 자원을 가져오고 닫아주는 과정은 그렇게 복잡하지 않지만 동시에 여러 자원을 사용하는 경우 코드가 매우 복잡해진다.

 

...
InputStream in = new FileInputStream(...);
try {
    OutputStream out = new FileOutputStream(...);
    try {
        ...
    } finally {
        out.close();
} finally {
    in.close();
}

 

동시 사용 자원이 늘어날수록 try-finally가 중첩되어 가독성이 상당히 떨어지고 실수할 위험도 매우 증가한다.

 

다른 문제로 통신을 통한 외부 자원을 사용하는 상황을 가정할 때, 외부의 문제로 try 블록에서의 자원 사용에서 예외가 발생하고 동시에 finally 블록에서의 close 메서드 또한 예외가 발생할 수 있다. 이런 경우 스택 트레이스에선 이후에 실행된 finally 블록의 예외만이 나타나게 되어 디버깅이 어려워질 수 있다.

 

 

try-with-resources

 

자바 7 이후의 try-with-resources는 이런 자원 관리를 매우 간편하게 수행해 준다.

try-with-resources로 BufferedReader를 아래와 같이 사용할 수 있다.

 

String readLine() throws IOException {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
        return br.readLine();
    }
    // 필요하다면 catch 블록 또한 추가할 수 있다.
}

 

try-with-resources를 사용하려면 반납해야 할 자원을 try 구문의 괄호 안에 정의하면 되며 try 블록이 수행된 이후 알아서 자원을 닫아준다. 이런 식으로 try 블록에 소괄호로 선언되는 자원은 인터페이스인 AutoCloseable의 구현체여야 한다. 빈번하게 사용되는 BufferedReader나 Input/OutputStream, java.sql.Connection 등은 이미 이 인터페이스를 상속하고 있으며 AutoCloseable은 close 메서드 하나만 정의하면 되는 간단한 인터페이스다.

 

 

try-finally와 달리 finally 블록이 없어 가독성 면에서도 깔끔하고 자동으로 자원의 반환이 일어나므로 실수할 여지가 줄어든다. 여러 자원을 사용하더라도 try-finally와 같이 중첩할 필요 없이 여러 자원을 괄호 안에 한 번에 선언하면 사용 이후 위에서부터 순차적으로 해제해 준다.

 

try (
   Resource1 resource1 = ...
   Resource2 resource2 = ...
) {
    ....
}

 

또한 위에서 언급한 finally 문에서의 예외 발생 시 try 블록의 예외가 묻히는 문제 또한 해결되었다. 별도의 설정이 없다면 프로그래머에게 주된 관심 사항인 자원의 활용 부분 (br.readLine()) 에서 발생한 예외를 주요하게 보여주고, close 메서드와 같이 자원 회수 시 발생하는 예외는 suppressed라는 표시를 달고 스택 트레이스 내역에 함께 표시된다.

 

자바 8까지는 괄호 안에서 매번 새로운 객체를 생성하고 사용해야 했지만 자바 9 이후부터는 final 객체, 혹은 effectively final인 객체 또한 괄호 내부에서 사용이 가능하다.

 

final Scanner scanner = new Scanner(new File("read.txt"));
PrintWriter writer = new PrintWriter(new File("write.txt"));
try (
    scanner;
    writer
) { 
    ...
}

 

 

따라서 try-finally보다 가독성과 관리가 편리하고, 한 번에 발생한 여러 예외를 스택 트레이스에서 모두 확인할 수 있는 try-with-resources를 사용하자.

 

 

참고자료

https://www.baeldung.com/java-try-with-resources

이펙티브 자바 3/E 아이템 9. try-finally보다는 try-with-resources를 사용하라

반응형