[Spring Boot] Resource의 해제를 어떻게 처리해야 할까?

Sungjin Cho·2024년 7월 31일
0

Spring Boot

목록 보기
10/15
post-thumbnail

의문점❓

spring에서는 stream을 close 하는 부분이 없는데 어디서 close 될까?

InputStream이나 OutputStream을 명시적으로 닫지 않으면 자원 누수가 발생할 수 있다. Java에서는 자원을 명시적으로 닫아야 한다. 특히 ByteArrayInputStreamByteArrayOutputStream과 같은 스트림은 닫지 않으면 메모리 누수가 발생할 수 있다.

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 구문을 사용하여 ByteArrayOutputStreamCSVPrinter를 자동으로 닫는다.

conroller의 downloadCsv 메서드는 ByteArrayInputStream을 반환하기 때문에 리소스를 명시적으로 닫지 않아도 된다. 스프링이 HTTP 응답을 보낼 때 리소스를 자동으로 처리한다.

Autocloseable 인터페이스

AutoCloseable 인터페이스는 자바에서 자원을 자동으로 해제하기 위한 인터페이스이다. 왜냐하면 이 인터페이스를 구현하는 클래스의 인스턴스는 try-with-resources 구문 안에서 자동으로 close() 메소드가 호출되기 때문이다.

이 인터페이스는 단 하나의 메소드인 close()를 정의하고 있다. 이 메소드는 자원을 해제하는 로직을 포함하며, try-with-resources 구문이 종료될 때 자동으로 호출된다. 따라서 개발자는 자원 해제를 위한 별도의 코드를 작성할 필요가 없다다.

AutoCloseable 인터페이스를 구현한 클래스는 파일 입출력 스트림, 데이터베이스 연결 등 다양할 수 있다. 이러한 클래스의 인스턴스를 try-with-resources 구문 안에서 생성하면, 구문이 종료될 때 자동으로 자원이 해제된다. 이는 프로그램의 안정성을 높이고 메모리 누수를 방지하는 데 도움이 된다.

예를 들어, 파일 입출력을 다루는 경우, FileInputStream 클래스는 AutoCloseable 인터페이스를 구현한다. 따라서 FileInputStream 인스턴스를 try-with-resources 구문 안에서 사용하면, 파일 입출력 작업이 완료된 후 자동으로 스트림이 닫힌다.

AutoCloseable 인터페이스의 도입으로 자바에서의 자원 관리가 훨씬 간편해졌다. 왜냐하면 개발자가 자원 해제를 위한 코드를 직접 작성하지 않아도 되기 때문이다. 이는 코드의 가독성과 유지 보수성을 향상시키는 데 기여한다.

try-with-resources 구문의 사용법

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

0개의 댓글