다크 모드
업로드 파이프라인
truloop-media-service의 스트리밍 업로드 흐름, 202 Accepted 패턴, S3 multipart 업로드 구현을 설명합니다.
업로드 흐름 개요
202 Accepted 패턴
업로드 요청 시 파일을 S3에 저장한 직후 즉시 응답합니다. 이미지/비디오 변환은 백그라운드에서 비동기 처리됩니다.
Client → [업로드] → 202 Accepted (즉시 반환)
↓
[SQS + Redis 큐 발행]
↓
[MediaWorker: Redis 폴링] → 변환 생성 → SQS → truloop-core 알림주의
에러 처리: SQS 발행이 실패하면 이미 업로드된 S3 파일을 삭제하고 idempotency key도 정리합니다. Redis 큐 Push 실패는 업로드를 실패시키지 않으며 로그만 기록합니다 (SQS가 primary).
응답 예시
json
{
"eid": "ABC123XYZ",
"url": "https://cdn.truloop.app/users/.../uuid.jpg",
"status": "processing",
"uploaded_at": "2026-03-10T12:00:00Z",
"message": "File uploaded successfully as PHOTO"
}정보
202 Accepted는 "요청은 수락되었지만 처리는 아직 완료되지 않았다"를 의미합니다. 클라이언트는 eid를 사용하여 이후 상태를 추적할 수 있습니다. eid는 TSID로 생성됩니다.
S3 업로드 전략
파일 크기에 따라 최적의 업로드 방식을 자동 선택합니다.
| 파일 크기 | 업로드 방식 | 설명 |
|---|---|---|
| < 100MB | PutObject (단일 업로드) | AWS 권장 방식, 오버헤드 최소화 |
| >= 100MB | Multipart Upload | 청크 분할 후 병렬 업로드 |
Multipart Upload 처리 과정
Multipart 초기화
CreateMultipartUpload로 업로드 세션을 시작합니다.
동적 청크 분할 및 병렬 업로드
파일 크기에 따라 청크 크기가 자동 결정됩니다. Worker Pool을 통해 병렬 업로드합니다.
ETag 수집
각 청크 업로드 완료 시 반환되는 ETag를 수집합니다.
Multipart 완료
모든 ETag를 포함하여 CompleteMultipartUpload를 호출합니다. 실패 시 AbortMultipartUpload를 호출합니다.
동적 청크 크기
| 파일 크기 | 청크 크기 |
|---|---|
| < 1GB | 10MB |
| 1GB ~ 5GB | 25MB |
| >= 5GB | 100MB |
Dynamic Worker Pool
- Worker 수는 CPU 코어 기반으로 자동 결정:
numCPU * 3(I/O bound 최적화) - 최소 Worker 수:
max(maxWorkers/2, 1) - 최대 Worker 수:
MAX_S3_UPLOAD_WORKERS(0이면 자동 감지) - 부하에 따라 동적으로 Worker 수 조정 (큐 크기 기반)
메모리 최적화
| 기법 | 설명 |
|---|---|
| Zero-copy Streaming | 전체 파일을 메모리에 로드하지 않고 청크 단위로 S3에 전송 |
| Buffer Pool | 재사용 가능한 sync.Pool 기반 버퍼 풀로 GC 부담 감소 (bufpool 패키지) |
| Size-adaptive Buffers | 파일 크기에 따라 5MB/10MB/25MB/100MB 풀에서 버퍼 할당 |
Idempotency (중복 방지)
Redis를 사용하여 중복 업로드를 방지합니다.
| 항목 | 설명 |
|---|---|
| 키 구조 | {idempotency_key}:loop:{loop_eid} → 상태 + 결과 매핑 |
| 범위 | loop_eid 단위 (같은 룹 내 중복 키 방지) |
| TTL | 1년 |
| 상태 | processing (진행 중) → completed (완료) |
| 헤더 | Idempotency-Key 요청 헤더로 전달 |
| 용도 | 네트워크 재시도 중복 방지, 비즈니스 로직 중복 업로드 방지 |
흐름
파일 검증
업로드 전 다음 항목을 검증합니다:
| 검증 항목 | 규칙 |
|---|---|
| 파일 확장자 | 이미지: .jpg, .jpeg, .png, .gif, .webp, .bmp, .tiff |
비디오: .mp4, .mov, .avi, .mkv, .webm, .m4v, .flv | |
| 파일 크기 | 최대 2GB (Content-Length 검사) |
| 필수 필드 | file (multipart), loop_eid |
| 미디어 타입 | Internal API에서는 type 파라미터 필수 (PHOTO, VIDEO, POSTER, HIGHLIGHT) |
제한 사항
서버 과부하를 방지하기 위한 제어:
| 항목 | 기본값 |
|---|---|
| 파일 크기 제한 | 2GB |
| 읽기/쓰기 타임아웃 | 30분 |
| Multipart 메모리 버퍼 | 32MB |
| MaxHeaderBytes | 1MB |
큐 메시지
업로드 완료 시 (이중 발행)
업로드 직후 SQS와 Redis Job Queue 양쪽에 메시지를 발행합니다.
| 큐 | 용도 | 소비자 |
|---|---|---|
SQS (media-upload-queue) | 서비스 간 이벤트 | truloop-core |
Redis (media:jobs) | 내부 처리 작업 | MediaWorker |
media-ready-queue (변환 완료 시)
MediaWorker가 변환 처리 완료 후 SQS에 MediaReadyMessage를 발행합니다. truloop-core의 SQS Consumer가 이를 폴링하여 미디어 상태를 업데이트합니다.
정보
비디오의 경우, 썸네일 생성과 트랜스코딩이 병렬로 실행되며 각각 독립적인 media-ready-queue 메시지를 발행합니다 (incremental visibility).
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-03-10 | 소스 코드 기반 전면 검증: 업로드 흐름 다이어그램 (크기 기반 전략, Redis), 에러 처리 힌트, 응답 예시, 동적 청크 크기, Worker Pool (numCPU*3), Idempotency (composite key, CheckAndReserve), 파일 검증 (확장자 목록), 이중 큐 발행 |