[Java] 자원 정리, try-catch-finally와 try-with-resources

벼랑 끝 코딩·2025년 3월 16일
0

Java

목록 보기
29/40

자바는 인스턴스의 경우 GC(Garbage Collector)를 통해 사용하지 않는 객체를 정리한다.
하지만 외부 자원의 경우 자바는 자동으로 자원을 정리하지 않기 때문에,
필수적으로 직접 자원을 정리해야 한다.

자원을 정리하는 코드는 어떠한 상황에서도 필수적으로 실행되어야 한다.
어떻게 하면 자원 정리를 안전하게 실행할 수 있을지 알아보자.

try-catch-finally

try {
	// 메인 로직
} catch (Exception e) {
	// 지정한 예외가 발생하는 경우 처리하는 예외 처리 로직
} finally {
	// 예외 처리 로직 이후 반드시 실행하는 로직
}

try-catch-finally 구문에서 try 블럭은 메인 로직을 실행하고
catch 블럭에서는 잡아서 처리할 예외를 설정한 후 처리 로직을 실행하며,
finally 구문에서는 catch 블럭 이후 반드시 실행되어야할 로직이 추가된다.

언뜻 보면 구조적으로 완벽한 모양새를 갖춘 듯 하지만,
try-catch finally에는 몇 가지 단점이 존재한다.

외부 변수 선언 문제

try-catch-finally 블럭은 서로 구분되어 변수를 공유할 수 없다.
변수를 공유하기 위해서는 try-catch-finally 블럭 이전,
외부에서 변수를 선언한 후 공유해야 한다.

int number = 0;  //  ** try-catch-finally 외부 블럭에서 변수 선언 **

// 외부 블럭에서 변수를 선언해야 공유 가능
try {
	number = 1;  // ** 변수 공유 **
    // 메인 로직
} catch (Exception e) {
	// 예외 처리 로직
} finally {
	number = 0;  //  ** 변수 공유 **
    // catch 블럭 이후 수행 로직
}

try-catch-finally의 각 블럭에서 선언한 변수는 공유할 수 없다.


// 외부 블럭에서 변수를 선언해야 공유 가능
try {
	int number = 0;  // ** try-catch-finally 블럭 내부에 변수 선언 **
    // 메인 로직
} catch (Exception e) {
	// 예외 처리 로직
} finally {
	number = 1;  //  ** 변수 공유 불가, 컴파일 에러 **
    // catch 블럭 이후 수행 로직
}

핵심 예외 삭제 문제

try-catch-finally 구문에서 try 내부 코드에 예외가 발생하면
catch 블럭에서 설정한 예외의 경우 잡아서 처리한다.
catch에서 예외를 잡지 못하면 블럭이 수행되지 않고 밖으로 예외가 던져지게 된다.
그 후 finally 구문이 수행된다.

하지만 만약 finally 구문에서 또 예외가 발생한다면?


// 외부 블럭에서 변수를 선언해야 공유 가능
try {
    // ** 예외 발생! **
} catch (XXXException e) {
	// ** catch 블럭에서 예외를 잡아서 처리하지 못한다면 외부로 throws **
} finally {
    // ** 여기서 또다른 예외가 발생한다면? **
}

이 때에는 기존에 발생한 예외를 덮어버리고 finally에서 발생한 예외가 최종적으로 던져진다.
즉 알아차려야 할 핵심 예외가 없어져버린 것이다!

이 밖에도 보통 자원 정리에 사용되는 finally 구문에서 자원 정리 코드에 실수가 발생하거나
finally 코드가 catch 이후로 호출이 지연되는 등의 문제가 있다.

치명적인 빈틈이 존재하는 try-catch-finally 구문의 해결책은 없을까?

try-with-resources

try-with-resources는 try-catch-finally의 단점을 극복하는 try 선언 방식이다.
자동으로 정리될 자원은 Autocloseable을 구현해야 한다.
정리해야할 자원은 세미콜론(;)으로 구분할 수 있다.

try (AutocloseableResource ar = new AutocloseableResource();
	 AutocloseableResource ar2 = new AutocloseableResource()) {
	// 메인 로직
} catch (Exception e ) {
	// 예외 처리 로직
}

try 블럭을 선언하면서 함께 선언된 객체들은
try 블럭이 수행된 직후 finally 블럭에서 close() 메서드 없이
자동으로 자원을 정리하는 코드가 실행된다.

외부에서 변수를 선언할 필요 없이, 자원들의 Scope는 try 블럭 내부로 한정되며,
핵심 예외를 반환하고 자원 처리 상황에서 발생하는 부가적인 예외들은
Suppressed 객체 내부에 담아 반환된다(e.getSuppressed() 메서드로 확인 가능).

try-with-resources 한계

try-with-resources를 통해 try 블럭에 자원을 함께 선언하면서
try-catch-finally 한계를 극복하고 완벽해진 것 같지만, 항상 사용할 수 있는건 아니다.

try-with-resources는 try 블럭이 끝까지 수행된 이후에 자원을 정리한다.
try 블럭이 끝까지 수행되지 않고 종료될 가능성이 있는 경우,
자원을 정리하지 않고 종료되기 때문에 try-catch-finally 구문의 사용을 고려해야 한다.

마무리

예외 관리는 개발에서 상당한 부분을 차지한다.
try-catch-finally와 try-with-resources를 통해 예외를 관리하는 것도 중요하지만
자원을 효율적으로 관리하기 위해서도 try 블럭을 적절하게 사용해야 한다.

특히 외부 자원을 사용하고 있다면, try 블럭을 어떻게 사용해야 할지 고민해보자.

profile
복습에 대한 비판과 지적을 부탁드립니다

0개의 댓글