[Java] try with resource 를 사용해야하는 이유

아두치·2021년 3월 13일
3

What is that?


try with resource 를 설명하기 위해 먼저 상황을 하나 가정하겠다.

다음과 같이 첫번째 인자로 주어진 파일에 두번째 인자로 주어진 문자열을 입력하는 메소드를 작성한다고 가정하자.

public void write(String fileName,String word){
	Writer writer = new FileWriter(fileName);
	writer.write(word);
}

이 코드는 정상적으로 컴파일되지 않는다.

이유는 입출력 관련 메소드(위 코드에서는 Writer.write 메소드)는 IOExcpetion 예외를 throws 하는데 이 IOException 예외가 Checked Exception 이기 때문이다.

따라서 예외를 try~catch 로 적절히 처리해주거나 throws 를 이용해 예외 처리의 책임을 호출한 쪽으로 위임해줘야 한다.

내가 가정한 상황은 외부에 제공하기 위한 파일 입력 메소드를 작성하는 상황이기 때문에 메소드 내부에서 예외를 직접 처리하기 보다는 잘못된 상황의 원인을 의미하는 예외 객체를 던져주는게 더 바람직할 것 같다.

public void write(String fileName,String word) throws IOException{
	Writer writer = new FileWriter(fileName);
	writer.write(word);
}

하지만 여기서 그치면 문제가 발생한다.

FileWriter 스트림이 정상적으로 생성된 후 write(word) 메소드를 실행하다가 예외가 발생할 경우 해당 예외가 정상적으로 throws 되겠지만, 스트림은 소멸하지 못하고 남겨진 채 메소드가 종료된다.

스트림이 메모리에 남아있을 경우 값 유실 문제가 발생할수도 있으므로 스트림을 메모리에서 소멸시켜줘야한다.

보통은 이 과정을 "스트림을 닫는다(close)" 라고 한다.

public void write(String fileName,String word) throws IOException{
	Writer writer = new FileWriter(fileName);
	writer.write(word);
	writer.close();
}

그렇다고 이렇게 작성하면 안된다.
write 메소드 실행 도중에 예외가 발생할 경우엔 close 메소드가 실행되지 않기 때문이다.

그래서 Java 6 까지는 다음과 같은 방법으로 입출력 스트림을 다루었다.

public void write(String fileName,String word) throws IOException{
	try{
		Writer writer = new FileWriter(fileName);
		writer.write(word);
	}
	finilly{
		writer.close();
	}
}

이렇게 try finally 로 입출력 작업 코드를 묶어주면 try 블록에서 예외가 발생하든 안하든 finally 는 무조건 실행되기 때문에 스트림을 닫는 작업을 보장하게 된다.

하지만 이 방법도 문제가 있다.

위 코드에서 try 블록을 실행하다가 예외가 발생하면 finally 블록을 실행한 후(스트림을 닫은 후) 발생한 예외를 throws 하는데, 만약 스트림을 닫는 과정에서 예외가 발생할 경우 try 블록에서 발생한 예외가 아니라 finally 블록에서 발생한 예외가 던져진다는 것이 문제다.

우리가 작성한 메소드를 사용하는 사용자 입장에서는 스트림을 닫다가 문제가 발생했다는 정보보다는 입력과정에서 어떤 문제가 있었는지에 대한 정보가 더 중요하다.

당연한 것 아닌가?

주문한 물건이 취소됐을 때 우리는 왜 취소됐는지가 궁금하지, 판매자가 물건을 취소하는 과정에서 발생한 문제가 궁금하진 않는것과 같다.

이런 문제를 자바 컴파일러가 알아서 해결해주기 위해 Java 7 부터는 try with resource 라는 문법이 추가되었다.


How to use it?


public void write(String fileName,String word) throws IOException{
	try(Writer writer = new FileWriter(fileName)){
		writer.write(word);
	}
}

일단 코드 자체도 훨씬 간단해졌고 알아보기 쉬워졌다.

finally 블록이 없다는 점에 주목하자.

try 뒤에오는 괄호안에 try 블록에서 사용할 스트림(리소스)를 생성해주기만 하면 try 블록에서 예외가 발생하든 발생하지 않든간에 생성한 리소스를 자동으로 close 해준다.


Why use it?


try with resource 를 사용했을 때 얻는 이점은 이게 전부가 아니다.

만약 아까와 같은 상황처럼 close 과정에서 예외가 발생해도 try 블록에서 발생한 예외가 throws 된다.

따라서 우리가 작성한 메소드를 사용하는 사용자는 무조건 입력 문제의 원인을 받아볼 수 있다.

그리고 우리가 작성한 메소드 사용자가 close 과정에서 발생한 예외도 받고 싶다면 이것 또한 가능하다.

public void test(){
	try{
		write("adduci","simple text");
	}
	catch(IOException e){
		e.printStackTrace(); // write 메소드의 try블록에서 발생한 예외
		Thowable[] suppressExceptions = e.getSuppressed(); // write 메소드의 try 블록 이외에서 발생한 예외들의 배열
	}
}

자바에서 close 과정에서 발생하는 예외와 같이 메인 원인이 아닌 부차적인 예외를 억제된(suppressed) 예외라고 한다.

try with resource 를 사용하면 부차적인 예외를 자동으로 억제(suppress) 해준다.


So


Java 7 이상 버전을 사용중이라면 리소스를 사용하는 코드는 반드시 try with resource 를 사용하자.

profile
HAVE YOU TRIED IT?

1개의 댓글

comment-user-thumbnail
2021년 3월 13일

자동으로 닫아주는게 신기하네요

답글 달기