파일 다운로드 예외가 발생해도 빈 파일이 다운로드 되는 상황

현재 fileStoreService에서 예외가 발생하면 catch에 들어가서 예외처리가 발생하지만

동시에 파일이 다운로드되는 상황이 발생했다.

// 타임캡슐 파일 저장
    @GetMapping(value = "/download/zip/timecapsule/{timecapsuleNo}/file")
    public ResponseEntity<DataResponse<Map<String, Object>>> downloadZip(@PathVariable("timecapsuleNo") Long timecapsuleNo, HttpServletRequest request, HttpServletResponse response) throws IOException, InterruptedException {
        try{
						response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.addHeader("Content-Disposition", "attachment; filename=" + new String((RandomStringUtils.randomAlphanumeric(6) + "-s3-download.zip").getBytes("UTF-8"), "ISO-8859-1"));

            String prefix = getPrefix(request.getRequestURI(), "/s3/download/zip/");
            fileStoreService.downloadZip(prefix, response, timecapsuleNo);
            // 정상적인 경우
            return new ResponseEntity<>(new DataResponse<>(200, "다운로드 성공"),
                    HttpStatus.OK);
        }catch (CommonException e) {
            // 예외 상황에 따른 ResponseEntity 반환
            return new ResponseEntity<>(new DataResponse<>(e.getCustomExceptionStatus().getCode(), e.getCustomExceptionStatus().getMessage()),
                    HttpStatus.OK);
        } catch (Exception e) {
            // 기타 예외 처리
            return new ResponseEntity<>(new DataResponse<>(500, "알 수 없는 에러가 발생했습니다. 잠시 후 다시 시도해주세요."), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

그 문제가 생겼던 이유는

response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.addHeader("Content-Disposition", "attachment; filename=" + new String((RandomStringUtils.randomAlphanumeric(6) + "-s3-download.zip").getBytes("UTF-8"), "ISO-8859-1"));

여기서 발생했는데, Service에 들어가기 전에 response 헤더에 상태를 전부 설정해주어서

클라이언트는 정상적인 응답을 받았다고 판단했지만 실제로는 예외처리가 발생한 상황이 된 거다.

그래서 해결 방법으로 response를 service 안으로 옮겨버리기로 결정했다.

// (1)
            // 서버 로컬에 생성되는 디렉토리, 해당 디렉토리에 파일이 다운로드된다
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.addHeader("Content-Disposition", "attachment; filename=" + new String((RandomStringUtils.randomAlphanumeric(6) + "-s3-download.zip").getBytes("UTF-8"), "ISO-8859-1"));
            File localDirectory = new File(RandomStringUtils.randomAlphanumeric(6) + "-s3-download.zip");

            try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
                // (2)
                // TransferManager -> localDirectory에 파일 다운로드
                MultipleFileDownload downloadDirectory = transferManager.downloadDirectory(bucket, prefix, localDirectory);

                // (3)
                // 다운로드 상태 확인
                log.info("[" + prefix + "] download progressing... start");
                DecimalFormat decimalFormat = new DecimalFormat("##0.00");
                while (!downloadDirectory.isDone()) {
                    Thread.sleep(1000);
                    TransferProgress progress = downloadDirectory.getProgress();
                    double percentTransferred = progress.getPercentTransferred();
                    log.info("[" + prefix + "] " + decimalFormat.format(percentTransferred) + "% download progressing...");
                }
                log.info("[" + prefix + "] download directory from S3 success!");

                // (4)
                // 로컬 디렉토리 -> 압축하면서 다운로드
                log.info("compressing to zip file...");
                addFolderToZip(zipOut, localDirectory);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                // (5)
                // 로컬 디렉토리 삭제
                FileUtil.remove(localDirectory);
            }
            return new DataResponse<>(200, "성공");

위에서 예외처리가 다 끝나면 response에 넣어주는 방법으로 바꾸었다.

만약에 try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {

여기 밑으로 response를 넣게 된다면 stream이 열려있기 때문에 실제로 헤더나 상태 코드의 변경은 이루어지지 않는 문제가 발생한다.

service로 response를 옮겨서 해당 문제를 해결할 수 있었다.