Spring

AWS Presigned URL로 트래픽 낮추기

recent0 2024. 3. 28. 00:53

서론

2022년, 팀 프로젝트를 할 때 일기 쓰기 기능에서 이미지 저장을 다음과 같이 구현했었습니다.

  1. 클라이언트에서 업로드 하려는 파일을 서버에 전달
  2. 서버는 전달받은 파일을 S3에 저장
  3. S3에 저장 후 리턴되는 이미지 경로를 DB에 저장

여기서 저는 '이미지 업로드를 클라이언트에서 할 수 있지 않을까?' 라는 의문점이 들었습니다. 다만 그때는 깊이 생각해보지 못했고, 현재 다시 의문에 대한 해결책인 PresignedURL을 소개해 드리려고 합니다.

 

소개 및 사용 이유

Presigned URL은 AWS 에서 제공하는 기능으로, 이름 그대로 미리 서명된 URL을 활용하여 AWS S3에 객체를 업로드할 수 있습니다. 즉 Presigned URL을 활용하면 누구든 객체를 업로드 할 수 있습니다.

 

AWS Presigned URL을 사용하는데는 크게 두 가지가 있다고 생각했습니다.

1.  통신과정에서 트래픽을 낮추기 위해

이미지 출처 : https://levelup.gitconnected.com/how-to-secure-s3-objects-using-a-presigned-url-ea2b0c57ae86

클라이언트로부터 파일을 받고, 해당 파일을 다시 S3에 저장하는 과정에서 서버의 리소스가 필요하기에 상대적으로 서버의 부담이 높습니다. 그래서 위 그림처럼 클라이언트가 업로드한다면 서버에서 파일을 받는 과정과, 업로드하는 과정이 빠지기 때문에 서버의 부담이 줄어들게 됩니다.

2. 역할 분리

Presigned URL을 사용하면 자연스럽게 역할을 분리할 수 있습니다. 서버는 Access Key, Secret Key를 활용하여 보안을 담당하고, 클라이언트는 업로드에 대한 기능을 담당하여 과도하게 서버에 몰려있던 책임을 분리할 수 있습니다.

 

그러면 그냥 클라이언트 단에서 해당 키들을 가지고 있으면 안 되냐고 생각하실 수 있지만, 키가 브라우저에 노출될 수 있는 가능성을 염두에 둬야 하므로 백엔드를 통해 검증하는 과정을 거치게 됩니다.

 

프로젝트 도입 부분

제가 진행하고 있는 사이드 프로젝트는 거의 모든 부분에서 사진을 업로드 하고 있습니다. 반려견의 진료 기록을 진료 내역 이미지와 함께 저장한다고 가정했을 때, 다음과 같은 흐름으로 사진을 업로드 하고 조회할 수 있도록 구현했습니다.

1. 이미지 업로드 시 Presigned URL 발급

presigned URL 발급 서비스 코드

클라이언트에서 S3에 접근해 파일을 업로드 하기 위해 presignedURL을 발급받을 수 있는 API를 다음과 같이 작성했습니다. 앞에서 언급한 것처럼 PresignedURL을 통해 이미지를 업로드할 수 있기 때문에 URL 노출을 대비해 만료 시간을 정하는 작업까지 진행해 주었습니다.

 

2. Presigned URL을 통해 이미지 업로드

{
    "preSignedUrl": "https://test-bucket.s3.ap-northeast-2.amazonaws.com/test.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240302T073447Z&X-Amz-SignedHeaders=host&X-Amz-Expires=158&X-Amz-Credential=111&X-Amz-Signature=1b1629"
}

클라이언트는 위와 같은 형식의 presignedURL을 발급받은 후 해당 URL을 바탕으로 S3에 파일을 업로드합니다.

 

3. 클라이언트 요청을 통해 DB에 저장되어 있는 이미지 경로 최신화

presignedURL 적용 전

이전에는 MultipartFile을 통해 이미지를 받아 디렉터리를 지정하여, API 서버에서 S3에 직접 이미지 업로드를 진행했습니다. 그리고 업로드가 끝나면 리턴되는 이미지 경로를 DB에 저장해 조회에 사용할 수 있도록 구현했습니다.

 

PresignedURL을 적용 후 개선된 코드는 다음과 같습니다.

presignedURL 적용 후

 

이미지 경로 저장

presignedURL을 도입함으로써 클라이언트가 이미지 업로드를 끝마치고, Request에 이미지 경로를 담아 보내기 때문에 서버에서 S3로 이미지를 적재하는 과정이 생략되었습니다.

 

결과적으로 업로드 과정이 생략되었기 때문에 리소스 사용량을 줄일 수 있게 되었습니다.

 

API 성능 측정 후  전 후 비교 

Jmeter를 통해 대략 얼마정도의 차이가 있는지 알아보겠습니다.

 

API 서버에 요청을 보내는 세팅은 다음과 같습니다.

 

적용 전 TPS -> 64.5

presignedURL 적용 전

적용 후 TPS -> 149.6

presignedURL 적용 후

측정 방법마다 다르겠지만, 어느 정도 서버의 부담이 내려갔다는 점에서 충분히 이점이 있다고 판단할 수 있습니다.

 

추가로 사용할 수 있는 곳들

이번에는 클라이언트가 업로드 역할을 맡아 이를 처리했지만, 서버 또한 해당 작업이 가능합니다.

 

예를 들어 S3, DB가 분리된 두 개 이상의 서비스나 서버를 운영하고 있다고 가정했을 때, 다른 서비스에서 해당 서비스의 데이터가 필요하다면 서로 presignedURL을 발급하여 서버에 업로드가 가능할 것입니다.

 

이로써 간단하게 PresignedURL에 대해 소개하고 사례를 보여드렸습니다.

추가로 보완점이 필요한 부분이 있다면 코멘트 남겨주시면 참고하겠습니다.

 

감사합니다.