스프링 웹 애플리케이션 파일 처리 (스트림)

유기훈·2025년 2월 8일

스프링 웹 애플리케이션에서는 파일을 어떻게 처리할까

예시

스프링 웹 애플리케이션에서 클라이언트로부터 이미지 파일을 받아서 AWS S3에 이미지를 저장하는 API가 있다고 해보자. 그렇다면 이미지 파일이 10MB인 경우에 이미지 파일을 Java Heap Memory에 모두 저장해야 하는 걸까? 만약 그렇다면, 동시에 요청이 몰리면 OOM이 날텐데 어떡하지?

결론은 NO이다. 스프링 그리고 서블릿 컨테이너(ex. 톰캣)가 어떻게 파일을 처리하는 지 알아보자.

파일 업로드 과정

파일 임시 저장

클라이언트(ex. 브라우저)가 HTTP POST 요청으로 이미지를 전송하면, 이 데이터는 네트워크를 통해 서버로 전달된다. 서버에 도착한 파일 데이터는 운영체제의 임시 디렉토리(ex. /tmp)에 저장된다. 이건 대부분의 서블릿 컨테이너가 기본적으로 처리하는 방식이다. 아래 그림처럼 socket을 통해 받은 데이터를 Servlet Container가 임시 디렉토리에 Write한다. 이 때 서블릿은 알아서 적잘한 Stream을 쓰기 때문에 Heap Memory를 많이 차지하지 않는다. 그리고 임시저장된 파일은 사용이 끝나면 알아서 삭제된다.

MultipartFile 생성

파일 업로드가 끝나면, 서블릿 컨테이너는 이 임시 파일에 대한 레퍼런스를 MultipartFile로 제공한다. 이 객체를 통해 파일 내용을 다시 읽거나 처리할 수 있다. 웹 애플리케이션의 도메인 로직을 개발하는 개발자는 MultipartFile 객체를 통해 파일의 메타데이터와 파일 내용에 접근할 수 있다.
MultipartFile 중요 메서드

  • getInputStream(): 파일 내용을 읽을 수 있는 InputStream 반환.
  • getBytes(): 파일 전체 내용을 바이트 배열로 반환. (전체 데이터를 한 번에 읽어오는 거라 메모리 부담 큼. 사용 X)

예시 코드

Java에서는 InputStream을 통해 클라이언트로부터 받은 파일을 스트림 방식으로 S3에 바로 업로드할 수 있다. 이렇게 하면 파일 전체를 메모리에 올리지 않고 업로드 가능하다.

import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.core.sync.RequestBody;

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/upload")
public class FileUploadController {

    private final S3Client s3Client;

    public FileUploadController(S3Client s3Client) {
        this.s3Client = s3Client;
    }

    @PostMapping
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        String bucketName = "your-bucket-name";
        String key = file.getOriginalFilename();

        try (InputStream inputStream = file.getInputStream()) {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(key)
                .contentType(file.getContentType())
                .build();

            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(inputStream, file.getSize()));

            return "File uploaded successfully!";
        } catch (IOException e) {
            e.printStackTrace();
            return "File upload failed!";
        }
    }
}

그 외

파일 임시 저장 방법 바꾸기

위에서 서블릿 컨테이너는 클라이언트로 부터 받은 파일을 임시 경로에 저장한다고 했다. 임시 경로에 저장하는 건 Disk에 저장한다는 말과 동일하다. 헌데 크기가 얼마 안되는 데이터라 처리 속도를 향상하기 위해 메모리에 저장할 수는 없을까? 물론 가능하다. 스프링 설정으로 가능하다. 아래와 같이 설정하면 파일이 2MB보다 작으면 메모리에서 처리하고 크면 디스크에 임시 저장한다.

spring.servlet.multipart.file-size-threshold=2MB
profile
개발 블로그

0개의 댓글