Java의 AutoCloseable interface와 try - with - resources에 대해 작성한 글입니다.
EntityManagerFactory와 EntityManager
Java Spring JPA에서 Entity를 관리하는 객체로 EntityManager를 사용합니다.
EntityManager는 EntityManagerFactory class에서 생성됩니다. 공장이라는 이름에 알맞은 듯합니다.


EntityManagerFactory, EntityManager interface를 살펴보다가 AutoCloseable interface를 extends 하고 있음을 발견했습니다. 공통으로 extends 하는 AutoCloseable interface에 대해 살펴보겠습니다.
AutoCloseable interface
AutoCloseable interface는 java.lang package 소속입니다.
java.lang package

jdk-21 기준으로 java.base라는 library root 내부 package입니다.
간략하게 java.lang package 내부 class들은 import 하지 않고 사용할 수 있는 기본적인 class입니다.
대표적인 class로 Object class가 존재합니다.
AutoCloseable
다시 돌아와서, AutoCloseable interface 내부를 살펴봅시다.

내부가 Java Doc으로 작성되어 있습니다. Java Doc을 읽어봅시다. 이름과 첫 줄에서부터 알 수 있듯이, 자원의 닫음(Closes this resource, ..)에 대한 interface입니다.
Closeable interface
뜬금없지만, 잠시 Closeable interface에 대해 설명해보겠습니다.
File system이나, Socket programming에서 사용하는 Stream이나, Socket들은 할당을 했으면, 반납이 필요한 객체입니다. 반납이 필요한 객체들은 기본적으로 Closeable interface를 extends 해야 합니다. 예시로 Socket class를 살펴봅시다.

Closeable interface는 java.io 소속 interface입니다. 위에서 보았듯이, 할당 후 반납이 필요한 객체들은 기본적으로 Closeable interface를 implements 해야 합니다. close method를 통해 반납을 해주어야 하기 때문입니다.

try - catch - finally
자원의 반납(닫음)에 대한 이야기입니다. 기존에는 File system이나, Socket Programming을 할 때, try - catch - finally 방식을 채용했습니다. try block에서 Stream이나 Socket 객체를 new를 통해 할당합니다. 이후 finally block에서 null인지, 아닌지에 따라 close를 해주어야 하는데, 이게 참 골칫덩어리입니다.
try{
FileInputstream is = new FileInputStream( /* "경로" , File 객체 */ );
....
}catch(...){
....
}finally(){
if(is != null){
is.close()
}
}
1~2개의 Stream이나 Socket 객체를 생성하는 경우는 쉽지만, 많은 Stream과 Socket을 필요로 하는 Programming에선 예외(까먹고 닫지 않는 경우..) 가 일어날 수 있다는 말입니다.
그래서 Java7에서부터 이러한 Programming적 예외를 방지하기 위해 try - with - resources를 도입합니다.
(프로그래밍적 예외 뿐 아니라, printStackTrace의 누락, 코드의 간결성 등을 해결하기 위해서도 도입되었습니다.)
(스포일러를 하자면 AutoCloseable interface를 도입했기 때문입니다.)
try - with - resources
try - with - resources란 기존의 try - catch - finally에서 문제점을 보완한 방식입니다. 코드가 드라마틱하게 바뀌진 않습니다. 예시를 들어 살펴보는 것이 좋아 보입니다.
try(
FileInputstream is = new FileInputStream( /* "경로" , File 객체 */ );
FileInputstream is2 = new FileInputStream( /* "경로" , File 객체 */ )
){
// 작업 수행
}catch(Exception e){
e.printStackTrace();
}
// (2개 이상의 반납이 필요로 하는 객체를 할당하기 위해선
// ; (세미콜론)으로 구분합니다. 하나면 세미콜론 없이 작성하면 됩니다.
try - catch - finally와 달라진 모습이 보입니다. 일단 자원의 할당을 try {} block이 아닌, () block에서 할당합니다. 또한 finally block도 사라진 모습을 알 수 있습니다. 딱 봐도 간략한 코드가 완성되었습니다. 어떻게 과연 했을까요?
바로 이번 글의 주제, AutoCloseable interface를 도입했기 때문입니다.
AutoCloseable interface
이는 java 7부터 도입된 새로운 interface입니다.
기존의 반납이 필요로 하는 객체들은 Closeable interface를 extends하고 있었습니다. 이름에서 알 수 있듯이, 이는 자동으로 자원을 반납해 주는 interface입니다.

java7 이상에선, Closeable interface를 AutoCloseable interface의 구현체로 둠으로 close()를 개발자가 하지 않고, java에 위임합니다. 또한, Closeable interface에 상위 interface를 추가 함으로써, 기존 코드를 변경하지 않고 안정적으로 자원에 대한 반납을 수행할 수 있게 되었습니다.
(기존 코드를 변경하지 않고.. 에 대한 내용은 프로그래밍 언어론(PL) 책을 읽어보시면 많은 도움이 됩니다.)
요약
요약하자면, Stream이나 Socket등의 객체는 Closeable interface의 구현체입니다. 보통 try - catch - finally방식을 통해
자원을 반납했습니다. 하지만 이는 프로그래밍의 실수를 야기하고, 코드를 길게 만들며, error print시 error의 누락을 발생시킵니다.
이를 보완하기 위해 java 7에서 AutoCloseable interface를 도입합니다. AutoCloseable interface를 Closeable interface의 구현체로 둠으로써 기존의 코드를 해치지 않고 (Socket class 같은 경우, AutoCloseable을 extends 하는 것이 아닌, 여전히 Closeable interface를 extends 합니다.) try - catch - finally 방식을 try - with - resources 방식으로 변경했습니다. 이는 자원의 반납을 개발자가 아닌 java에 위임하여 안정적인 반납을 보장하게 된 것뿐만 아니라, 코드를 보다 간결하게 만들어 주었습니다.