Life is connecting the dots .

AWS Presigned URL, 클라이언트 이미지 업로드 적용 과정 본문

Programming

AWS Presigned URL, 클라이언트 이미지 업로드 적용 과정

soyeori 2024. 11. 21. 10:12

프로젝트 [리스티웨이브]의 어드민 게시물/공지 기능을 만들면서 이미지업로드 기능을 만들어야 했다. 우리 서비스의 특성상 사용자가 이미지 업로드를 빈번하게 하고, 또 이번에 어드민 기능의 게시물을 업로드하는 과정에도 이미지 업로드를 여러 번 할 수 있다. 이처럼 이미지를 업로드할 때 AWS의 S3를 이용해서 다음과 같은 절차로 이미지가 업로드된다.

 

참고로 aws S3(Amazon Simple Storage Service)는 데이터를 버킷(컨테이너) 내의 객체로 저장하는 객체 스토리지 서비스이다. 여기서 객체는 해당 파일을 설명하는 모든 메타데이터이고, 버킷은 객체에 대한 컨테이너로 생각하면 된다. Amazon S3에 데이터를 저장하려면 (1) 버킷을 생성하고, 버킷 이름 및 AWS 리전을 지정하고, (2) 해당 버킷에 객체로 데이터를 업로드 하면 된다. 각 객체에는 고유한 키가 있고, 이는 곧 버킷 내 객체를 구별하는 식별자가 된다.

 

이미지 업로드 과정

 

이미지 업로드는 파일 용량이 크기 때문에 일반적인 JSON을 주고받는 API요청에 비교하면 훨씬 부하가 큰 작업이다. 따라서 위에서 설명한 것처럼 빈번한 이미지 업로드가 이루어질 때 이미지 업로드 과정이 백엔드 서버를 거치게 되면 서버의 과부하를 일으킬 수 있다. 서버의 과부하를 막고자 동시에 업로드하는 요청을 제어하는 등 방법을 사용하여 우회적으로 이미지 업로드를 제한하는 방법도 있지만 이는 우리 서비스 특성에 맞지 않고, 무엇보다 사용자 경험에 좋지 않다. 이를 개선하기 위해 Presigned URL을 사용해 이미지를 업로드하는 방법을 선택하게 되었다.

 

Presigned URL

AWS에 나와있는 설명을 토대로 정리해 보자면, Presigned URL은 S3 버킷의 객체에 임시로 접근할 수 있는 링크이다. 이 URL을 통해 특정 시간 동안 다운로드할 수 있고, 반대로 업로드할 수 있다. 

 

주요 기능

  1. 다운로드 허용:
    • 이 URL을 통해 특정 파일을 제한된 시간 동안 다운로드할 수 있다.
    • AWS 사용자 계정으로 로그인하지 않아도 URL만 알면 접근이 가능하다.
    • URL을 만든 AWS 사용자의 권한을 대신 사용하는 방식이다.
  2. 업로드 허용:
    • 다른 사람이 파일을 S3 버킷에 업로드할 수 있도록 허용할 수도 있다.
    • 업로드할 때 AWS 자격 증명이나 추가 권한 설정이 필요하지 않다.
    • 만약 같은 이름(키)의 파일이 이미 있다면, 새로 업로드된 파일로 덮어씌워진다.
  3. 만료 시간 설정:
    • URL은 유효 기간 동안 여러 번 사용할 수 있지만, 만료되면 더 이상 작동하지 않는다.

필요성

  1. 보안 강화:
    • Presigned URL 생성 시 설정한 유효기간 동안만 링크가 유효하며 만료되면 접근이 불가능하기 때문에 잘못된 요청을 제한할 수 있다.
    • 백엔드 서버는 비밀키와 접근키를 사용해 AWS와 통신하고, 자원에 대한 권한을 부여하는 Presigned URL을 생성한다. 즉, 백엔드에서 URL 생성으로 보안과 권한 설정을 담당하기 때문에 클라이언트에서 Presigned URL을 이용해 S3에 직접 접근할 수 있도록 허용해도 클라이언트에서 민감한 정보(비밀키)에 접근할 수 없으므로 보안을 유지할 수 있다.
  2. 대역폭 관리:
    • 클라이언트는 S3와 직접 통신하므로, 서버가 업로드 작업을 대신 처리하지 않아도 되어 성능 면에서도 효율적이다.
  3. 편의성:
    • 사용자가 AWS 계정이나 권한이 없어도 안전하게 파일을 업로드 또는 다운로드할 수 있게 허용해 줌으로써 편의성을 제공한다.

 

Presigned URL을 이용한 업로드 과정

 

Presigned URL 사용한 업로드 과정

 

위의 그림을 바탕으로 Presigned URL을 이용한 업로드 과정을 정리해 보면,

  1. 사용자가 이미지 업로드 시도
  2. 백엔드 서버에 Presigned URL을 생성 요청
  3. 서버가 S3에 Presigned URL 발급 요청
  4. S3로부터 전달받은 Presigned URL을 클라이언트로 전달
  5. 클라이언트는 리스폰스로 받은 Presigned URL에 PUT 메소드를 사용하여 직접 이미지 업로드
  6. 완료 후, 클라이언트가 백엔드 서버에 업로드를 성공했다고 알림

 

이렇게 순서가 이루어진다. 2번의 백엔드 서버에 Presigned URL을 생성 요청은 다음과 같이 이미지 확장자(type)를 Request 페이로드로 보내준다. 여기서 order필드는 게시물의 순서이다. 우리는 게시물 생성 방식으로 블록형태를 선택했는데, 게시물의 내용물 타입(본문, 소제목, 버튼, 이미지 등)을 추가해서 작성하는 형태여서 이미지 블록이 추가된 순서를 함께 보내주는 것이다.

 

Presigned URL을 생성 요청시 Request Body

 

 

3, 4번 과정을 거쳐 전달받은 리스폰스는 다음과 같이 order에 해당하는 Presigned URL이 담겨있다. 

[
    {
        "order": 4,
        "presignedUrl": "https://listy-wave-images.s3.ap-northeast-2.amazonaws.com/api/ac438b6e-5bb5-4cff-bc5a-3cfb148295ff.jpeg?..."
    },
    {
        "order": 7,
        "presignedUrl": "https://listy-wave-images.s3.ap-northeast-2.amazonaws.com/api/7a8f028c-796c-4f76-a446-7fd30797878f.jpeg?..."
    }
]

 

이후 5번의 과정으로 클라이언트는 리스폰스로 받은 Presigned URL에 PUT 메소드를 사용하여 직접 이미지를 업로드한다. 위에서 전달받은 Presigned URL로 각각 이미지 업로드 요청을 보낸 것을 확인할 수 있다.

 

 

마지막으로 클라이언트가 백엔드 서버에 생성된 게시물 시퀀스와 함께 이미지 업로드 성공을 알리는 API를 호출한다. 

 

구현하는 과정에서 조금 헷갈렸던 부분은 게시물을 생성하는 과정과 이미지를 업로드하는 과정의 순서였다. 보통은 이미지를 업로드 후 그 url을 담아서 최종 게시물을 생성하는 API를 호출한다. 하지만 Presigned URL은 사용자 쪽에서 이미지를 업로드하는 것이므로 먼저 빈 값으로 게시물을 생성하고, 성공적으로 게시물이 생성된다면 그때 이미지 업로드를 하고 생성된 게시물과 매칭시켜 주는 것이다.

 

최종 업로드 과정

  1. 게시물 생성, 이때 이미지가 있다면 이미지 필드는 빈 값
  2. 사용자가 이미지 업로드 시도
  3. 백엔드 서버에 Presigned URL을 생성 요청, 이때 생성된 게시물 고유 아이디와 함께 전달
  4. 서버가 S3에 Presigned URL 발급 요청
  5. S3로부터 전달받은 Presigned URL을 클라이언트로 전달
  6. 클라이언트는 리스폰스로 받은 Presigned URL에 PUT 메소드를 사용하여 직접 이미지 업로드
  7. 완료 후, 클라이언트가 백엔드 서버에 업로드를 성공했다고 알림, 이때 생성된 게시물 고유 아이디와 함께 전달

 

 


참고

Medium - [AWS/NCLOUD] S3 Presigned-URL에 대하여

인프런

AWS Docs - Presigned URL

AWS Docs - S3