-
yotube 요약 n8n 워크플로우 구축기개발 관련 2026. 5. 21. 13:06반응형
Oracle Cloud Always Free + Docker로 n8n 자동화 구축기 — YouTube 신규 영상 자막을 Notion에 자동 정리하기
0원으로 24시간 돌아가는 자동화 서버를 만들고 싶었다. 결론부터 말하면 됐다. 그런데 만드는 과정에서 Oracle IP가 YouTube에 차단된다는 사실을 알게 됐고, 라이브러리도 두 개나 갈아치웠고, n8n에서는 4연속으로 에러를 만났다. 이 글은 그 구축기다.
목표
내가 만들고 싶었던 건 단순했다.
- 특정 유튜브 채널에 새 영상이 올라오면 자동 감지
- 해당 영상의 자막(스크립트) 추출
- AI로 요약
- 원본 자막 + 요약을 한 행으로 Notion 데이터베이스에 적재
한 줄짜리 목표였는데, 막상 부딪혀보니 YouTube 생태계의 봇 차단·API 정책·서비스 outage를 한 바퀴 다 돌게 됐다.
최종 환경
- 서버: Oracle Cloud Always Free VPS (Ubuntu 22.04)
- 워크플로우 엔진: n8n (Docker 컨테이너로 운영)
- 신규 영상 감지: YouTube Data API v3
- 자막 추출: youtube-transcript.io API
- AI 요약: OpenAI gpt-4o-mini
- 저장소: Notion API
처음부터 이 구성이었던 게 아니다. 자막 추출은 자체 마이크로서비스로 시작했다가 두 번 갈아엎었고, 신규 영상 감지도 RSS로 시작했다가 outage 만나서 공식 API로 옮겼다. 그 과정이 본론이다.
왜 Oracle Cloud Always Free인가
n8n을 24시간 돌리려면 항상 켜져 있는 서버가 필요하다. 처음엔 로컬 PC에서
npx n8n으로 띄워봤는데, PC를 끄면 자동화가 멈춘다. 의미가 없다.VPS를 알아보다 결론은 두 갈래였다.
- 월 4~6달러 짜리 Hetzner/PikaPods — 안정적이고 설정도 쉽다
- Oracle Cloud Always Free — 진짜 0원, 단 가입 시 카드 등록 필요
Oracle Cloud Always Free는 ARM Ampere A1 인스턴스를 최대 4 OCPU / 24GB RAM / 200GB 스토리지까지 무료로 준다. 사양이 무료치고 말이 안 된다.
가장 걱정됐던 건 카드 결제였는데, 알아보니 계정을 직접 "Pay As You Go"로 업그레이드하지 않는 한 자동 결제는 일어나지 않는다. Always Free 계정 상태에서는 카드가 결제 수단으로 활성화조차 되어 있지 않다. 한도를 넘으면 자원이 그냥 생성이 안 될 뿐 결제로 넘어가지 않는다.
단, 30일 $300 트라이얼 크레딧과 Always Free 자원은 다르다. 트라이얼 크레딧을 쓰는 유료 서비스를 잘못 켜두면 30일 후에 곤란해질 수 있다. Always Free 태그가 붙은 자원만 골라 써야 한다.
단점은 두 가지다. Oracle이 가끔 무료 인스턴스를 경고 없이 종료한다는 점(그래서 워크플로우 백업 필수), ARM 인스턴스가 "용량 부족"으로 생성 자체가 안 될 때가 많다는 점. 나는 운 좋게 한 번에 됐다.
1단계: Oracle VM 만들기
Oracle Cloud 콘솔에서 인스턴스를 만들 때 핵심만 짚으면 이렇다.
- Shape:
VM.Standard.A1.Flex(ARM) 또는VM.Standard.E2.1.Micro(AMD, 1GB RAM) - Image: Canonical Ubuntu 22.04
- Networking: 새 VCN 만들기, public IP 할당
- SSH key: 본인 공개키 등록
나는 AMD 1GB로 시작했다. ARM이 사양은 좋은데 생성이 잘 안 되기도 하고, n8n 혼자 돌리는 데는 AMD도 된다(스왑만 잘 잡으면).
생성 후 ssh 접속:
ssh -i ~/.ssh/oracle_key ubuntu@본인IP2단계: 1GB RAM 살리기 — 스왑 2GB
AMD 1GB RAM 인스턴스에서 n8n을 그냥 띄우면 메모리 부족으로 시작도 못한다. 스왑을 잡아줘야 한다.
sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab확인:
free -hSwap:줄에2.0Gi가 보이면 됨.3단계: 방화벽 — Oracle 콘솔 + 서버 양쪽
여기서 첫 번째 함정. Oracle 콘솔의 보안 목록(Security List) 과 서버 내부 iptables 가 둘 다 막혀 있어서, 양쪽 다 열어야 한다.
Oracle 콘솔 보안 목록
Networking → Virtual Cloud Networks → 본인 VCN → Security Lists → Default Security List → Add Ingress Rules
- Source CIDR:
0.0.0.0/0 - IP Protocol:
TCP - Destination Port Range:
5678
서버 내부 iptables
sudo iptables -I INPUT 6 -m state --state NEW -p tcp --dport 5678 -j ACCEPT sudo netfilter-persistent save처음에 Oracle 콘솔만 열고 한참 헤맸다. Ubuntu 이미지에 iptables 룰이 기본으로 박혀 있다는 걸 모르면 답이 안 나온다.
4단계: Docker 설치
sudo apt update && sudo apt upgrade -y curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker ubuntu마지막 줄(
usermod) 후에는 반드시exit로 나갔다가 다시 ssh 접속해야 한다. 그래야 docker 명령이 sudo 없이 먹는다.docker --version docker psdocker ps가 권한 에러 없이 빈 표라도 보여주면 OK.5단계: n8n docker-compose.yml
mkdir ~/n8n && cd ~/n8n nano docker-compose.yml내용:
services: n8n: image: n8nio/n8n:latest restart: unless-stopped ports: - "5678:5678" environment: - GENERIC_TIMEZONE=Asia/Seoul - N8N_SECURE_COOKIE=false volumes: - n8n_data:/home/node/.n8n volumes: n8n_data:N8N_SECURE_COOKIE=false가 중요하다. HTTPS 없이 IP로 접속할 때 이걸 꺼야 로그인이 막히지 않는다. 혼자 쓰는 자동화라 이 정도는 감수한다.도메인은 필요한가? 처음엔 다들 "도메인 + HTTPS"를 권한다. 외부 서비스 연동할 때 편하다는 이유다. 하지만 혼자 쓸 거면 IP 주소만으로 충분하다.
restart: unless-stopped는 서버가 재부팅돼도 n8n이 자동으로 다시 뜨고 워크플로우가 그대로 재개되도록 해준다.저장(
Ctrl+O→ Enter →Ctrl+X) 후 실행:docker compose up -d docker compose ps6단계: 503 "Database is not ready!" — 정상이다
브라우저로
http://본인IP:5678접속하면 처음엔:Cannot GET /이 뜨고, 좀 있다가:
{"code":503,"message":"Database is not ready!"}가 뜬다. 정상이다. 1GB RAM에서 SQLite 초기화하느라 시간이 걸린다. 2~3분 기다리고 새로고침하면 n8n 첫 화면이 뜬다.
로그로 진행상황 보고 싶으면:
docker compose logs -fEditor is now accessible via:같은 줄이 보이면 그때 접속하면 된다.
여기까지가 셋업이다. 이제 진짜 본론, 워크플로우를 만들 차례인데 — 여기서부터 진짜 삽질이 시작된다.
7단계: 1차 설계 — 그리고 첫 번째 벽
처음 그린 그림은 이랬다.
Schedule(15분) → RSS로 신규 감지 → 자막 추출 → OpenAI 요약 → Notion 적재자막 추출은 파이썬
youtube-transcript-api라이브러리가 유명하길래, 이걸 FastAPI 마이크로서비스로 감싸서 Docker 컨테이너로 같이 띄우기로 했다. n8n과 같은 Docker 네트워크에 두면http://yt-transcript:8500으로 호출할 수 있다.# app.py (1차 시도) from fastapi import FastAPI, HTTPException from youtube_transcript_api import YouTubeTranscriptApi app = FastAPI() @app.get("/transcript") def get_transcript(videoId: str, lang: str = "ko,en"): api = YouTubeTranscriptApi() fetched = api.fetch(videoId, languages=[l.strip() for l in lang.split(",")]) text = " ".join(s.text for s in fetched) return {"videoId": videoId, "text": text}컨테이너를 빌드하고, n8n에서 health check도 통과. 영문 테스트 영상(Rick Astley) 자막도 잘 나왔다. "오, 되네?"
그리고 진짜 대상인 한국 채널 영상으로 호출하자 500 에러.
youtube_transcript_api._errors.RequestBlocked: YouTube is blocking requests from your IP. ... IP belonging to a cloud provider (like AWS, GCP, Azure, etc.)원인: 클라우드 IP 차단
YouTube는 데이터센터 IP에서 오는 자막 요청을 차단한다. 가정용 ISP IP에서는 자막 요청이 하루 몇 건이지만, 클라우드 IP는 스크래핑 봇이 대량으로 두드리기 때문에 IP 대역을 통째로 막아둔 것.
내 서버는 Oracle Cloud. 정확히 그 블랙리스트 안이었다. Rick Astley 영상이 통과한 건 그냥 운(인기 영상이라 캐시됐거나, 첫 요청이라 rate-limit에 안 걸렸거나).
핵심 교훈: 서버 코드를 어떻게 바꿔도 같은 IP로 나가는 한 해결 안 된다.
8단계: 2차 시도 — yt-dlp로 갈아타기
youtube-transcript-api는 YouTube 내부 API를 두드린다. 그럼 자막 다운로드 메커니즘이 다른yt-dlp는 어떨까? yt-dlp는 자막을timedtext엔드포인트에서 가져오기도 하니 차단 양상이 다를 수 있다.마이크로서비스를 yt-dlp 기반으로 다시 짰다. 결과는...
ERROR: [youtube] Sign in to confirm you're not a bot. Use --cookies-from-browser or --cookies for the authentication.똑같이 차단. 라이브러리를 바꾸는 건 의미가 없었다. 문제는 IP 그 자체였으니까.
9단계: 막다른 길 — "RSS 설명만 쓰면 안 될까?"
자막 추출이 안 되니 잠깐 이런 생각을 했다. "유튜브 RSS 피드에 영상 설명(description)이 들어오는데, 그걸로 요약하면 자막 없이도 되는 거 아닌가?"
실제로 RSS의 description을 까봤다.
영상 description 길이 내용 미국 국채금리 급등… 133자 면책조항 + 채널 링크뿐 종목 분석: 테슬라 0자 비어있음 삼성전자 총파업… 133자 면책조항 + 채널 링크뿐 영상 본 내용을 알 수 있는 단서가 0%. 어떤 영상을 넣어도 요약이 "투자 참고용 안내…"로 똑같이 나올 판이었다. 즉시 폐기.
10단계: 3차 — 차단 우회를 "남에게 맡기기"
검색을 해보니 답이 나왔다. 사람들은 자막 차단 문제를 자력으로 우회하지 않고, 우회를 대신 해주는 SaaS API를 쓰고 있었다. n8n 공식 템플릿 갤러리에도
youtube-transcript.io를 쓰는 워크플로우가 등록돼 있었다.- Webshare 같은 residential proxy도 방법이지만, "프록시 써도 차단됐다"는 이슈가 GitHub에 수두룩했다. 안정성에 베팅하기 어렵다.
- youtube-transcript.io는 차단 우회를 서비스 측에서 알아서 처리한다. 가입하면 무료 토큰 25개. API 키 하나만 받으면 끝.
POST https://www.youtube-transcript.io/api/transcripts Authorization: Basic <api-token> Body: { "ids": ["videoId"] }⚠️ Authorization은
Basic이지Bearer가 아니다. 이거 한참 헤맸다.토큰을 발급받아 호출하니 한국어 자막이 깔끔하게 떨어졌다.
그동안 고생해서 띄운 자체 마이크로서비스 컨테이너는 미련 없이 철거했다. n8n HTTP Request 노드 하나가 그 역할을 대신하게 됐다. 때로는 직접 만든 걸 버리는 게 정답이다.
11단계: n8n 워크플로우 조립 — 4연속 에러
이제 n8n에서 노드를 연결할 차례. 그런데 실행 버튼을 누를 때마다 새로운 에러가 튀어나왔다.
에러 ①
Multiple matches foundRSS에서 영상이 2개 들어왔는데, 뒤쪽 노드에서
$('videoId 추출').item으로 앞 노드 데이터를 참조하니 "2개 중 어느 거?" 하고 n8n이 멈췄다.해결:
Loop Over Items(SplitInBatches) 노드를 넣어 영상을 1개씩 처리. 루프 안에서는 항상 아이템이 하나뿐이라 참조가 모호하지 않다.에러 ②
Response body is not valid JSONHTTP Request 노드가 응답을 JSON으로 강제 파싱하다 실패.
해결: 응답 포맷을
text로 받고, 다음 Code 노드에서 직접JSON.parse를 시도(try/catch)하도록. 응답 구조가 어떻든 깨지지 않게.에러 ③ OpenAI
too many requests(429)요약 노드에서 OpenAI가 rate limit을 던졌다. 자막이 길어 토큰이 컸고, OpenAI 계정 tier도 낮았다.
해결: 자막을 프롬프트에 넣을 때 길이를 잘라 토큰을 줄이고, 노드에 재시도(3회, 5초 간격) 옵션을 켰다. 그리고 OpenAI 계정에 크레딧을 채워 tier를 올렸다.
에러 ④ RSS
Status code 404가장 황당했던 에러. 30분 전엔 잘 되던 RSS가 갑자기 404.
처음엔 "IP 밴인가?" 싶었지만 404는 밴(403/429)이 아니다. 로컬 PC에서 호출해도 404, 구글 공식 채널 RSS도 404.
검색해보니 — YouTube RSS 피드(
youtube.com/feeds/videos.xml)의 플랫폼 전역 outage였다. 2026년 들어 YouTube가 비공식 RSS 피드를 간헐적으로 죽이고 있었고, n8n 커뮤니티에도 같은 이슈 스레드가 올라와 있었다.12단계: RSS를 버리고 YouTube Data API v3로
RSS는 비공식 인터페이스다. 공식 API로 가는 게 답이었다.
GET https://www.googleapis.com/youtube/v3/playlistItems ?part=snippet &playlistId=UU... (채널 ID의 UC를 UU로 바꾸면 '업로드' 재생목록) &maxResults=2 &key=<API_KEY>💡 꿀팁: 채널 ID
UCxxxx에서 앞 두 글자만UU로 바꾸면 그 채널의 '업로드 전체' 재생목록 ID가 된다.playlistItems.list로 최신 영상을 깔끔하게 가져올 수 있다.YouTube Data API v3는 하루 10,000 units 무료 quota를 준다.
playlistItems.list는 1회 호출에 1 unit. 15분마다 폴링해도 하루 96회. 차고 넘친다.API 키에는 IP 제한(서버 IP만 허용)과 API 제한(YouTube Data API v3만)을 걸어서, 키가 노출돼도 악용이 불가능하게 했다.
최종 아키텍처
[Schedule Trigger: 15분] │ [YouTube Data API: playlistItems.list] ← 채널 업로드 목록 최신 2개 │ [Code: videoId 추출] ← 응답에서 videoId/제목/링크/날짜 파싱 │ [Loop Over Items: 1개씩] │ [Notion: 중복 체크] ← VideoId로 이미 있는 영상인지 조회 │ [IF: 신규?] ──(이미 있음)──→ 루프 다음 │ (신규) [youtube-transcript.io API] ← 자막 추출 │ [Code: 자막 텍스트 정규화] │ [IF: 자막 있음?] ──(없음)──→ 루프 다음 │ (있음) [OpenAI: 요약] ← gpt-4o-mini, 핵심 bullet + TL;DR │ [Notion: 행 추가] ← 원본 자막 + 요약을 한 행으로 적재 │ 루프 다음 영상핵심 설계 포인트
- 중복 방지: "마지막 실행 시각" 같은 상태를 관리하는 대신, Notion DB에서
VideoId로 직접 조회한다. 이미 있으면 건너뛴다. 상태 관리가 없어 더 견고하다. - Loop로 1개씩 처리: 멀티 아이템 참조 문제를 원천 차단.
- 방어적 분기: 자막이 없는 영상은
IF노드로 걸러 조용히 건너뛴다. 워크플로우 전체가 죽지 않는다. - Notion rich_text 2000자 제한: 자막/요약을 1990자로 잘라 넣는다.
Notion DB 속성 구조
속성명 타입 제목 Title 영상 URL URL 채널명 Text 게시일 Date VideoId Text 자막 원문 Text AI 요약 Text ⚠️ 속성 이름과 타입이 한 글자라도 다르면 매핑이 깨진다. 한글 띄어쓰기까지 정확히 맞춰야 한다.
자동 실행 켜기
n8n 최신 버전은 예전의 'Active 토글'이 우상단
Publish버튼으로 바뀌었다. Publish를 누르면 Schedule Trigger가 백그라운드에서 15분마다 돌기 시작한다.
보안 — 솔직한 이야기
지금 설정대로면
5678포트가0.0.0.0/0(인터넷 전체)에 열려 있다. 봇들이 내 IP의 5678 포트를 끊임없이 스캔할 수 있다. n8n 로그인이 걸려 있긴 하지만, HTTPS가 아니라 HTTP라서 로그인 정보가 평문으로 오간다.옵션은 두 가지.
옵션 1: 내 IP만 허용
Oracle 콘솔에서 5678 규칙의 Source CIDR를
0.0.0.0/0대신 본인 공인 IP/32로 바꾼다. 봇 스캔이 의미가 없어진다. 단, 집 공인 IP는 가끔 바뀌고 외부(카페·모바일)에서 접속하면 안 된다.옵션 2: SSH 터널 (가장 안전)
5678 포트를 인터넷에 아예 안 연다. Oracle 콘솔과 iptables에서 5678 규칙을 빼고, 접속할 때만 SSH 터널을 만든다.
ssh -i ~/.ssh/oracle_key -L 5678:localhost:5678 ubuntu@본인IP이러고 브라우저에서
http://localhost:5678로 접속. 인터넷에 노출되는 포트는 22(SSH)뿐이라 가장 안전하다.비용 정리
전부 무료이거나 무료 tier 안에서 돈다.
항목 비용 Oracle Cloud VPS Always Free YouTube Data API v3 무료 (폴링 1회 = 1 unit, 하루 quota 10,000) youtube-transcript.io 무료 25 토큰 (영상 1개당 1개 소모, 소진 시 유료) OpenAI gpt-4o-mini 영상 1개당 수 원 수준 폴링 주기를 너무 짧게 두면 youtube-transcript.io 토큰만 아깝다. 채널 업로드 빈도에 맞춰 15분~1시간으로 조절하면 된다.
회고 — 배운 것
- 외부 플랫폼의 차단 정책은 코드로 못 이긴다. IP가 문제면 코드를 아무리 바꿔도 소용없다. 우회를 전문으로 하는 서비스를 쓰는 게 빠르고 안정적이다.
- 직접 만든 걸 버릴 줄 알아야 한다. 고생해서 띄운 자체 자막 마이크로서비스는 결국 HTTP 노드 하나로 대체됐다. 매몰비용에 미련 두지 않기.
- 비공식 인터페이스(RSS)는 공식 API보다 약하다. 편하다고 RSS를 썼다가 outage에 당했다. 안정성이 중요하면 공식 API.
- 에러는 한 번에 하나씩. Multiple matches → JSON 파싱 → rate limit → 404. 짜증났지만 하나씩 잡다 보니 워크플로우가 점점 단단해졌다.
- Oracle 보안 목록 ≠ 서버 iptables. 인프라 레벨에서도 함정은 있다. 두 곳 다 열어야 한다.
- 첫 실행 503은 정상. 의심하지 말고 2~3분 기다리기.
처음엔 "한 줄짜리 목표"였지만, 막상 부딪혀보니 YouTube 생태계의 봇 차단·API 정책·서비스 outage를 한 바퀴 다 돌았다. 그래도 결과물은 만족스럽다. 이제 영상이 올라오면 알아서 요약이 Notion에 쌓인다.
비슷한 셋업 시도하시는 분께 이 글이 삽질을 좀 줄여드렸길 바란다.
반응형'개발 관련' 카테고리의 다른 글
Claude Desktop로 WWDC 요약하기 (Puppeteer, MCP) (0) 2025.04.11 [jekyll] 로컬에서 구동시 Dependency Error 문제 해결하기 (0) 2024.03.21 tree 사용하기 (0) 2023.04.07 아이패드 활용해서 Apple 문서 편하게 보기 (0) 2023.03.03 API 란 무엇일까 ? (0) 2022.02.02