파일 업로드 서비스에 썸네일 생성 시스템 도입기
파일 업로드 서비스에 썸네일 생성 시스템 도입기
moseoh
aws python

파일 업로드 서비스에 썸네일 생성 시스템 도입기

moseoh · 2025년 07월 14일

파일 업로드 서비스를 운영하면서 사용자 경험을 개선해야 할 문제가 생겼습니다. 이번 글에서는 AWS Lambda와 Spring Boot를 연동하여 서버리스 기반의 썸네일 생성 시스템을 구축한 과정과 그 과정에서 마주한 고민들을 공유하겠습니다.

왜 썸네일 생성기가 필요했을까?

저희 파일 업로드 서비스에는 다양한 형식의 파일들이 업로드되고 있었습니다. 그런데 몇 가지 문제가 점점 심각해지고 있었어요.

미리보기가 어려운 파일 형식들

특히 STL(3D 모델), TIF 파일, PDF 등은 웹 브라우저에서 바로 미리보기하기가 까다로웠습니다. TIF 파일의 경우 웹 표준이 아니어서 대부분의 브라우저에서 직접 렌더링이 되지 않거든요. 사용자들은 파일을 다운로드해야만 내용을 확인할 수 있었고, 이는 분명히 불편한 경험이었습니다.

성능 문제

원본 이미지가 큰 경우 로딩 속도가 현저히 느려지는 문제도 있었습니다. 10MB가 넘는 고해상도 이미지를 목록에서 미리보기로 보여주려면 사용자가 한참을 기다려야 했어요. 특히 모바일 환경에서는 더욱 심각했습니다.

비용 및 트래픽 최적화

원본 파일을 그대로 서빙하다 보니 트래픽 비용이 계속 증가하고 있었습니다. 사용자가 파일 목록을 볼 때마다 큰 용량의 원본 파일이 전송되고 있었거든요. 향후 CDN을 도입할 계획인데, 썸네일이 있으면 CDN에서 캐싱할 데이터 양도 줄어들고 전송 비용도 크게 절약될 것 같았습니다.

🤔 여러 설계 방법 중에서 선택하기

썸네일 생성을 구현하는 방법은 여러 가지가 있었습니다. 하지만 저희의 특수한 업로드 구조 때문에 선택지가 제한적이었어요.

현재 시스템 구조

이 문제를 해결하기 전에 먼저 저희 파일 업로드 시스템의 구조를 설명해야겠어요. 저희 시스템은 다음과 같이 동작합니다:

클라이언트(웹/앱) → Spring Boot 서버 (PreSigned URL 발급) → 클라이언트가 S3로 직접 업로드 → 업로드 완료 후 서버에 메타데이터 전송

핵심은 클라이언트가 서버를 거치지 않고 S3에 직접 업로드한다는 점입니다. 이 구조는 서버 대역폭을 절약하고 업로드 성능을 개선하는 장점이 있지만, 파일 업로드 시점에 서버에서 직접 후처리 작업을 할 수 없다는 제약이 있었습니다.

고려했던 선택지들

1. 동기식 처리 (업로드와 동시에 썸네일 생성)

현재 시스템에서는 불가능합니다. 클라이언트가 S3에 직접 업로드하기 때문에 서버가 업로드 시점을 실시간으로 알 수 없거든요.

2. 백그라운드 작업 큐 (Spring Boot 내부)

마찬가지로 불가능합니다. 파일이 서버를 거치지 않기 때문에 서버 내부에서 바로 처리할 수 없어요.

3. 별도 마이크로서비스

  • 장점: 독립적인 확장 가능, 장애 격리

  • 단점: 인프라 복잡성 증가, 운영 부담 4. AWS Lambda (서버리스)

  • 장점: 자동 확장, 비용 효율성, 관리 부담 최소화

  • 단점: 콜드 스타트, 실행 시간 제한 결국 현실적으로 선택할 수 있는 방법은 별도 마이크로서비스AWS Lambda 두 가지였습니다.

왜 AWS Lambda를 선택했을까?

현재 인프라 관리자가 부족한 상황에 있어서 관리 포인트를 최소화하는 것이 중요했습니다. Lambda를 선택한 이유는:

  1. 관리 부담 최소화: 별도의 서버를 띄우고 모니터링하고 장애 대응을 할 필요가 없습니다.
  2. 확장성: 파일 업로드가 몰리는 시간대가 불규칙한데, Lambda는 자동으로 확장되어 별도 설정 없이도 트래픽 변화에 대응할 수 있습니다.
  3. 비용 효율성: 썸네일 생성은 파일이 업로드될 때만 필요한 작업이므로, 사용한 만큼만 비용을 지불하는 모델이 적합했습니다. 무료 사용량 또한 넉넉했어요.

썸네일 생성 결과를 어떻게 전달할까?

Lambda를 선택한 후 다음 고민은 썸네일 생성이 완료되었을 때 그 결과를 Spring Boot 서버에 어떻게 알려줄 것인가였습니다. Lambda는 S3 이벤트에 의해 트리거되어 독립적으로 실행되기 때문에, 작업 완료 후 그 결과를 메인 서버에 전달하는 메커니즘이 필요했어요.

고려했던 알림 방법들

1. Lambda에서 직접 DB 접근

가장 간단해 보이는 방법이었지만, Lambda에 DB 쓰기 권한을 주는 것은 보안상 좋지 않다고 생각했습니다. 또한 DB 스키마 변경이나 비즈니스 로직이 변경될 때마다 Lambda 코드도 함께 수정해야 하는 강결합 문제도 있었어요.

2. AWS IAM Role 기반 접근

Lambda에 IAM Role을 추가하는 것 자체는 어렵지 않았습니다. 하지만 문제는 Spring Boot 서버에서 Lambda만 접근할 수 있는 엔드포인트를 어떻게 구현하느냐였어요. AWS API Gateway + IAM 인증을 사용하거나, Spring Boot에서 AWS SigV4 서명을 검증하는 로직을 구현해야 하는데, 이는 기존의 단순한 REST API 구조와는 맞지 않았습니다. 결국 “Lambda 전용 보안 엔드포인트”를 만들기 위해 추가로 구현해야 할 부분들이 많아 복잡도가 높아졌어요.

3. 이벤트 기반 방식 (SQS/SNS)

SQS나 SNS 같은 AWS 메시징 서비스를 사용하는 방법도 있었지만, 저희 시스템에는 이벤트 기반 아키텍처가 전혀 구축되어 있지 않았습니다. 단순히 SQS나 SNS만 추가하는 게 아니라, Spring Boot에서 메시지를 받아 처리하는 전체 이벤트 처리 체계를 새로 만들어야 했어요. 이는 썸네일 기능 하나를 위해서는 너무 큰 작업이었습니다.

4. 직접 API 호출 (Webhook)

  • 장점: 간단한 구현, 추가 AWS 서비스 불필요, 기존 REST API 구조 그대로 활용
  • 단점: 네트워크 실패 시 재시도 로직 필요

최종 선택: Webhook

결국 Webhook 방식을 선택했습니다. 가장 큰 이유는:

  1. 기존 구조 활용: 이미 잘 동작하고 있는 REST API 구조를 그대로 사용할 수 있었습니다.
  2. 추가 인프라 불필요: 새로운 AWS 서비스나 이벤트 처리 시스템을 도입할 필요가 없었어요.
  3. 보안 경계 명확: Lambda는 단순히 HTTP 요청만 보내고, Spring Boot가 모든 비즈니스 로직과 DB 접근을 담당하는 명확한 역할 분리가 가능했습니다. 인프라 관리자가 부족한 상황에선 단순함이 최고의 덕목이었고, 썸네일 기능 하나를 위해 시스템 전체 아키텍처를 바꾸는 것보다는 현실적인 해결책을 찾는 것이 중요했습니다.

핵심 구현 내용

Spring Boot: Webhook 수신

썸네일 생성 완료 알림을 받을 엔드포인트를 구현했습니다. 보안을 위해 Secret Key 검증을 추가했어요.

@PostMapping("/thumbnails")
public ResponseEntity<ApiResponse> handleThumbnail(@RequestBody ThumbnailRequest request) {
validateSecret(request.getSecret()); // <-- secret 키 검증, 실제로는 Header를 통해 검증합니다.
fileService.updateThumbnail(request.getFileId(), request.getThumbnailUrl());
return ResponseEntity.ok().build();
}

Lambda: 썸네일 생성 및 알림

S3 이벤트를 받아 썸네일을 생성하고 완료되면 Spring Boot 서버로 알림을 보냅니다.

def lambda_handler(event, context):
for record in event['Records']:
file_key = record['s3']['object']['key']
# 썸네일 생성
thumbnail_url = process_thumbnail(file_key)
# 완료 알림
notify_completion(file_key, thumbnail_url)

보안 고려사항

Webhook은 공개 엔드포인트이기 때문에 보안이 중요했습니다. 가장 단순하면서도 효과적인 방법으로 미리 공유된 Secret Key를 HTTP 헤더에 담아 전송하고 서버에서 검증하는 방식을 사용했습니다. 복잡한 JWT나 OAuth 같은 인증 체계를 도입하기에는 과도했고, 이 정도면 충분히 안전하다고 판단했어요.

리소스 할당

Lambda의 경우 RAM 할당량을 통해 CPU를 알아서 할당해 줍니다. 이미지, PDF의 경우 512mb 만으로도 1초 미만의 실행시간으로 충분하였지만 stl 과 같은 3D 파일의 썸네일을 생성하기 위해는 많이 모자랐습니다. 때문에 120초라는 긴 타임아웃 시간과 1024MB 메모리를 할당해 주었습니다.

확장 가능성

환경 변수를 통해 썸네일 크기, 품질, Webhook URL 등을 관리하여 코드 수정 없이도 설정을 변경할 수 있도록 했습니다.

새로운 파일 형식 지원을 쉽게 추가할 수 있도록 파일 확장자별 분기 처리 구조로 설계했습니다. 현재는 이미지, PDF, STL 파일을 지원하지만, 새로운 형식이 필요하면 해당 처리 로직만 추가하면 됩니다.

if file_ext in ['.jpg', '.png']:
return create_image_thumbnail(file_path)
elif file_ext == '.pdf':
return create_pdf_thumbnail(file_path)
# 새로운 형식 처리 로직 추가

또한 향후 요구사항이 변경되어 여러 크기의 썸네일이나 다양한 포맷으로 변환이 필요해도 기존 구조를 크게 바꾸지 않고 확장할 수 있습니다.

실제 운영 결과와 아쉬운 점

긍정적인 효과

  1. 로딩 속도 개선: 기존 원본 파일 대신 최적화된 썸네일로 페이지 로딩 속도가 크게 향상되었습니다.
  2. 비용 절감: 트래픽이 줄어들면서 데이터 전송 비용이 감소했습니다.
  3. 사용자 경험 개선: STL, TIF, PDF 등의 파일도 미리보기가 가능해져 사용성이 향상되었습니다.

예상치 못한 문제점

즉시성 부족이 가장 큰 문제였습니다. 사용자가 파일을 업로드한 직후 바로 썸네일을 확인할 수 없어서, 특히 PDF나 큰 용량 파일의 경우 “썸네일이 언제 생기는지” 사용자가 알 수 없었어요. 비동기 처리의 장점을 얻은 대신 사용자 경험에서는 트레이드오프가 발생한 셈이었습니다.

개선 방향

이런 문제를 해결하기 위해 몇 가지 개선 방안을 고려하고 있습니다:

폴링 기반 상태 확인

썸네일이 아직 생성되지 않은 파일에는 “썸네일 준비중” 플레이스홀더를 보여주고, 주기적으로 서버에 썸네일 생성 완료 여부를 확인하는 방식입니다. 간단하지만 불필요한 요청이 많이 발생할 수 있어요.

실시간 상태 업데이트

Server-Sent Events(SSE)나 WebSocket을 활용하여 썸네일 생성 진행 상황을 실시간으로 알려주는 방법도 있습니다. 사용자 경험은 가장 좋지만 구현 복잡도가 높아집니다.

동기식 처리로 전환

아예 발상을 바꿔서 업로드와 썸네일 생성을 동기적으로 처리하는 방법도 고려해볼 만합니다. 사용자가 업로드 버튼을 누르면 “파일 업로드 및 썸네일 생성 중…” 로딩 화면을 보여주고, 모든 처리가 완료된 후에 결과를 보여주는 거죠. 처리 시간은 길어지지만 사용자 입장에서는 예측 가능한 경험을 제공할 수 있습니다.

각각의 방법은 구현 복잡도와 사용자 경험 측면에서 서로 다른 트레이드오프를 가지고 있어서, 서비스의 성격과 사용자 패턴을 고려해 적절한 방법을 선택해야 할 것 같습니다.

마무리

관리포인트를 최소화 하면서 AWS Lambda와 Webhook을 활용한 서버리스 썸네일 생성 시스템을 구축할 수 있었습니다. 기술적 완성도보다는 관리 부담을 최소화하면서도 실용적인 해결책을 찾는 것이 목표였는데, 그 관점에서는 성공적이었다고 생각합니다.

완벽한 시스템은 아니지만, 제한된 리소스 내에서 사용자 경험을 개선할 수 있는 효과적인 솔루션이었습니다. 앞으로 사용자 피드백을 더 개선하여 더 나은 서비스를 제공할 수 있도록 지속적으로 발전시켜 나갈 예정입니다. 땡큐.