[Java] 리소스 반납은 왜 항상 '역순'일까?

joyful·2026년 3월 1일

Java/Spring

목록 보기
41/47

자바에서 외부 리소스(DB, 파일 I/O, 네트워크)를 다룰 때 가장 중요한 규칙 중 하나는 "사용한 리소스는 반드시 닫아야(close) 한다."는 것이다. 그런데 여기에는 숨겨진 디테일이 하나 더 있다. 바로 '생성된 순서의 반대로 닫아야 한다.'는 원칙이다.

왜 굳이 역순이어야 할까? 단순히 관습일까? 아니면 기술적인 필연성 때문일까?


1. 거울을 보는 듯한 대칭: 생성과 소멸

자바에서 리소스 관리의 핵심은 "나중에 생성된 것을 먼저 닫는 LIFO(Last-In, First-Out)" 원칙에 있으며, 이는 마치 스택(Stack) 구조와 같은 형태로 리소스의 생명주기를 다룬다.

  • 생성: A(통로) → B(도구) → C(데이터)
  • 소멸: C(데이터) → B(도구) → A(통로)

이러한 '역순 배치'가 표준이 된 이유는 객체 간의 의존성(Dependency) 때문이다. 나중에 만들어진 객체(C)는 필연적으로 먼저 만들어진 객체(A, B)를 참조하거나 그 기반 위에서 동작하기 때문이다.


2. 현실 세계의 비유: 수도꼭지와 호스

이 원리를 가장 쉽게 이해할 수 있는 비유는 수도꼭지와 호스다.

수도꼭지라는 원천 자원에 호스라는 도구를 끼우고 물통에 물을 받고 있는 상황을 상상해보자. 지금 이 순간에도 호스 안에는 물이 꽉 차서 흐르고 있으며, 호스는 수도꼭지에 연결되어 있어야만 그 물을 온전히 전달할 수 있다.

그런데 물을 다 받기도 전에 누군가 갑자기 수도꼭지부터 잠그거나 호스를 확 뽑아버리면 어떻게 될까? 호스 안에 남아있던 물이 사방으로 튀어 옷이 젖거나, 빠져나가지 못한 물이 고여 곰팡이가 생길 수 있다.

그래서 우리는 항상 물통에 물을 다 받고, 호스를 먼저 정리한 뒤, 마지막에 수도꼭지를 잠그는 순서를 지킨다. 이것이 현실 세계에서도 가장 안전하고 깔끔한 정리 방법이기 때문이다.


3. 자바의 세계로 치환하기

이 현실의 원리는 자바의 세계에서도 그대로 적용된다. 수도꼭지가 잠겨 물이 튀는 것은 부모 객체가 먼저 닫혀 자식 객체가 에러를 던지는 것과 같고, 호스에 물이 고이는 것은 통로가 끊겨 미처 해제되지 못한 자원이 리소스 누수(Leak)를 일으키는 것과 같다.

자바는 이 '역순 정리'를 아예 언어 차원의 표준으로 명시했다. 우리가 쓰는 try-with-resources 구문을 보면 알 수 있다.

try (Resource A = ....;	// 1번 생성(수도꼭지)
	 Statement stmt = ...;	// 2번 생성(호스)
     ResultSet rs = ...) {	// 3번 생성(물)
     // 로직 수행
}
// 자바가 보장하는 종료 순서: C -> B -> A

나중에 생성된 리소스가 먼저 생성된 리소스에 의존하고 있을 확률이 높다는 전제하에, 자바는 가장 말단(C)부터 뿌리(A)까지 거슬러 올라가며 안전하게 자원을 회수한다.


💡 마치며

결국, 리소스 반납이 역순인 이유는 나중에 만들어진 자원일수록 먼저 만들어진 자원에 의존하고 있기 때문이다.

자바에서 리소스를 닫는 행위는 단순히 문을 닫는 것이 아니라, 안전하게 연결을 해제하는 과정이다. 나무의 뿌리가 뽑히면 가지가 말라 죽듯, 부모가 사라지면 자식은 고아가 된다. 가장 말단(자식)부터 정리하며 뿌리(부모)까지 거슬러 올라가는 '역순 정리'는 자바 개발자가 지켜야 할 기초적이고도 중요한 설계의 대칭성이다.

profile
기쁘게 코딩하고 싶은 백엔드 개발자

0개의 댓글