내가 운영하는 놀랩(nolab.kr)은 심심풀이 테스트·퀴즈·게임을 모아둔 사이트다. 콘텐츠를 계속 새로 올려야 사는 사이트인데, 매일 소재를 찾고 만드는 게 일이었다. 그래서 이걸 통째로 자동화했다. 지금은 이렇게 돌아간다:
매일 새벽 5시, 클로드 클라우드 에이전트가 알아서 요즘 유행을 조사하고, 콘텐츠 2~3개를 코드로 만들어 GitHub PR로 보낸다. 나는 아침에 폰으로 PR을 열어 실제 동작하는 프리뷰를 눌러보고 — Merge(배포) / 코멘트(수정 요청) / Close(폐기) 중 하나를 누른다. 끝.
이 글은 그 파이프라인을 만든 전체 과정이다. 뭘 준비해야 하는지, 어떤 순서로 만들었는지, 클로드에게 실제로 어떤 프롬프트를 줬는지, 수정·커밋·스케줄링이 어떻게 굴러가는지 전부 적었다.
0. 전체 그림 — 어드민 페이지를 만들지 마라
처음엔 "어드민 페이지를 만들어서, AI가 만든 콘텐츠를 보여주고, 승인 버튼을 누르면 배포되게 하자"고 생각했다. 그런데 설계하다 보니 그 어드민 페이지가 이미 존재했다. GitHub의 PR 화면이다.

이 구조의 좋은 점:
- 승인 UI를 한 줄도 안 만들었다. GitHub 모바일 앱이 그대로 대시보드다.
- 프리뷰가 공짜다. Vercel(또는 Netlify)은 PR을 열면 실제 동작하는 프리뷰 URL을 자동으로 달아준다. 스크린샷이 아니라 진짜로 눌러보고 판단할 수 있다.
- 수정·폐기가 Git의 기본 동작이다. 수정 = 브랜치에 커밋 추가, 폐기 = PR 닫기. 롤백도 Git이 해준다.
- main은 항상 안전하다. AI는 main에 직접 푸시할 수 없고, 사람이 Merge를 눌러야만 배포된다.
1. 준비물
항목내용
| Claude 구독 | Pro 또는 Max. API 종량제가 아니라 구독만으로 로컬 Claude Code + 클라우드 스케줄 에이전트를 전부 쓴다. |
| Claude Code | 로컬 개발·파이프라인 구축용 CLI/IDE 확장. 스케줄 등록도 여기서 /schedule로 한다. |
| GitHub 저장소 | 사이트 코드가 GitHub에 있어야 한다. 클라우드 에이전트는 GitHub 저장소를 클론해서 작업한다. |
| Vercel 연동 | main 푸시 = 배포, PR = 프리뷰 배포가 되도록 GitHub 연동. (Netlify 등도 동일) |
| GitHub 연결 2종 | claude.ai ↔ GitHub 계정 연결(OAuth) + Claude GitHub App을 저장소에 설치. 아래 3단계에서 자세히. |
2. 1단계 — 파이프라인을 손으로 한 바퀴 돌려본다
스케줄부터 걸면 안 된다. 먼저 "브랜치 → PR → 프리뷰 → 승인" 한 사이클을 로컬 클로드와 함께 수동으로 돌려서, 이 흐름이 우리 저장소에서 실제로 매끄러운지 확인했다.
1호 콘텐츠는 "90년대 노래 이상형 월드컵"이었다. 이 소재를 고른 이유가 자동화 관점에서 중요하다:
- 저작권 안전 — 음원을 서버에 올리는 게 아니라 유튜브 공식 임베드(iframe)로 재생한다. 영상과 조회수는 원저작자 몫. 연예인 사진을 긁어오는 류의 콘텐츠는 초상권·저작권 리스크가 커서 처음부터 금지 목록에 넣었다.
- 기존 엔진 재사용 — 이미 있던 치킨 이상형 월드컵의 토너먼트 코드를 복사해 카드만 유튜브 플레이어로 바꿨다. AI에게 "기존 패턴을 따라라"라고 시킬 수 있는 구조가 자동화의 핵심이다.
- 데이터 검증이 자동화 가능 — 유튜브 video ID를 AI 기억으로 쓰면 반드시 틀린다. 실제 검색으로 찾고, 곡마다 oEmbed API로 임베드 허용 여부를 검증시켰다:
16곡 전부 200을 받은 것만 데이터에 넣었다. 음반사 공식 MV 중엔 외부 임베드를 막아둔 게 있어서 이 검증이 필수다.curl -s -o /dev/null -w "%{http_code}" \ "https://www.youtube.com/oembed?format=json&url=https://www.youtube.com/watch?v=VIDEO_ID" # 200 = 임베드 가능성 높음 / 401·403 = 임베드 차단 / 404 = 영상 없음
제작이 끝나면 로컬에서 품질 게이트를 통과시킨다(우리 저장소의 CLAUDE.md에 규칙으로 박아둔 것): SEO 메타·sitemap 등록, 사이트 체인지로그 기록, tsc 타입체크, 프로덕션 빌드, 헤드리스 브라우저(playwright)로 전체 플로우 완주 확인. 그리고:
git switch -c feat/song90-worldcup
git add … && git commit -m "90년대 노래 이상형 월드컵 신설 …"
git push -u origin feat/song90-worldcup
gh pr create --title "🎤 90년대 노래 이상형 월드컵" --body "…승인 방법: Merge=배포 / 코멘트=수정 / Close=폐기"

몇 분 뒤 PR에 Vercel 프리뷰 링크가 자동으로 달렸고, 폰에서 눌러보고 Merge를 눌렀더니 2~3분 만에 실서비스에 반영됐다. 파이프라인 검증 끝.
3. 2단계 — 수정 루프를 경험해 본다
머지 전에 PR에 이렇게 코멘트를 남겨봤다:
"처음부터 시작하니까 노래가 바로 안 나오는데, 하이라이트 부분이나 노래가 시작하는 부분으로 위치하게 할 수 있을까?"


클로드가 이 코멘트를 받아서 한 일이 이 파이프라인의 성격을 잘 보여준다. 단순히 "start 파라미터를 붙였습니다"가 아니라 —
- 유튜브 임베드의 start=초 파라미터로 해결 가능함을 파악
- 16개 영상 각각의 watch 페이지를 실제로 받아와, "가장 많이 다시 본 구간"(Most replayed) 히트맵 데이터를 파싱해 하이라이트 시작점을 실측 (페이지 안의 macroMarkersListEntity JSON에서 intensityScoreNormalized가 최대인 구간)
- 예외 판단 — 두 곡은 0초 피크가 "다들 처음부터 트는" 왜곡이라 차순위 실제 코러스 피크를 채택, 한 곡은 히트맵 미제공이라 정직하게 처음부터 재생
- 수정 커밋 → 같은 브랜치에 푸시(프리뷰 자동 갱신) → PR에 "뭘 어떻게 고쳤는지" 답글 코멘트
4. 3단계 — 스케줄링: 매일 새벽 5시에 클라우드에서
파이프라인이 검증됐으니 이제 사람이 하던 "제작" 부분을 스케줄된 클라우드 에이전트로 넘긴다. Claude Code에서 /schedule로 등록하면 되고, 핵심 스펙은 이렇다:
항목값메모
| 스케줄 | 0 20 * * * (UTC) | = 매일 새벽 5시 KST. cron은 UTC 기준이라 -9시간 변환 필요. 최소 간격 1시간. |
| 실행 위치 | Anthropic 클라우드 | 내 컴퓨터가 꺼져 있어도 돌아간다. 저장소를 클론해서 작업. |
| 모델 | Opus 4.8 | 기본값은 Sonnet. 콘텐츠 문구 품질(사이트 특유의 톤)이 중요해서 상위 모델 선택. |
| 도구 | Bash, Read/Write/Edit, Glob/Grep, WebSearch/WebFetch 등 | 유행 조사에 웹검색이 필수라 꼭 포함. |
여기서 삽질: GitHub 연결은 두 단계다

루틴 등록이 두 번 거절당했는데, 오류 코드가 힌트였다:
- 401 "Connect your GitHub account" — claude.ai 계정과 GitHub 계정의 OAuth 연결이 안 된 상태. → claude.ai/code에서 GitHub 연결(Authorize).
- 403 "You don't have access to a repository" — 계정 연결은 됐지만 Claude GitHub App이 해당 저장소에 설치되지 않은 상태. → github.com/apps/claude → Install → 저장소 선택.
5. 실제 루틴 프롬프트 전문
스케줄 에이전트는 매일 빈 컨텍스트로 시작하므로, 프롬프트가 곧 시스템이다. 실제로 등록한 프롬프트 전문:
너는 놀랩(nolab.kr — 심심풀이 테스트·퀴즈·게임 사이트, Next.js App Router + Vercel 자동 배포)의
데일리 콘텐츠 제작 에이전트다. 오늘의 신규 콘텐츠 2~3개를 만들어 브랜치 하나 + PR 하나로 올려라.
main 직접 푸시는 절대 금지.
[0. 시작 전 필독]
- 저장소 루트 CLAUDE.md를 읽고 규칙을 따른다. 예외 하나: '개발 연구일지(옵시디언)' 기록은
클라우드에서 접근 불가하므로 생략한다(주인이 로컬에서 몰아서 기록). 나머지 게이트(SEO,
사이트 연구일지 changelog)는 전부 지킨다.
- docs/content-log.md(소재 대장)를 반드시 읽는다: '배포됨' 소재와 겹치는 컨셉 금지,
'거절됨'·'금지' 소재는 다시 제안하지 않는다.
- lib/tests.ts(콘텐츠 카드 목록), 상식 글 데이터(SENSE_NOTES가 정의된 lib 파일),
app/changelog/page.tsx 최근 엔트리, git log --oneline -20을 확인해 기존·최근 소재와
겹치지 않게 한다.
- 놀랩 톤: 존댓말 + 하찮음 + 「연구소가 진지하게 측정하는 척」 세계관. 사용자 노출 문구 전부 이 톤.
[1. 유행 조사]
웹검색으로 한국 기준 요즘 유행(밈, 유행어, 화제, 시즌 소재)을 조사해 오늘 만들 소재를 고른다.
PR 본문에 「왜 이 소재인지」 근거(링크 포함)를 쓸 수 있어야 한다.
[2. 콘텐츠 2~3개 제작 — 유형을 섞을 것, 같은 유형만 3개 금지]
- 상식 글 1~2편: SENSE_NOTES 배열에 항목을 추가하는 기존 패턴 그대로. 텍스트만이라 가장 안전.
- 맞추기/퀴즈/테스트 1개: 기존 테스트·퀴즈 코드 패턴(lib/mental.ts + app/mental/ 등)을 참조해
동일 구조로. 예: 유행어 뜻 맞추기, 세대 판별 테스트.
- 월드컵 변형: lib/song90.ts + app/song90/(유튜브 임베드 노래 월드컵) 또는 lib/chicken.ts 패턴
재사용. 노래 월드컵 후속편(2000년대, 발라드 등)은 곡 데이터만 갈면 되므로 효율적.
[저작권 절대 규칙]
- 실존 인물(연예인 포함) 사진이나 웹에서 긁은 이미지·짤을 저장소에 넣는 것 금지.
- 이미지가 필요하면 직접 그린 SVG, 이모지, 유튜브 공식 임베드(iframe)만 사용.
- 유튜브 videoId는 기억으로 지어내지 말고 실제 검색으로 찾은 뒤, 전곡
oEmbed(https://www.youtube.com/oembed?url=...) HTTP 200으로 임베드 허용을 검증한다.
노래류는 watch 페이지의 Most replayed 히트맵(macroMarkersListEntity) 피크를 파싱해
하이라이트 start 초도 넣는다(song90 방식).
[3. 품질 게이트 — 전부 통과해야 PR]
- SEO: lib/tests.ts에 seo(title/description) 채우고 app//layout.tsx 추가
(app/mental/layout.tsx 복사).
- 새 콘텐츠 id를 lib/db.ts의 TESTS 화이트리스트에 추가.
- changelog: app/changelog/page.tsx ENTRIES 맨 위에 새 엔트리(date=오늘 KST,
version=직전 버전에서 MINOR+1, 놀랩 톤 notes).
- npx tsc --noEmit && npm run build 통과 필수. 실패한 채 PR 금지.
[4. PR]
- 브랜치명: auto/daily-YYYYMMDD.
- 오늘 만든 소재를 docs/content-log.md '배포됨' 섹션 맨 위에 『YYYY-MM-DD id — 한 줄 설명』으로
추가하는 변경도 같은 PR에 포함한다(머지 = 대장 기록).
- PR 본문에: 각 아이템 소개, 소재 선정 근거(유행 링크), 검증한 것(tsc/build/oEmbed 등),
Vercel 프리뷰에서 확인할 것 체크리스트, 승인 방법 안내(Merge=배포, 코멘트=수정 요청,
Close=폐기, 특정 아이템만 빼려면 코멘트로 요청).
- 확신 없는 부분(저작권 애매, 검증 못 한 것)은 PR에 솔직하게 표기한다. 완성도가 기준 미달인
아이템은 억지로 채우지 말고 빼고, 뺐다고 PR에 적는다.

프롬프트 설계에서 중요했던 것들
- "기존 패턴을 참조하라"를 파일 경로로 지목 — "우리 스타일로 만들어"가 아니라 lib/mental.ts, app/song90/처럼 정확한 참조 파일을 준다. 결과물의 일관성이 완전히 달라진다.
- 검증을 명령어 수준으로 명시 — "검증해라"가 아니라 oEmbed curl 명령과 판정 기준(200/401/404)까지 적는다. AI의 "아마 되겠지"를 차단하는 방법은 실행 가능한 검증 절차뿐이다.
- 금지 목록은 이유와 함께 — 저작권 규칙에 "왜"가 있으면 애매한 경계 사례에서도 올바른 쪽으로 판단한다.
- "빼는 용기" 조항 — 완성도 미달이면 억지로 채우지 말고 빼고, 뺐다고 보고하라. 이 한 줄이 없으면 AI는 개수를 채우려고 어설픈 걸 밀어넣는다.
- 솔직함 강제 — 검증 못 한 것, 애매한 것을 PR에 표기하게 한다. 검수하는 사람(나)의 시간을 아끼는 건 결국 이 정직성이다.
- 환경 차이 명시 — 클라우드 에이전트는 내 로컬 노트(옵시디언)에 못 쓴다. 이런 "로컬 전용 규칙"은 예외를 명시하고 사람(또는 로컬 세션)이 나중에 백필한다.
6. 4단계 — 소재 대장: 중복과 재제안 막기
자동 제안이 매일 오면 두 가지 문제가 생긴다: ① 기존 콘텐츠와 비슷한 걸 또 만들거나 ② 내가 폐기(Close)한 소재를 며칠 뒤 다시 들고 온다. ①은 코드 읽기로 어느 정도 막히지만, ②는 기록이 없으면 못 막는다 — 닫힌 PR의 "왜 닫혔는지"는 어디에도 안 남기 때문이다.
해결책은 저장소에 소재 대장 파일(docs/content-log.md)을 두는 것:
# 놀랩 소재 대장
## 배포됨 ← 에이전트가 만든 소재를 같은 PR에서 여기 추가 (머지 = 기록)
- 2026-07-19 song90 — 90년대 노래 이상형 월드컵
## 거절됨 (다시 제안 금지) ← PR을 Close할 때 주인이 한 줄 추가
- (아직 없음)
## 금지 소재 ← 처음부터 안 되는 것들
- 실존 인물 사진이 필요한 모든 컨셉 (초상권·저작권)
- 웹에서 긁은 이미지·짤이 필요한 컨셉
- 정치·종교·혐오 소재, 사행성 소재
루틴 프롬프트에 두 가지 의무를 넣었다: 제안 전에 대장을 읽을 것, 그리고 만든 소재를 같은 PR에서 대장에 추가할 것. 이러면 머지하는 순간 기록이 저절로 쌓인다. 유일한 수동 작업은 PR을 Close할 때 거절됨에 한 줄 적는 것뿐이다.
7. 수정·커밋·배포 규칙 정리
상황누가어떻게
| 신규 콘텐츠 제작 | 클라우드 에이전트 (매일 새벽 5시) | 브랜치 auto/daily-YYYYMMDD + PR 1개. main 직접 푸시 금지. |
| 배포 | 사람 | PR에서 Merge 클릭 → Vercel이 main을 자동 배포. 배포 권한은 항상 사람에게. |
| 수정 | 사람이 요청, AI가 수행 | PR에 원하는 경험을 코멘트 → AI가 같은 브랜치에 수정 커밋 → 프리뷰 자동 갱신 + 답글 보고. |
| 부분 채택 | 사람이 요청, AI가 수행 | "두 번째 것만 빼줘" 코멘트 → 해당 아이템 제거 커밋. |
| 폐기 | 사람 | PR Close + 소재 대장 '거절됨'에 한 줄. |
| 품질 검증 | AI (PR 전 필수) | 타입체크 + 프로덕션 빌드 + 데이터 실측 검증(oEmbed 등). 실패 상태로 PR 금지. |
8. 안전장치 목록
- main 보호 — 에이전트는 브랜치+PR만. 배포 버튼(Merge)은 사람 손에만 있다.
- 저작권 3중 장치 — 실존 인물 사진·웹 이미지 저장 금지 / 이미지는 자작 SVG·이모지·유튜브 공식 임베드만 / 유튜브는 전곡 임베드 허용 검증.
- 환각 차단 — 외부 데이터(video ID, 하이라이트 시점)는 전부 실측. "기억으로 지어내지 말 것"을 명시하고 검증 명령어를 준다.
- 빌드 게이트 — 타입체크·빌드 실패 상태로는 PR을 못 열게 한다. 프리뷰가 안 뜨는 PR은 검수 시간 낭비다.
- 소재 대장 — 중복 제안·거절 소재 재등장 방지.
- 정직성 조항 — 검증 못 한 것·애매한 것은 PR에 표기, 미달 아이템은 빼고 보고.
9. 한계와 다음 계획
- 코멘트 자동 반응은 아직 반자동 — 지금은 PR 코멘트를 로컬 클로드 세션에 보여주면 고쳐서 푸시한다. Claude GitHub Actions를 붙이면 코멘트에 @claude만 멘션해도 자동으로 수정이 돈다. 다음 단계.
- 이미지 생성은 미해결 — 이미지가 꼭 필요한 콘텐츠(카드 일러스트 등)는 지금 자작 SVG로 때운다. 이미지 생성 API를 붙이면 완전 자동이 되지만 별도 과금이라 보류.
- "비슷한 결" 중복은 판단의 영역 — 대장이 있어도 컨셉이 미묘하게 겹치는 제안은 나올 수 있다. 그래서 최종 승인은 여전히 사람 몫이고, 그게 이 설계의 의도이기도 하다.
- 취향 학습 — 거절 사유를 대장에 쌓다 보면, 그 자체가 "주인 취향 데이터"가 된다. 나중에 프롬프트에 반영할 예정.
10. 정리 — 순서만 다시
- 사이트를 GitHub + Vercel(프리뷰 배포) 구조로 만든다.
- 저장소에 CLAUDE.md로 규칙(품질 게이트·톤·금지사항)을 박는다. 이 파일이 자동화의 뼈대다.
- 로컬 클로드와 함께 1호 콘텐츠를 브랜치 → PR → 프리뷰 → Merge로 수동 1회 돌려 파이프라인을 검증한다.
- PR 코멘트로 수정 요청 → 수정 커밋 → 프리뷰 갱신, 수정 루프도 1회 경험한다.
- claude.ai GitHub 계정 연결(OAuth) + Claude GitHub App 저장소 설치. (둘 다!)
- Claude Code에서 /schedule로 루틴 등록 — cron(UTC 변환 주의), 모델, 그리고 위의 프롬프트.
- 소재 대장 파일을 만들고 루틴에 읽기/쓰기 의무를 추가한다.
- 아침마다 폰으로 PR 열고: Merge / 코멘트 / Close.



결과적으로 내가 매일 하는 일은 "아침에 PR 하나 읽고 버튼 하나 누르기"로 줄었다. 콘텐츠 공장의 공장장이라기보다, 매일 아침 신제품 시식만 하는 사장에 가깝다. 이 구조는 콘텐츠 사이트가 아니어도 — 블로그, 문서, 데이터 갱신, 정기 리포트 등 "주기적으로 뭔가 만들어서 사람이 승인하면 반영되는" 모든 일에 그대로 이식할 수 있다.
p.s. 이런 밈은 나도 이런거 보면서 공부한다.

'개발 > 사이드프로젝트' 카테고리의 다른 글
| 새로 만든 웹사이트, 네이버 검색에 안 나온다면? 서치어드바이저 검색등록 직접 해본 후기 (robots.txt·사이트맵까지) (0) | 2026.07.19 |
|---|---|
| 자동화 연차 관리 시스템 만들기 (3) | 2025.02.26 |
| [디스코드 봇] 롤 전적검색기능 만들기 - 1 (0) | 2024.03.17 |
| 간편하고 특이하게 나만의 QR코드 만들기 (0) | 2023.10.24 |
| 파이썬으로 간단하게 챗봇 만들기 (0) | 2023.05.09 |
댓글