웹 또는 모바일 애플리케이션에서 Amazon S3로 직접 업로드

JIN·2022년 7월 18일
2

일반적인 서버 기반 환경의 프로세스

  1. 사용자가 파일을 애플리케이션 서버에 업로드한다.
  2. 애플리케이션 서버는 처리를 위해 업로드를 임시 공간에 저장한다.
  3. 응용 프로그램은 파일을 데이터베이스 , 파일서버 또는 영구 저장을 위한 개체 저장소로 전송한다.

프로세스는 간단하다. 하지만 부하가 많아질 경우 웹 서버의 성능에 심각한 부작용이 있을 수 있다. 왜냐하면 미디어 업로드는 일반적으로 크기 때문에 I/O 및 서버 CPU 시간의 많은 부분을 나타낼 수 있다. 또한 트래픽 패턴이 급증하는 애플리케이션의 경우 터질수도 있다.

이러한 파일을 S3에 직접 업로드하면 애플리케이션 서버를 통해 이러한 요청을 프록시 하는 것을 방지할 수 있다. 이렇게 하면 네트워크 트래픽과 서버 CPU 사용량을 크게 줄이고 응용 프로그램 서버가 사용량이 많을 동안 다른 작업을 수행할 수 있다.

서버리스 업로드 구현

S3 버킷에 직접 업로드 하는 경우 먼저 Amazon S3 서비스에서 서명된 URL ( preSigned URL)을 요청해야 한다. 그런 다음 서명된 URL을 사용하여 직접 업로드 할 수 있다.

애플리케이션 프론트앤드 2단계 프로세스

  1. 클라이언트가 API Gateway로 Request를 보내면 Lamda 가 preSingedURL함수를 호출한다. 그 후에 S3에서 preSignedURL을 가지고 온다.
  2. 애플리케이션에서 S3 버킷으로 파일을 직접 업로드 한다.

AWS 계정에 S3 업로더 예제 배포 :

  1. README.md에 나열된 전제조건 설치
  2. 터미널 창에서 실행
  3. 프롬프트에서 스택 이름을 S3uploader 입력, 원하는 리전 선택
  4. 배포 완료 되면 APIendpoint 출력 확인
  5. URL/uploads

애플리케이션 테스트 방법 2가지

  1. Postman 을 사용하여 API를 직접 호출하고 SignedURL이 포함된 바이너리 파일 업로드
  2. API 통합 방법을 보여주는 기본 프론트엔트 애플리케이션 사용

1번 방법으로 테스트

  1. 포스트맨으로 엔드포인트 요청(GET) 보내고 응답으로 SingedURL 받아오기
  2. PUT으로 요청 업로드 붙여넣고 바디 > 바이너리
  3. 파일 선택 JPG 선택 파일 업로드 후 200 ok 받음
  4. S3 콘솔에서 확인

2번 방법으로 테스트

  1. 예제의 리포지토리에서 S3버킷으로 index.html 복사
  2. 공개적으로 읽을 수 있도록 개체의 권한 업데이트
  3. 브라우저에서 html로 이동
  4. 파일 선택 후 이미지 업로드
  5. S3 콘솔에서 확인

S3 업로드 프로세스 이해

웹 어플리케이션에서 S3로 객체를 업로드 할 때 CORS 에 대해 S3를 구성해야 한다.

S3UploadBucket:
    Type: AWS::S3::Bucket
    Properties:
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
            - "*"
          AllowedMethods:
            - GET
            - PUT
            - HEAD
          AllowedOrigins:
            - "*"
            

SignedURL 을 호출하는 람다 함수

const AWS = require('aws-sdk')
AWS.config.update({ region: process.env.AWS_REGION })
const s3 = new AWS.S3()
const URL_EXPIRATION_SECONDS = 300

// Main Lambda entry point
exports.handler = async (event) => {
  return await getUploadURL(event)
}

const getUploadURL = async function(event) {
  const randomID = parseInt(Math.random() * 10000000)
  const Key = `${randomID}.jpg`

  // Get signed URL from S3
  const s3Params = {
    Bucket: process.env.UploadBucket,
    Key,
    Expires: URL_EXPIRATION_SECONDS,
    ContentType: 'image/jpeg'
  }
  const uploadURL = await s3.getSignedUrlPromise('putObject', s3Params)
  return JSON.stringify({
    uploadURL: uploadURL,
    Key
  })
}

업로드 프로세스에 인증 추가
현재 API 엔드포인트가 열려 있으며 인터넷의 모든 서비스에서 사용할 수 있다.
즉 SignedURL을 받으면 누구나 JPG 파일을 업로드할 수 있다. 대부분의 프로덕션 시스템에서 개발자는 인증을 사용하여 API에 액세스 할 수 있는 사람과 S3 버킷에 파일을 업로드 할 수 있는 사람을 제어

권한 부여자를 사용하자 JWT 권한 부여자 Amazon Cognito, Auth0과 같은 서비스가 될 수 있는 자격 증명 공급자를 통해 API 에 대한 액세스를 제어할 수 있다.

MyApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      Auth:
        Authorizers:
          MyAuthorizer:
            JwtConfiguration:
              issuer: !Ref Auth0issuer
              audience:
                - https://auth0-jwt-authorizer // 여기서 이 URL은 우리 프로젝트의 JWT URL이 되는 건가?
            IdentitySource: "$request.header.Authorization"
        DefaultAuthorizer: MyAuthorizer

ACL 수정 및 공개적으로 읽을 수 있는 객체 생성

s3Params에 넣어주면 되나보다
현재 구현에서 업로드된 객체는 공개적으로 액세스 할 수 없다.
업로드된 객체를 공개적으로 읽을 수 있도록 하려면 해당 ACL(액세스 제어 목록)을 설정해야 한다.

const s3Params = {
  Bucket: process.env.UploadBucket,
  Key,
  Expires: URL_EXPIRATION_SECONDS,
  ContentType: 'image/jpeg',
  ACL: 'public-read'
}

요청에 서명할 수 있는 적절한 버킷 권한이 있어야 한다
함수에 PutObjectAcl 권한이 있는지 확인

- Statement:
          - Effect: Allow
            Resource: !Sub 'arn:aws:s3:::${S3UploadBucket}/'
            Action:
              - s3:putObjectAcl

결론

서버리스 패턴은 서비스에서 네트워크 부하를 줄여줄 수 있다. 그래서 애플리케이션을 훨씬 더 확장 가능하게 만들고 급증하는 트래픽을 처리할 수 있다.

ver.1

Lambda(preSignedURL) > S3 > SQS(이벤트 알림) > Lamda(이벤트 받아서 Dynamo에 넣기) > DynamoDB 형태로 구현
1번 과정인 Lambda(Presigned Url) > S3
2번 S3 에서 SQS로 Polling 하여 값 넣기
3번 Lamda > DynamoDB

ver2.

Lambda용 S3 이벤트 소스 (S3에서 SQS로)
Lambda용 SQS 이벤트 소스 (SQS에서 DynamoDB로)
Lambda용 API 게이트웨이 이벤트 소스 (Dynamo에 대한 API 게이트웨이)

  1. API Gateway Endpoint로 presignedURL 생성
  1. GET 요청으로 uploadURL 받아오기
  1. PUT 요청으로 uploadURL에 파일을 담아서 S3에 업로드
  1. S3에 이미지 업로드 됨
  1. Lambda용 S3 이벤트 소스
    파일이 대상 S3 버킷에 복사될 때마다 S3 이벤트 알림이 Lambda 의 비동기식 호출을 트리거합니다.AWS에 따르면 함수를 비동기식으로 호출하면 Lambda가 이벤트를 SQS대기열로 보냅니다. 별도의 프로세스가 대기열에서 이벤트를 읽고 Lambda 함수를 실행합니다.

  2. Python으로 작성된 Lambda의 함수 핸들러는 이벤트에 파일 이름이 포함된 webm 파일을 읽습니다. Lambda는 webm 파일의 url을 추출하고 각 메시지를 SQS로 푸시합니다.

참고링크
https://aws.amazon.com/ko/blogs/compute/uploading-to-amazon-s3-directly-from-a-web-or-mobile-application/
https://dev.classmethod.jp/articles/lim-lambda-s3-sqs/
https://dev.classmethod.jp/articles/lim-lambda-s3-2021/

profile
배우고 적용하고 개선하기

0개의 댓글