Lambda 함수로 .pdf 파일 업로드 후 첫번째 페이지 썸네일 이미지 생성하기

현재 진행하고 있는 프로젝트에서 구현한 이미지 업로드 기능을 개선한 과정을 정리해보았다.

문제


> 기존 Backend 디렉토리

프론트에서 .pdf파일을 업로드 할 경우, public 폴더에 안에 디렉토리를 생성했다.
files 폴더는 .pdf파일 원본, images/file 폴더는 .pdf파일 한장 한장을 이미지로 생성한 파일들과 uploads 폴더는 썸네일 이미지 한 폴더로 구분하여 저장하는 방식으로 구성되어 있다.

> Backend에서 모든 로직을 처리 이러한 로직 구성은 디스크 공간 낭비와 파일 조회 속도 저하를 초래하기 때문에 개선방법을 찾아보았다.

개선 방법


> 개선된 flow

  1. 클라이언트(React)가 서버(Nest.js)에 Presigned URL을 요청.
  2. 서버가 S3에 대한 임시 접근 권한이 있는 Presigned URL을 생성하여 클라이언트에게 반환.
  3. 클라이언트는 받은 Presigned URL을 사용하여 파일(PNG, PDF)을 직접 S3 버킷으로 업로드(PUT 요청).
  4. 파일이 S3에 저장되면 S3 이벤트가 트리거되어어 Lambda 함수를 실행시켜 PDF 첫번째 페이지를 이미지화하고 리사이징.
  5. 처리가 완료되면 클라이언트는 서버에 POST 요청을 보내 업로드가 완료되었음을 알림.
  6. 서버는 Presigned URL과 관련 메타데이터를 데이터베이스에 저장.

이 아키텍처를 사용해서 개선하므로써,
서버를 거치지 않고 클라이언트가 직접 S3에 파일을 업로드하므로 서버 부하가 감소하고 Lambda를 통한 서버리스 이미지 처리로 확장성이 높다. Presigned URL을 사용할때 만료 시간을 부여 하여 보안을 유지하면서도 효율적인 업로드가 가능며 S3와 Lambda의 이벤트 기반 아키텍처로 비동기 처리까지 가능하게 했다.

Lambda 함수에서 임시 파일 경로를 사용하는 이유


Lambda는 서버리스 환경으로, 영구적인 파일 시스템에 접근할 수 없는 대신 /tmp 디렉토리만 쓰기 가능한 임시 저장소로 제공된다.

Lambda 함수는 pdftoppm 명령줄 도구를 사용하여 PDF를 이미지로 변환한다. 이러한 외부 프로세스는 실제 파일 경로를 인자로 받아야 하므로, S3에서 가져온 PDF 데이터를 임시 파일로 저장해야 한다.

> Lambda 함수 동작 방식

적용 방법 - AWS Setting


1. IAM 사용자 생성 후 액세스 키를 발급받는다.

• 권한 : AmazonS3FullAccess

2. 역할(Role) 생성.

• 신뢰할 수 있는 엔터티 유형 : AWS 서비스
• 사용 사례 > 서비스 또는 사용 사례 : Lambda
• 권한 추가 > 권한 정책 :

- AmazonS3FullAccess
- AWSLambdaBasicExecutionRole

• 역할 이름 : generateThumbnail

3. S3 버킷 생성

• 버킷 이름 : bucket
• Amazon 리소스 이름(ARN) : arn:aws:s3:::bucket
• AWS 리전 : 아시아 태평양(서울) ap-northeast-2
• 모든 퍼블릭 액세스 차단 - 비활성
• CORS(Cross-origin 리소스 공유)

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE",
            "HEAD"
        ],
        "AllowedOrigins": [
            "http://localhost:3000"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

> 버킷 생성 시, 체크 확인

4. Lambda 함수 생성

• 함수 이름 : pdf-thumbnail-lamda
• 런타임 : Node.js 22.x
• 아키텍처 : x86_64
• 기본 실행 역할 변경 > 기존 역할 사용 : generateThumbnail

5. 트리거 추가

• 트리거 구성 : S3
• 버킷 : s3/bucket
• 이벤트 유형 : 모든 객체 생성 이벤트
• 접두사 : public/ebook
> 트리거 추가 > 구성 셋팅

6. S3 이벤트 알림 설정

> S3 이벤트 알림 설정

적용 방법 - Lambda Code


1. VS Code

> 디렉토리

2. Lambda 코드 소스 업로드

3. 외부 라이브러리 Layers 추가

4. Lambda 계층 생성 및 추가

> 계층 생성

> 계층 추가

5. 테스트

> 이벤트 JSON

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "ap-northeast-2", // 버킷이 생성된 리전
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "bucket", // 버킷 
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::bucket" // 버킷 ARN
        },
        "object": {
          "key": "public/ebook/test.pdf", // 실제 경로에 업로드된 파일
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

> 테스트 로그 확인

> 썸네일 이미지 생성 확인

마무리


프론트 개발자에서 풀스택으로 일하게 되면서 백엔드에서는 파일 업로드 기능을 어떻게 처리하는지에 대해 알게 되었고, AWS를 사용해 보면서 S3와 Lambda의 개념에 대해서도 공부할 수 있었다.

앞으로도 좀 더 개선할 수 있는 방법에 대해 공부하면서 개선해 나가야겠다.

참고
Lambda를 활용한 S3 이미지 리사이징
[AWS S3+Lambda] 이미지 처리 2탄: Image Resizing으로 썸네일 이미지 만들기
[AWS] 📚 람다 - 이미지 리사이징 서비스 (S3 / API Gateway) 구현하기

AWS Lambda S3 FileUpload