SnowGlow 배포 기록: YouTube → MP3 변환을 CloudFront/S3로 안정화하기

김기수·2026년 4월 28일

https://deji3nqzrstvf.cloudfront.net/

이 글은 SnowGlow(겨울 Snow Visualizer + YouTube MP3 변환)의 실제 구축/디버깅 과정을 기록한 개발 로그입니다.
목표는 “YouTube URL을 넣으면 변환 진행률이 보이고, 완료 후 CloudFront URL로 안정적으로 재생/시크(206 Range)되는” 구조를 만드는 것이었습니다.


TL;DR (결론)

  • 프론트는 CloudFront + S3(정적) 로 배포
  • 백엔드는 EC2 + Docker(Express + yt-dlp + ffmpeg) 로 운영
  • 변환 결과(mp3)는 S3 업로드 후 CloudFront URL로 재생
  • 오디오 시크 안정화는 CloudFront/S3의 Byte-range(206) 로 해결
  • CloudFront 하나로 묶는다면 behavior는 최소 3개가 핵심:
    • /* → 프론트 S3
    • /api/* → 백엔드(NLB)
    • /jobs/*.mp3 → 오디오 S3

1) 문제 정의: 왜 이 구성이 필요했나?

1-1. YouTube 변환 서버 단일 구성의 한계

YouTube를 변환하는 워커(yt-dlp + ffmpeg)는 CPU/네트워크를 크게 사용한다.
API 서버와 변환을 한 서버에서 같이 돌리면 다음 문제가 발생하기 쉽다.

  • 변환 요청이 몰릴 때 API 응답이 느려짐/다운됨
  • 변환된 파일을 서버 로컬에 두면 용량/정리/재시작 내구성이 약함
  • 브라우저 시크(재생 위치 이동)를 위해 HTTP Range(206) 지원이 필요한데, 서버 스트리밍만으로는 운영/캐시가 복잡해짐

1-2. 목표 UX

  • 사용자는 YouTube URL 입력
  • 서버는 변환 진행률을 job/polling으로 제공
  • 변환 완료 후 mp3 재생 + 시크가 안정적이어야 함

2) 최종 아키텍처

2-1. 데이터 흐름

  1. Client → POST /api/youtube/jobs (YouTube URL)
  2. Server(EC2)에서 yt-dlp + ffmpeg로 mp3 생성
  3. mp3를 S3에 업로드 (jobs/<jobId>.mp3)
  4. Server는 job 상태에 audioUrl(CloudFront URL)을 저장
  5. Client는 GET /api/youtube/jobs/:jobId로 polling → done이면 audioUrl로 재생

2-2. CloudFront 1개로 묶기(Front + API + Audio)

  • Default /* → Front S3
  • /api/* → NLB(백엔드)
    • Cache: CachingDisabled
    • Methods: All
  • /jobs/*.mp3 → Audio S3
    • 필요 시 CORS 헤더(특히 Web Audio 분석)

3) 핵심 이슈 & 해결 로그

3-1. “Missing required env: S3_BUCKET”

원인:

  • 실제 코드 수정 문제가 아니라 이전 node 프로세스가 그대로 떠있어서 새 .env가 반영되지 않음

해결:

  • 기존 프로세스 종료 후 재시작(포트 점유 확인 포함)

3-2. CloudFront URL은 탭에서 재생되는데 앱에서 “no supported source”

원인:

  • <audio crossOrigin="anonymous">로 Web Audio 분석을 하려면
    CloudFront 응답에 CORS 헤더가 있어야 함

해결:

  • CloudFront behavior에 Response headers policy로 CORS 허용(Managed CORS 정책 사용)

3-3. yt-dlp 에러: Impersonate target "chrome:windows-10" is not available

원인:

  • Alpine 기반 컨테이너에서 yt-dlp의 --impersonate chrome:windows-10이 요구하는 의존성이 없어 타겟이 비활성화됨

해결:

  • 서버의 yt-dlp 기본 플래그에서 impersonate 제거
  • Docker 이미지 재빌드/푸시 후 EC2에서 pull + 재시작

3-4. S3 업로드 에러: Could not load credentials from any providers

원인:

  • EC2에서 AWS SDK가 사용할 자격증명(Instance Role 또는 env)을 못 찾음

해결(권장):

  • EC2에 IAM Role(Instance Profile) 연결
  • 최소권한 정책으로 S3 PutObject 가능하게 구성

참고:

  • AmazonS3FullAccess는 빠른 원인 분리엔 도움이 되지만 운영에선 비추천(권한 과다)

3-5. CloudFront 504 Gateway Timeout

증상:

  • 서버 내부 localhost:3000 호출은 성공하는데,
    CloudFront를 통해 /api/... 호출하면 504

원인:

  • CloudFront 오리진을 NLB에 붙일 때 HTTPS(443)로 설정했지만
    실제 NLB/백엔드가 TLS를 제공하지 않는 구성(HTTP)이라 연결 실패

해결:

  • CloudFront → NLB 오리진은 우선 HTTP only로 연결
  • 전 구간 TLS가 필요하면, 나중에:
    • NLB에 TLS(443) 리스너 + ACM 인증서로 TLS 종료(옵션)

3-6. yt-dlp 봇 체크: Sign in to confirm you’re not a bot

원인:

  • 일부 영상에서 유튜브가 로그인/봇체크를 강제
  • 서버가 “사용자 브라우저 쿠키”를 직접 읽을 수 없으므로(웹 보안 정책) 상시 우회가 어렵다

현실적인 해결:

  • 서버에 --cookies를 줄 수 있도록 cookies.txt 파일 경로를 env로 지원
    • YTDLP_COOKIES_FILE=/run/secrets/youtube-cookies.txt
    • 호스트에 cookies.txt를 두고 컨테이너에 read-only로 마운트

운영 포인트:

  • 쿠키는 만료될 수 있고, 만료 시 다시 실패할 수 있음
  • 민감정보이므로 git 커밋 금지 + 최소 권한 + 파일 권한(600) 권장

4) 배포 가이드 (요약)

4-1. 프론트 배포 (S3 업로드)

cd client
npm run build
aws s3 sync dist s3://snowglow-front/ --delete

변경 반영이 느리면 invalidation:

aws cloudfront create-invalidation --distribution-id <DISTRIBUTION_ID> --paths "/*"

4-2. 서버 배포 (EC2 + Docker)

sudo docker pull rlarltn/snowglow-server:latest

sudo docker stop snowglow-server || true
sudo docker rm snowglow-server || true

sudo docker run -d --restart unless-stopped \
  -p 3000:3000 \
  --env-file .env \
  --name snowglow-server \
  rlarltn/snowglow-server:latest

쿠키 파일을 쓰는 경우:

chmod 600 /home/ubuntu/secrets/youtube-cookies.txt

sudo docker run -d --restart unless-stopped \
  -p 3000:3000 \
  --env-file .env \
  -v /home/ubuntu/secrets/youtube-cookies.txt:/run/secrets/youtube-cookies.txt:ro \
  --name snowglow-server \
  rlarltn/snowglow-server:latest

5) 운영 팁

  • CloudFront behavior 캐시
    • /api/*는 CachingDisabled 권장
    • /jobs/*.mp3는 필요에 따라 캐시 정책을 조절(대개 캐시가 이득)
  • SPA 라우팅
    • Default root object /index.html
    • 403/404 → /index.html (200) 매핑
  • 관측(로그)
    • job 단위 로그를 남기면(성공/실패/yt-dlp stderr) 운영 대응이 빨라짐

profile
엄청난 클라우드 고수

0개의 댓글