WIL/웹 개발

Presigned URL을 사용한 파일 업로드 구현

아크리미츠 2024. 10. 15. 20:27

프리사인드 URL이란?

프리사인드 URL(Presigned URL)은 클라이언트가 서버를 거치지 않고, 직접 클라우드 저장소(예: AWS S3)에 파일을 업로드할 수 있도록 임시 권한을 제공하는 URL. 이 URL은 일정 시간 동안만 유효하고, 그 시간 안에만 지정된 파일을 업로드할 수 있음.

장점:

  • 서버 부하 감소: 파일이 서버를 거치지 않고 클라이언트에서 클라우드로 바로 업로드되기 때문에 서버 자원이 절약됨.
  • 보안 강화: URL은 제한된 시간 동안만 유효하고, 업로드할 수 있는 파일도 미리 지정되므로 안전함.
  • 업로드 속도 향상: 클라이언트가 직접 파일을 올리기 때문에 업로드 속도가 더 빠름.

클라이언트 코드: 파일 업로드와 폼 제출

1. 파일 선택하고 폼 제출

const fileInput = document.getElementById('image');
const file = fileInput.files[0];

if (!file) {
  document.getElementById('uploadForm').submit();
  return;
}
  • 사용자가 파일을 선택하지 않으면 그냥 폼을 제출하고 끝냄.
  • 파일이 선택되었으면, 해당 파일을 처리하기 위해 프리사인드 URL을 요청하는 과정을 시작함.

2. 프리사인드 URL 요청

const fileName = encodeURIComponent(file.name);
const response = await fetch(`/presigned-url?filename=${fileName}`);
const presignedUrl = await response.text();
  • 파일 이름을 URL에 맞게 인코딩해서 서버에 프리사인드 URL을 요청함.
  • 서버에서 반환된 프리사인드 URL을 통해 파일을 업로드할 준비를 함.

3. 파일 업로드

const uploadResult = await fetch(presignedUrl, {
  method: 'PUT',
  headers: {
    'Content-Type': file.type
  },
  body: file
});
  • 프리사인드 URL을 사용해서 클라우드 저장소에 파일을 업로드함.
  • 파일의 타입(MIME)을 헤더에 지정하고, PUT 메소드로 파일 데이터를 전송함.

4. 업로드 성공 시 폼 제출

if (uploadResult.ok) {
  const imageUrl = presignedUrl.split("?")[0];
  document.getElementById('imageUrl').value = imageUrl;
  document.getElementById('uploadForm').submit();
}
  • 파일 업로드에 성공하면, 업로드된 파일의 URL을 추출해 폼의 imageUrl 필드에 설정함.
  • 이후 폼을 제출해서 서버로 다른 데이터와 함께 파일 URL도 전송함.

전체 코드

async function uploadAndSubmit(event) {
  event.preventDefault();

  const fileInput = document.getElementById('image');
  const file = fileInput.files[0];

  if (!file) {
    document.getElementById('uploadForm').submit();
    return;
  }

  const fileName = encodeURIComponent(file.name);

  const response = await fetch(`/presigned-url?filename=${fileName}`);
  const presignedUrl = await response.text();

  const uploadResult = await fetch(presignedUrl, {
    method: 'PUT',
    headers: {
      'Content-Type': file.type
    },
    body: file
  });

  if (uploadResult.ok) {
    const imageUrl = presignedUrl.split("?")[0];
    document.getElementById('imageUrl').value = imageUrl;

    document.getElementById('uploadForm').submit();
  } else {
    console.error("파일 업로드 실패", uploadResult);
    alert("파일 업로드에 실패했습니다.");
  }

  return false;
}

서버 코드: 프리사인드 URL 생성하기

클라이언트가 프리사인드 URL을 요청하면, 서버에서 그 URL을 생성해줘야 함. AWS S3의 프리사인드 URL을 생성하는 비즈니스 로직은 아래와 같음.

서버에서 프리사인드 URL 생성하기

// Controller

@GetMapping("/presigned-url")
@ResponseBody
String getURL(@RequestParam String filename) {
    var result = s3Service.createPresignedUrl("test/" + filename);
    return result;
}

// Service

public String createPresignedUrl(String path) {
    var putObjectRequest = PutObjectRequest.builder()
        .bucket(bucket)
        .key(path)
        .build();
    var preSignRequest = PutObjectPresignRequest.builder()
        .signatureDuration(Duration.ofMinutes(3))
        .putObjectRequest(putObjectRequest)
        .build();
    return s3Presigner.presignPutObject(preSignRequest).url().toString();
}
  1. PutObjectRequest는 AWS S3에 업로드할 파일의 버킷과 경로(파일 이름)를 설정함.
  2. PutObjectPresignRequest는 프리사인드 URL의 유효 시간을 설정하고, 3분 동안 URL이 유효하도록 설정함.
  3. s3Presigner를 통해 프리사인드 URL을 생성하고, 해당 URL을 반환함.

서버에서는 클라이언트가 보낸 파일 경로를 받아서 이 URL을 생성해 클라이언트에 전달해줌. 클라이언트는 이 URL을 사용해 S3에 파일을 업로드하게 됨.


전체 플로우

  1. 파일 선택: 클라이언트에서 사용자가 파일을 선택함.
  2. 프리사인드 URL 요청: 클라이언트는 서버에 파일 이름을 보내서 프리사인드 URL을 요청함.
  3. 프리사인드 URL 생성: 서버는 AWS S3와 연동해 프리사인드 URL을 생성하고 클라이언트에 반환함.
  4. 파일 업로드: 클라이언트는 받은 프리사인드 URL을 사용해 파일을 S3에 업로드함.
  5. 폼 제출: 업로드가 완료되면, 클라이언트는 업로드된 파일의 URL을 폼에 포함시켜 제출함.