
자바에서 파일, 네트워크 소켓, 데이터베이스 연결 같은 자원은 사용 후 반드시 정리(close) 해야 한다. 그렇지 않으면 메모리 누수(memory leak) 또는 리소스 잠금(locking issue) 같은 문제가 발생할 수 있다.
자원을 정리하는 일반적인 방법은 try-finally 를 사용하는 것이다. 하지만 예외 처리를 제대로 하지 않으면 예상치 못한 문제들이 발생할 수 있다.
아래 코드는 두 개의 리소스를 생성하고, 하나의 메서드에서 호출하는 과정에서 예외가 발생할 경우를 다룬다.
package network.tcp.autocloseable;
public class ResourceCloseMainV2 {
public static void main(String[] args) {
try {
logic();
} catch (CallException e) {
System.out.println("CallException 예외 처리");
e.printStackTrace();
} catch (CloseException e) {
System.out.println("CloseException 예외 처리");
e.printStackTrace();
}
}
private static void logic() throws CallException, CloseException {
ResourceV1 resource1 = null;
ResourceV1 resource2 = null;
try {
resource1 = new ResourceV1("resource1");
resource2 = new ResourceV1("resource2");
resource1.call();
resource2.callEx(); // CallException 발생
} catch (CallException e) {
System.out.println("ex: " + e);
throw e; // CallException 다시 던짐
} finally {
if (resource2 != null) {
resource2.closeEx(); // CloseException 발생
}
if (resource1 != null) {
resource1.closeEx();
}
}
}
}
이 코드에서 발생할 수 있는 주요 문제들을 살펴보자.
null 체크 문제finally 블록을 사용하면 예외가 발생해도 자원 정리 코드가 실행된다.null 이 된다.null 체크를 하지 않으면 NullPointerException 이 발생할 수 있다.finally 블록에서 closeEx() 메서드를 호출하는데, 이 과정에서 예외가 발생하면 그 이후 코드가 실행되지 않는다.callEx()에서 발생한 CallException이다.finally 블록에서 추가로 CloseException이 발생하면 핵심 예외가 덮어씌워지는 문제가 발생할 수 있다.try-with-resources 사용 (권장)AutoCloseable을 구현하면 try-with-resources 문법을 활용할 수 있다.null 체크도 필요 없다.public class ResourceCloseMainV3 {
public static void main(String[] args) {
try (ResourceV1 resource1 = new ResourceV1("resource1");
ResourceV1 resource2 = new ResourceV1("resource2")) {
resource1.call();
resource2.callEx(); // CallException 발생
} catch (CallException e) {
System.out.println("CallException 예외 처리");
e.printStackTrace();
} catch (CloseException e) {
System.out.println("CloseException 예외 처리");
e.printStackTrace();
}
}
}
suppressed 예외 활용하기try-with-resources는 리소스 정리 중 발생한 예외를 억제(suppressed)하여 관리할 수 있다.getSuppressed()를 사용하면 숨겨진 예외를 확인할 수 있다.catch (Exception e) {
Throwable[] suppressed = e.getSuppressed();
for (Throwable t : suppressed) {
t.printStackTrace();
}
}
finally 블록에서 여러 개의 예외를 안전하게 처리하기try-catch를 추가하여 한 리소스의 정리 실패가 다른 리소스 정리에 영향을 주지 않도록 한다.finally {
try {
if (resource2 != null) resource2.closeEx();
} catch (Exception e) {
e.printStackTrace();
}
try {
if (resource1 != null) resource1.closeEx();
} catch (Exception e) {
e.printStackTrace();
}
}
✅ try-with-resources를 사용하면 자원 정리가 자동화되고, 예외 처리도 안전하게 관리 가능
✅ getSuppressed()로 숨겨진 예외까지 추적 가능
✅ finally 블록에서 여러 개의 자원을 정리할 때는 개별 try-catch를 사용하여 다른 자원의 정리 실패를 방지해야 함
자원 관리를 안전하게 처리하는 것은 안정적인 애플리케이션 개발의 핵심이다. try-with-resources와 적절한 예외 처리를 활용하여 더 견고한 코드를 작성하자! 🚀