development/Spring Boot

Spring @Async 환경에서 MultipartFile "파일을 찾을 수 없음" 에러 해결하기

root@soni 2025. 8. 16. 18:34
Spring Boot에서 파일 업로드 기능을 구현하던 중, 비동기 처리를 위해 @Async 어노테이션을 사용했을 때 다음과 같은 에러가 발생했습니다.
java.io.FileNotFoundException: /tmp/tomcat.xxx/work/Tomcat/localhost/ROOT/upload_xxx.tmp (No such file or directory)

또는

java.lang.IllegalStateException: The temporary upload location is not valid

문제 발생 원인

 

1. HTTP 요청 생명주기와 MultipartFile

Spring에서 MultipartFile은 HTTP 요청의 생명주기에 의존합니다.

HTTP 요청 시작 → MultipartFile 임시 저장 → 요청 처리 → HTTP 응답 완료 → 임시 파일 자동 삭제

2. @Async 메서드의 실행 타이밍

@Async 어노테이션이 붙은 메서드는 별도의 스레드 풀에서 실행되며, 호출 즉시 반환됩니다.

public void processFilesAsync(MultipartFile imageFile, MultipartFile videoFile) {
    uploadAsync(imageFile, videoFile); // 비동기 호출 후 즉시 반환
} // HTTP 응답 완료 → MultipartFile 임시 파일 삭제

@Async
public void uploadAsync(MultipartFile imageFile, MultipartFile videoFile) {
    // 실제 실행은 이후에 발생 (임시 파일이 이미 삭제된 상태)
}

3. 타이밍 문제

  1. HTTP 요청 처리: MultipartFile이 임시 디렉토리에 저장됨
  2. 컨트롤러 응답: HTTP 응답이 즉시 반환됨
  3. 임시 파일 정리: Spring이 MultipartFile의 임시 저장소를 자동으로 삭제
  4. @Async 실행: 별도 스레드에서 실행되지만 이미 파일이 삭제된 상태

해결 방법

1. MultipartFile을 File 객체로 사전 변환

public void processFilesAsync(MultipartFile imageFile, MultipartFile videoFile) throws IOException {
    // HTTP 요청이 유효한 동안 File 객체로 변환
    File savedImageFile = fileStorageHelper.saveToTemp(imageFile);
    File savedVideoFile = fileStorageHelper.saveToTemp(videoFile);
    
    // 이제 안전하게 비동기 처리
    uploadAsync(savedImageFile, savedVideoFile);
}

@Async
public void uploadAsync(File imageFile, File videoFile) {
    // File 객체로 안전하게 접근 가능
    uploadToCloud(imageFile);
    uploadToCloud(videoFile);
}

2. FileStorageHelper 구현

public File saveToTemp(MultipartFile multipartFile) throws IOException {
    String uniqueFilename = UUID.randomUUID().toString() + getFileExtension(multipartFile.getOriginalFilename());
    Path targetPath = Paths.get("/tmp/uploads").resolve(uniqueFilename);
    multipartFile.transferTo(targetPath.toFile());
    return targetPath.toFile();
}

해결 후 실행 흐름

  1. HTTP 요청 수신
  2. MultipartFile → File 변환 (안전한 위치에 저장)
  3. HTTP 응답 반환
  4. MultipartFile 임시 파일 자동 삭제 (문제없음)
  5. @Async 메서드 실행 (File 객체로 안전하게 접근)
  6. 처리 완료 후 임시 File 객체들 수동 삭제

추가 고려사항

메모리 효율성

  • MultipartFile은 파일 내용을 메모리에 보관
  • File 객체는 디스크 기반으로 메모리 효율적

Thread Safety

  • MultipartFile은 thread-safe하지 않을 수 있음
  • File 객체는 여러 스레드에서 안전하게 접근 가능

에러 처리

  • 파일 변환 과정에서 발생할 수 있는 IOException 처리
  • 임시 파일 정리 로직의 예외 처리

Spring의 @Async와 MultipartFile을 함께 사용할 때는 HTTP 요청의 생명주기를 고려해야 합니다. 비동기 처리 전에 MultipartFile을 안전한 File 객체로 변환하는 것이 핵심 해결책입니다.

이 방법을 통해 파일 업로드의 안정성을 확보하고, 대용량 파일 처리 시에도 메모리 효율성을 유지할 수 있습니다.