AWS S3 presignedURL을 이용해서 image Upload 하기

기록일기📫·2021년 7월 7일
47
post-thumbnail

진행중인 프로젝트에서 게시판(editor)이나, 유저 프로필 등 이미지 업로드가 필요하여 여느때와 같이 Best Practice에 관해 찾아보게 되었다.

AWS presigned URL을 이용해서 S3 bucket에 업로드 하는 방식으로 구현하였는데, 꽤 괜찮은 방법인것 같아 포스팅을 통해 공유하려 한다😊

해당 포스팅은 https://medium.com/developing-koan/uploading-images-to-s3-from-a-react-single-page-application-45a4d24af09f 를 참조하여 작성되었습니다.


Presigned URL이 뭐죠? 🤷‍♀️

본격적으로 시작하기 전에, presigned URL에 대해 먼저 알아보자. S3 버켓에 이미지를 업로드 하기 위해서는 해당 S3에 대한 접근 권한을 인증해야 한다.

접근 권한에 대한 인증을 마치면 S3에 업로드 할 수 있는 URL을 발급해 주는데, 이 URL을 preSigned(pre-signed) URL라고 한다.

발급받은 presigned URL을 이용하면 브라우저에서 AWS S3 버킷에 바로 파일을 업로드 할 수 있다.

따라서 우리는 서버에서 S3에 대한 접근 권한을 인증 받은 뒤 발급 받은 presigned URL을 이용해 브라우저에서 직접 업로드 하는 방식으로 이미지 업로드를 구현해 볼 것이다!


왜 굳이 Presigned URL?

그러면 왜 서버에서 파일을 업로드 하지 않고 귀찮게 preSigned URL을 발급받는 과정을 거쳐서 브라우저에서 업로드를 하게 하는걸까?

서버에서 S3 버킷으로 파일을 업로드 하려면 우선 업로드 하려는 파일을 브라우저에서 서버로 보낸 뒤, 해당 파일을 S3 버킷으로 업로드 하는 과정을 거쳐야 한다.

즉 브라우저 -> 서버 -> S3 순으로 이미지를 전달하는것이다.

이 과정은 자원이 많이 소모되고 비효율적인데, 그 이유는 저장하지도 않을 무거운 이미지 파일을 서버로 전송하는 과정이 포함되어 있기 때문이다.

다른 방법으로는 서버를 배제한 채 presigned URL 발급 로직 및 업로드 로직을 브라우저에서 전부 진행하는 방법이 있다. 하지만 이 또한 브라우저의 보안이 굉장히 취약하단 점을 감안했을 때 좋지 않은 방법이다.

preSigned URL을 발급 받는 과정에서 S3에 대한 접근 인증 권한을 가진 credential을 탈취 당하게 된다면 누구나 나의 S3 버켓에 접근할 수 있게 된다. 프로덕션 환경에서 credential을 탈취 당해 유저가 등록한 이미지들이 모두 삭제됐다고 생각해보면 정말 엄청난 재앙이 아닐 수 없다.

이러한 이유로 백엔드에서 S3에 대한 접근 권한 인증 및 preSigned URL을 발급 받아 브라우저에 전달하고, 브라우저에서 이를 사용해 직접 이미지를 업로드 하는 방법을 채택하였다.


시작! 🙌

자, 그럼 시작해보자. 전체 프로세스는 대략 이러하다.

  1. 이미지 업로드 요청 시 서버 api 호출
  2. 서버에서 AWS S3에 preSignedURL 요청
  3. AWS에서 preSignedURL을 return
  4. 서버는 브라우저로 preSignedURL을 전달
  5. 브라우저에서 AWS preSignedURL로 이미지 upload
  6. 서버에게 해당 요청이 종료 되었음을 알림

조금 쉽게 설명해보면, 브라우저에서 '나 이미지 올리고 싶어. 올릴 수 있는 URL을 줘!'하고 서버에 요청을 보낸다. 그러면 서버는 다시 aws에 요청하여 preSignedURL을 받아 클라이언트에 전달해준다.

마지막으로 브라우저는 해당 Url을 이용해서 PUT 요청을 통해 업로드할 이미지를 전송하는 방식이다.

실제로 프로젝트에 적용했던 코드를 보며 각 과정을 자세히 뜯어보자.

1. 이미지 업로드 요청 시 서버 api 호출

위 코드는 quill editor에 이미지 drag & drop, 이미지 copy & paste가 가능하도록 커스터마이징 한 imageHandler를 설정해주는 부분이다.

유저가 에디터에 이미지를 업로드 하게 되면, getPresignedUrl 함수를 호출한다.

함수 내부에서는 서버에 preSignedURL 발급 요청을 보내 response를 받아온다.

2~4 서버에서 AWS S3에 preSignedURL 요청후 브라우저에게 전달

서버는 브라우저에 요청을 받으면 aws s3에 요청하여 preSignedURL을 얻어온다.

참고로, AWS-SDK를 이용하여 프로그래밍을 통해 S3에 접근하려면 AWS 계정에서 권한을 가진 User를 생성해 Secret Key를 발급받아야 한다.

해당 내용은 검색을 통해 잘 설명된 자료를 쉽게 구할 수 있다. 본 포스팅에서는 생략하겠다.

5~6 브라우저가 AWS preSignedURL로 이미지 upload하고 서버에 해당 요청이 종료되었음을 알림

브라우저는 서버에서 preSignedURL를 return받고, PUT 메서드를 통해 해당 url로 이미지 업로드 요청을 보내게 된다.

이로써 브라우저에서 S3에 직업 파일을 업로드 할 수 있게 된다!


동작 확인

자! 이제 모든 과정이 종료되었다. 그러면 실제로 잘 동작하는지 확인해보자.

에디터에 복사 붙여넣기 혹은 drag & drop을 통해 이미지를 업로드 한 후에,

브라우저에서도 개발자 도구를 켜서 element를 확인해 보면, 이렇게 S3로 정상 업로드가 된 것을 확인할 수 있다!

AWS S3에서도 파일 정상 업로드를 확인 가능하다 :)


후기

역시 삽질도 하다보면 재밌다(?)

이미지 업로드가 필요하여 S3를 통한 이미지 업로드 하는 방식을 찾아보고 있었는데, 검색하다 발견한 해당 포스팅이 많은 도움이 되었다. 😊

https://medium.com/developing-koan/uploading-images-to-s3-from-a-react-single-page-application-45a4d24af09f

국내에는 관련 글이 생각보다 많지 않아 진행하는데 난관을 조금 겪었지만 결국 어찌저찌(?) 해결했다. 사실 중간에 조금 많이 헤맸어서, 처음 에디터에 정상 입력된 이미지를 보았을 때는 굉장히 감동적이었다.😂😂

현재 진행중인 프로젝트는 다 AWS의 서비스를 이용해 개발하고 있는데, 어렵지 않게 나만의 개발환경을 구성하고 배포할 수 있다는 점이 클라우드의 가장 큰 매력인 것 같다.

AWS가 제공하는 서비스에 대해 조금 더 공부해보고 싶은데, 자꾸 미뤄지는 것 같아 자격증 시험을 접수 하고 도전해 볼지 고민 중이다.

나도 컨트리뷰터!

아! 또 기념할만한 일이 하나 있었다. quill 에디터에 이미지 drag & drop 대응을 위해 quill-image-drop-and-paste 라이브러리를 사용했다.

이미지에서는 문제가 없었는데, 텍스트를 에디터에 붙여넣었을때 커서가 텍스트의 끝까지 도달하지 않고 처음 텍스트에서 계속 유지되었다.

레포에 issue로 남겨둘까 하다가, 코드를 직접 수정해서 PR을 보내 보았는데 머지가 되었다! 첫 contributor가 되는 경험이라 굉장히 뿌듯했다!🎉🎉🎉

기분좋은 순간을 기념할 겸해서 올려본다😊😊

이제 거의 완성되었다! 프로젝트 회고록을 올리는 그날까지 조금만 더 열심히 달려봐야겠다🦾

reference

이 포스팅은 아래 글들을 참조하여 작성하였다.

https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/PresignedUrlUploadObject.html

https://medium.com/developing-koan/uploading-images-to-s3-from-a-react-single-page-application-45a4d24af09f

4개의 댓글

comment-user-thumbnail
2021년 7월 14일

좋은 글 감사합니다. 저도 비슷한 기능을 구현 중이라 도움 받은 것 같네요!

1개의 답글
comment-user-thumbnail
2023년 6월 1일

Excellent stuff; I'll let my friends know about it and have them check it out. I appreciate you sharing! Whenever you have more time, go to: quordle

답글 달기