InputStream이나 OutputStream을 명시적으로 닫지 않으면 자원 누수가 발생할 수 있다. Java에서는 자원을 명시적으로 닫아야 한다. 특히 ByteArrayInputStream
과 ByteArrayOutputStream
과 같은 스트림은 닫지 않으면 메모리 누수가 발생할 수 있다.
try-with-resources 문법을 사용하여 자원을 자동으로 닫을 수 있다.
아래는 try-with-resources 문법을 적용하여 수정한 메서드이다.
public ByteArrayInputStream generateCsv(List<List<String>> data) throws IOException {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
CSVPrinter csvPrinter = new CSVPrinter(new PrintWriter(out), CSVFormat.DEFAULT)) {
for (List<String> record : data) {
csvPrinter.printRecord(record);
}
csvPrinter.flush();
return new ByteArrayInputStream(out.toByteArray());
} catch (IllegalArgumentException | IOException e) {
log.error("Error generating CSV: {}", e.getMessage());
throw e;
}
}
@PostMapping("/csv")
public ResponseEntity<InputStreamResource> downloadCsv(@RequestBody ExcelDownloadRequestDto request) {
ByteArrayInputStream byteArrayInputStream;
try {
byteArrayInputStream = taxExcelService.generateCsv(request.csvData());
} catch (IOException e) {
throw new RuntimeException("Error generating CSV", e);
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + request.fileName());
return ResponseEntity
.ok()
.headers(headers)
.contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
.body(new InputStreamResource(byteArrayInputStream));
}
Spring에서 리소스를 처리할 때는 try-with-resources
를 사용하여 리소스를 명시적으로 닫는 것이 좋다. try-with-resources
구문을 사용하면 *AutoCloseable
인터페이스를 구현한 객체는 블록이 끝날 때 자동으로 닫힌다. 따라서 명시적으로 close()
를 호출할 필요가 없다.
예를 들어 ByteArrayInputStream
과 같은 리소스를 사용한 후에 명시적으로 닫지 않아도 된다. 하지만, InputStreamResource
로 반환되는 ByteArrayInputStream
의 경우, 클라이언트 측에서 다운로드가 완료되면 자동으로 스트림이 닫힐 것이다.
정리하자면 service의 generateCsv
메서드는 generateCsv
메서드는 try-with-resources
구문을 사용하여 ByteArrayOutputStream
과 CSVPrinter
를 자동으로 닫는다.
conroller의 downloadCsv
메서드는 ByteArrayInputStream
을 반환하기 때문에 리소스를 명시적으로 닫지 않아도 된다. 스프링이 HTTP 응답을 보낼 때 리소스를 자동으로 처리한다.
AutoCloseable 인터페이스는 자바에서 자원을 자동으로 해제하기 위한 인터페이스이다. 왜냐하면 이 인터페이스를 구현하는 클래스의 인스턴스는 try-with-resources 구문 안에서 자동으로 close() 메소드가 호출되기 때문이다.
이 인터페이스는 단 하나의 메소드인 close()를 정의하고 있다. 이 메소드는 자원을 해제하는 로직을 포함하며, try-with-resources 구문이 종료될 때 자동으로 호출된다. 따라서 개발자는 자원 해제를 위한 별도의 코드를 작성할 필요가 없다다.
AutoCloseable 인터페이스를 구현한 클래스는 파일 입출력 스트림, 데이터베이스 연결 등 다양할 수 있다. 이러한 클래스의 인스턴스를 try-with-resources 구문 안에서 생성하면, 구문이 종료될 때 자동으로 자원이 해제된다. 이는 프로그램의 안정성을 높이고 메모리 누수를 방지하는 데 도움이 된다.
예를 들어, 파일 입출력을 다루는 경우, FileInputStream 클래스는 AutoCloseable 인터페이스를 구현한다. 따라서 FileInputStream 인스턴스를 try-with-resources 구문 안에서 사용하면, 파일 입출력 작업이 완료된 후 자동으로 스트림이 닫힌다.
AutoCloseable 인터페이스의 도입으로 자바에서의 자원 관리가 훨씬 간편해졌다. 왜냐하면 개발자가 자원 해제를 위한 코드를 직접 작성하지 않아도 되기 때문이다. 이는 코드의 가독성과 유지 보수성을 향상시키는 데 기여한다.
try-with-resources 구문은 자바 7에서 도입된 구문으로, 자원을 자동으로 해제하기 위해 사용된다. 이 구문을 사용하면 AutoCloseable 인터페이스를 구현한 자원을 안전하게 해제할 수 있다. 왜냐하면 이 구문이 종료될 때 자동으로 close() 메소드가 호출되기 때문이다.
try-with-resources 구문의 기본 형태는 다음과 같다. 이 구문 안에서 생성된 자원은 구문이 종료될 때 자동으로 해제된다. 따라서 개발자는 자원 해제를 위한 별도의 코드를 작성할 필요가 없다.
try (AutoCloseable resource = new AutoCloseableResource()) {
// 자원을 사용하는 코드
} catch (Exception e) {
// 예외 처리 코드
}
이 구문을 사용할 때는 자원이 AutoCloseable 인터페이스를 구현해야 한다. 이는 자원이 자동으로 해제될 수 있도록 보장하기 위함이다. 따라서 개발자는 자원을 사용하기 전에 이를 확인해야 한다.
참고: https://f-lab.kr/insight/understanding-autocloseable-and-try-with-resources-in-java