ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Try with Resources / Try Finally
    스터디 2024. 6. 10. 01:44

     

    도입

    최근 이펙티브 자바 책을 읽으며, 객체 생성과 파괴 파트에서 try-with-resources 관련 내용을 볼 수 있었다. 저자는 try-finally 보다는 try-with-resources를 사용을 권장한다.

    InputStream, OutputStream, java.sql.Connection 등 close 메서드를 직접 닫아줘야하는 자원에 대해서 이러한 내용을 제시하였다.

    저자는 다양한 자원들이 finalizer, cleaner를 안전망으로 활용하고 있으나, 이에 대한 문제점을 제기한다.

    • 닫아야할 자원이 여러 개인 경우 try-finally는 중첩으로 인해 지저분한 구조를 갖게 된다.
    • finally 블록에서도 예외가 발생할 수 있으므로 finally 블록에서 발생한 예외가 다른 예외를 삼켜버려 close 메서드가 실패할 수도 있다.
    • 이를 해결하기 위해 try-with-resources 사용을 권장한다.

    finalizer, cleaner에 대한 문제점도 try-with-resources 앞쪽 단락에서 설명하였다. 핵심만 언급하자면

    • 객체가 가비지 컬렉터에 의해 회수될 때 호출되는데, 가비지 컬렉션의 호출 시점을 예측할 수 없으므로, 즉시 수행된다는 보장이 없다.
    • 가비지 컬렉션 과정에서 특별히 처리해야 하므로 심각한 성능 문제를 동반한다. (실제 실험 결과도 제시하였다.)

    ⇒ 이에 대한 해결 방법으로 try with resources를 제시하였다. 또는 자원 사용자가 close 메소드를 직접 잘 호출해주어야한다.

    자바 Resource 예외 처리

    DB, Network, File의 경우 자바 외부에 위치한다.

    이러한 프로세스 외부에 위치한 데이터에 어떤 동작을 수행할 때, 예외가 발생할 수 있다.

    만약, 입출력에 관련된 resource들에 접근해서 사용하다가 예외가 발생하는 경우 아래와 같은 문제로 이어질 수 다.

    • 리소스 누수 : 자원이 제대로 닫히지 않으면, 시스템 리소스를 계속 점유하여 시스템의 성능 저하 또는 시스템의 오류를 초래할 수 있다.
    • 데이터 손실 : 데이터가 제대로 저장되지 않거나 손상될 수 있다.
    • 예외 유실 : finally 블록 내에서 발생하는 예외를 덮어쓸 수 있다. 이는 문제점을 파악하는 데 어려움을 야기할 수 있다.

    try finally란?

    자바에서 예외 처리 시 사용되며, 예외가 발생하든, 발생하지 않든 반드시 실행되어야 하는 코드를 관리하는데 사용된다. 주로 자원을 해제하거나 정리할 때 필요한 로직을 수행할 때 쓰인다.

    try finally 의 특징

    • 예외 처리 유연성 : finally 블록이 try 블록을 실행한 후 항상 실행된다. 따라서, 예외 발생 여부와 관계없이 예외처리 동작을 수행할 수 있도록 구현할 수 있다.
    • 코드 분리와 관심사의 분리 : 비즈리스 로직과 예외 처리 로직을 명확히 분리할 수 있다.

    try finally 예제

    class CustomFileWriter extends FileWriter {  // close 내부에서 문제가 발생하는 것을 보여주기 위한 커스텀 클래스
        public CustomFileWriter(String fileName) throws IOException {
            super(fileName);
        }
    
        @Override
        public void write(String str) throws IOException { // write 동작 중 예외 발생
            super.write(str);
            throw new IOException("Error during write operation");
        }
    
        @Override
        public void close() throws IOException { // close 동작 중 예외 발생
            super.close();
            throw new IOException("Failed to close the writer"); 
    
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            CustomFileWriter writer = null;
            try {
                writer = new CustomFileWriter("output.txt");
                writer.write("Hello, world!"); // 1. write 동작 중 예외 발생
            } catch (IOException e) { // 2. write 예외 catch
    		        /* 예외 처리 */
                System.err.println("Exception: " + e.getMessage()); // message : error during write operation
            } finally {
                if (writer != null) {
                    try {
                        writer.close(); // 3. 예외 발생으로 인해 close 수행하려던 중 close 예외 또 발생
                    } catch (IOException e) {
                        System.err.println("Exception: " + e.getMessage()); // 4. 어떤 exception이 찍혔을까
                    }
                }
            }
        }
    }
    
    

    구현 : 위 경우에는 close() 동작 내부에서 예외를 억지로 발생시킴으로써, 예외 덮어쓰기를 보임.

    문제점

    • finally 블록에서 예외가 발생하는 경우 이전 예외를 덮어쓰기 때문에 디버깅하기 어려워진다.
    • close() 동작 이전에 예외가 발생하는 경우 close() 동작이 수행되지 않을 수 있다.
    • 여러 자원을 다루게 되는 경우 finally 문으로 인해 코드 유지보수가 복잡해진다.

    try with resource란?

    try with resources는 JDK 7부터 추가된 try catch 문의 변형 문법이다.

    try with resource는 try에 resource 객체를 전달하면, try 코드 블록이 끝나면 자동으로 자원을 종료해주는 기능이다.

    try with resources 특징

    • 자동 자원 해제 : AutoCloseable 인터페이스를 구현한 객체만 사용할 수 있다. 이 인터페이스의 close() 메소드는 try 블록의 실행이 완료되면 자동으로 호출되어, 명시적을 자원을 해제하는 코드를 작성하지 않아도 되며, 자원 누수의 가능성을 줄일 수 있다.
    • 코드 간결성 : 코드가 더 간결해지고 읽기 쉬워진다. 자원을 해제하기 위한 별도의 finally 블록이 필요한 경우가 줄어든다.
    • 예외 처리 향상 : try블록과 close() 메소드에서 예외가 동시에 발생하면, close() 메소드에서 발생한 예외는 억제되고 try 블록의 예외가 우선적을 처리된다.
    • 리소스 확장성 : 여러 자원을 관리해야 하는 경우 , 각 자원을 세미 콜론으로 구분하여 하나의 try with resources 구문에서 처리할 수 있다.

    try with resources 예제

    class CustomFileWriter extends FileWriter {  // close 내부에서 문제가 발생하는 것을 보여주기 위한 커스텀 클래스
        public CustomFileWriter(String fileName) throws IOException {
            super(fileName);
        }
    
        @Override
        public void write(String str) throws IOException { // write 동작 중 예외 발생
            super.write(str);
            throw new IOException("Error during write operation");
        }
    
        @Override
        public void close() throws IOException { // close 동작 중 예외 발생
            super.close();
            throw new IOException("Failed to close the writer");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            try (CustomFileWriter writer = new CustomFileWriter("output.txt")) { // 1.try with resource로 자원 접근, finally 블록을 작성하지 않아도 됨.
                writer.writte("Hello, world!"); // 2. write에서 예외 발생
            } catch (IOException e) { // 3. write 도중 발생한 예외를 처리
                System.err.println("Exception: " + e.getMessage()); // 4. write 예외 확인
                for (Throwable sup : e.getSuppressed()) { // 5. 억제된 예외(close 예외) 확인 및 로깅
                    Sysfotem.err.println("Suppressed: " + sup.getMessage());
                }
             }e
        }
    }
    
    =====================================================================================
    BUILD SUCCESSFUL in 183ms
    2 actionable tasks: 1 executed, 1 up-to-date
    Exception: Error during write operation
    Suppressed: Failed to close the writer // close에서 예외가 발생했으나, getSuppressed() 즉, 억제된 예외로 처리됨.
    오후 2:11:39: Execution finished ':Main.main()'.
    

    구현

    • 위 경우에는 close() 동작 내부에서 예외를 억지로 발생시킴으로써, 예외 덮어쓰기가 개선됨을 보임.
    • 원래 AutoCloseable을 인터페이스를 구현한 클래스여야 하지만, FileWrite에서 이미 Closeable 인터페이스를 구현하고 있고, Closeable은 AutoCloseable을 확장하고 있기 때문에 추가적으로 구현하지 않았다.

    try-finally에 비한 개선

    • try 블록 종료 시 자동으로 자원을 해제한다.
    • 이전 exception에 대해 덮어쓰지 않고, suppressed(억제된) 예외로 처리되어 디버깅을 통해 예외를 추적할 수 있다.
    • 여러 자원을 다루게 되는 경우 세미콜론으로 구분하여 여러 자원에 대한 관리가 가능하여, 확장에 용이하.

    FileWriter → OutpurStreamWriter → Writer → Closeable → AutoCloseable public class FileWriter extends OutputStreamWriter public class OutputStreamWriter extends Writer public abstract class Writer implements Appendable, Closeable, Flushable public interface Closeable extends AutoCloseable

    '스터디' 카테고리의 다른 글

    Spring Annotation 의 내부구조 동작원리  (0) 2024.06.09
    JAVA 병렬 프로그래밍  (0) 2024.06.09
    Spring Transactional 뜯어보기  (0) 2024.05.15
    Spring Tomcat 분석  (0) 2024.05.01
    SQL Query 최적화 (Spring JPA, Go gorm)  (1) 2024.03.15
Designed by Tistory.