9. try-finally보다는 try-with-resources를 사용하라

Effective Java의 아이템 9는 "try-finally 보다는 try-with-resources를 사용하라"라는 주제로, 자원 관리를 보다 안전하고 효율적으로 처리하는 방법에 대해 다룹니다. 주로 자원을 자동으로 관리하는 방법에 관한 내용으로, try-with-resources 구문을 사용해야 하는 이유와 그 장점을 설명합니다.

주요 내용:

  1. try-finally의 한계:

    • try-finally 블록을 사용하여 자원을 해제하는 전통적인 방법은 매우 흔한 패턴입니다. 예를 들어, 파일이나 네트워크 연결 등의 자원을 닫는 데 사용됩니다.

    • 하지만 이 방식에서는 예외가 발생할 경우, 자원을 안전하게 닫지 못하는 경우가 발생할 수 있습니다. 예외가 발생한 후 자원을 닫는 작업을 finally 블록에서 수행하는데, 자원을 닫는 도중 또 다른 예외가 발생하면 두 번째 예외가 첫 번째 예외를 덮어쓰게 되어 중요한 정보를 놓치게 됩니다.

  2. try-with-resources:

    • try-with-resources 구문은 자원 관리에 있어 더 안전하고 깔끔한 방법을 제공합니다. 자원을 관리하는 객체는 AutoCloseable 인터페이스를 구현해야 합니다. 이 인터페이스는 close() 메서드를 정의하여 자원을 해제하는 방법을 제공하고, try-with-resources 블록 내에서 이 자원들이 자동으로 닫히게 합니다.

    • try-with-resources를 사용하면 예외가 발생하더라도 자원은 자동으로 닫히며, 여러 자원이 있을 경우 순서대로 닫힙니다. 또한, 예외가 여러 개 발생할 경우 try-with-resources 구문은 모든 예외를 처리하여 예외가 덮어쓰여지지 않도록 합니다.

  3. AutoCloseable 인터페이스:

    • 자원을 닫는 작업을 자동으로 수행하려면 해당 객체가 AutoCloseable 인터페이스를 구현해야 합니다. 이 인터페이스는 close() 메서드를 정의하며, 자원을 닫는 데 필요한 작업을 수행합니다. 예를 들어, InputStream, OutputStream, Connection 등이 이 인터페이스를 구현하고 있습니다.

  4. try-with-resources의 장점:

    • 간결함: try-with-resources는 자원 해제를 자동으로 처리하므로, 개발자가 finally 블록을 작성할 필요가 없습니다. 코드가 더 간결하고 읽기 쉬워집니다.

    • 안전성: 예외가 발생하더라도 자원 해제를 보장합니다. 자원 해제 중에 예외가 발생해도, 발생한 예외들을 모두 추적할 수 있습니다.

    • 효율성: 자원을 제대로 닫지 않아 발생할 수 있는 리소스 누수 문제를 방지할 수 있습니다.

  5. finally와의 차이점:

    • finally를 사용하는 방식에서는 자원 해제 중 예외가 발생하면 원래 발생한 예외를 덮어쓸 수 있습니다. 이는 중요한 예외 정보를 놓치게 만들 수 있습니다. 반면, try-with-resources는 자원 해제 중 발생한 예외를 모두 기록하여 놓치지 않도록 합니다.

예시 코드:

try-finally 방식 (옛 방식):

public void readFile(String filePath) {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(filePath));
        // 파일을 읽는 작업
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

try-with-resources 방식 (추천 방식):

public void readFile(String filePath) {
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        // 파일을 읽는 작업
    } catch (IOException e) {
        e.printStackTrace();
    }
}

분석:

  • 간결성: try-with-resources는 자원을 자동으로 닫아주기 때문에, finally 블록을 작성할 필요가 없어 코드가 더 간결해집니다. 이는 코드의 유지보수성과 가독성을 크게 향상시킵니다.

  • 안전성: try-with-resources는 여러 자원을 관리할 때, 자원 닫기 중 예외가 발생해도 이를 누락하지 않고 모두 처리합니다. 또한, 예외 처리에 있어 예외가 덮어쓰이지 않도록 예외들을 모두 기록할 수 있어 문제를 더 잘 추적할 수 있습니다.

  • 자원 누수 방지: 자원이 자동으로 닫히므로, 자원 누수 문제를 예방할 수 있습니다. finally에서 자원을 수동으로 닫는 방식은 실수로 자원을 닫지 않거나 예외를 놓칠 수 있지만, try-with-resources는 이를 방지합니다.

결론:

Effective Java 아이템 9에서 추천하는 try-with-resources는 코드의 안전성과 효율성을 높이는 중요한 패턴입니다. 자원 해제를 명시적으로 처리하는 finally 블록 대신, 자원을 자동으로 관리하는 try-with-resources 구문을 사용함으로써 코드가 더 안전하고 간결해지며, 예외 발생 시 자원 누수나 예외 덮어쓰기를 방지할 수 있습니다.

추가 고려 사항

Suppressing Exceptions (addSuppressed)

  • try-with-resources를 사용하여 자원을 닫는 과정에서 기록된 발생한 예외(Suppressed Exception)는 Throwable.getSuppressed() 메서드를 사용하여 확인할 수 있습니다. 예를 들면:

public void testSuppressedException() {
    try (MyResource res = new MyResource()) {
        throw new RuntimeException("Primary Exception");
    } catch (Exception e) {
        System.out.println("Caught: " + e.getMessage());
        for (Throwable suppressed : e.getSuppressed()) {
            System.out.println("Suppressed: " + suppressed.getMessage());
        }
    }
}

class MyResource implements AutoCloseable {
    @Override
    public void close() {
        throw new RuntimeException("Closing Exception");
    }
}
  • 위 코드에서 Primary Exception이 발생한 후, close()에서 또 다른 예외(Closing Exception)가 발생합니다.

  • try-finally에서는 Closing Exception이 Primary Exception을 덮어쓰지만, try-with-resources에서는 Closing Exception이 Suppressed Exception으로 저장됩니다.

Last updated