CAPSTONE 4 / TREND RADAR / 135 MIN

유튜브 급상승 30, 트렌드 레이더

수집 → 스냅샷 → 변동 파생 → AI 브리핑, 흐름을 읽는 서비스

지금 한국에서 가장 뜨는 영상 30개를 주기적으로 수집해 스냅샷으로 쌓고, 순위 변동(▲▼)과 NEW 진입, 카테고리 점유율의 흐름을 읽어내며, Bedrock이 "왜 뜨는지" 브리핑을 씁니다. 급상승은 스냅샷이 아니라 흐름입니다. 실데이터이며, 수집 주기마다 바뀝니다. 배포 없이 로컬 완결, 다른 참가자와의 의존성도 없습니다.

소요 시간 135분 미션 5개 + DoD 산출물 브라우저에서 뛰는 내 트렌드 레이더 Update 2026.08
MISSION BRIEFING

지금 트렌드를 다루는 법

숙지

다른 캡스톤과 같은 기조입니다: 빈 폴더에서 superpowers 워크플로로 설계하고 Phase 단위로 빌드합니다. 목표물은 실존 프로젝트 youtube-trend(FastAPI + DynamoDB + React, AWS Fargate 운영)를 135분 스코프로 압축한 트렌드 레이더입니다. 실데이터이며, 수집 주기마다 바뀝니다.

레이어구성핵심 원칙
수집httpx로 YouTube videos.list 주기 호출(워크샵 60초, 원작 1시간)부분 실패 격리, 카드 한 장의 오염이 사이클을 못 죽인다
저장스냅샷 링버퍼(최근 48개), 같은 시각 버킷은 한 번만멱등 쓰기, 원작은 같은 인터페이스로 DynamoDB
파생delta(▲▼), NEW, 이탈 수, 카테고리 점유율급상승은 스냅샷이 아니라 흐름, 기준선과의 비교가 정보다
AI와 화면Bedrock Converse를 REST 직접 호출, 듀얼 테마 SPA키 2개는 환경변수에만, 화면은 우리 API만 본다

1. 레이더 (메인 화면)

기능설명
Top-30 그리드순위 배지 + ▲▼ delta 배지 + NEW 마커, 썸네일 16:9, 제목/채널/조회수/좋아요, 클릭 시 유튜브
카테고리 필터고정 8종 칩: Music, Gaming, Entertainment, News&Politics, Sports, Film&Animation, Science&Tech, Comedy
통계 바합산 조회수, 채널 수, 최다 카테고리, 이탈 수(직전 스냅샷 대비)
듀얼 테마시스템 기본 + 토글 + localStorage, 색은 전부 CSS 변수

2. 흐름과 브리핑

기능설명
시계열 차트최근 N시간 카테고리 점유율 누적 막대(인라인 SVG), 진입/이탈 흐름
브리핑 now현재 스냅샷 요약: 주제 클러스터 3~4개와 왜 뜨는지 + 제작/시청 관점 인사이트
브리핑 daily기준선과의 비교: 무엇이 올라왔고 무엇이 빠졌나, 흐름의 해설
브리핑 캐시같은 시각 버킷 동안 재사용, LLM 호출은 아껴 쓴다
L학습 목표, Level 200

이 캡스톤을 마치면 다음 네 가지를 설명할 수 있고, 다시 만들 수 있습니다.

목표검증되는 순간
주기 수집 파이프라인과 멱등 스냅샷 저장을 설계하는 법M2에서 60초마다 스냅샷이 쌓이되 같은 버킷은 한 번만 저장될 때
기준선 비교로 변화(델타)를 파생하는 법M3에서 ▲▼/NEW 배지가 직전 스냅샷과의 비교로 정확히 붙을 때
외부 SDK 없이 REST 계약만으로 LLM을 부르는 법M4에서 httpx 한 장으로 Bedrock Converse가 응답할 때
서드파티 키를 서버에 격리하는 프록시 패턴브라우저 개발자 도구 어디에도 두 키가 보이지 않을 때
R챕터 연결 지도, 배운 내용이 재등장하는 곳

이 캡스톤은 새 기술을 가르치지 않습니다. Ch1~6에서 배운 것을 실전 배치합니다.

챕터이 캡스톤에서 재등장하는 곳
Ch1 설치부터 Headless까지M0~전체: 세션 운용, 지시 → 실행 → 검증 루프
Ch2 SubagentsM2~M3: code-reviewer 서브에이전트로 수집기/파생 로직 리뷰, 백엔드와 화면 병행
Ch3 Admin SetupM4: Bedrock 라우팅과 자격의 배경, Bearer 키가 조직 통제선 안에서 도는 이유
Ch4 Settings, 훅과 스킬OPT: 하네스 성숙화. 원작 youtube-trend의 .claude/(커맨드 3, 스킬 4)가 도달점의 표본입니다
Ch5 CLI Reference확장 방향: 헤드리스 패턴으로 수집 사이클 상태를 점검하는 파이프라인
Ch6 Agent SDK확장 방향: 브리핑 생성을 코드에서 부르는 상주 리포터의 다음 단계
규칙, 캡스톤 공통 ① 코드와 파일 구성을 직접하지 않고, Claude에게 지시해서 만듭니다 ② 각 Phase의 DoD를 통과해야 다음으로 ③ 막히면 "막힐 때 열어보기"의 힌트를 씁니다. 다른 참가자와의 의존성은 없습니다.
BLUEPRINT

구현 청사진, 원작 상세 분석

참고

원작 youtube-trend의 실코드를 분석해 뽑은 구현 지도입니다. M1 설계가 이 구조로 수렴하도록 방향을 잡아 주고, 계획이 이상하게 흐르면 여기로 돌아와 대조하세요.

0세 개의 경로 한 눈에
수집 루프, 조회 경로, 브리핑 경로
[수집 루프]  주기마다 (워크샵 60초, 원작 1시간)
Scheduler ──> Collector: videos.list(mostPopular, KR, 30) ──> 정규화(_stat_int, 부분 실패 격리)
                                                                  │
                          Snapshot Store <── 멱등 저장(같은 시각 버킷은 한 번만) ──+
                          (워크샵: 링버퍼 48개 / 원작: DynamoDB pk+sk)

[조회 경로]  브라우저가 당길 때
Browser ── fetch ──> FastAPI: /api/trending ──> 최신 스냅샷 + 기준선 비교(derive)
   ^                                              │
   +── 30장 + ▲▼/NEW + 카테고리 + 점유율 ─────────+   (YouTube를 직접 치지 않는다)

[브리핑 경로]  버튼 눌렀을 때만
FastAPI ── httpx POST(REST 직접, Authorization: Bearer) ──> bedrock-runtime .../converse
   │                                                              │
   +── 시각 버킷 캐시 <── 한국어 브리핑(now | daily) ──────────────+

Level 200의 핵심 구분입니다: 화면 요청과 무관하게 도는 수집 루프, 저장된 것만 읽는 조회 경로, 비싸서 캐시하는 브리핑 경로. 셋을 분리하면 유튜브 쿼터도, LLM 비용도, 응답 속도도 한 번에 잡힙니다.

1구조 지도 (원작 실물)
구성원작 파일핵심 계약
수집기collector/youtube.pyhttpx 주입(테스트는 MockTransport), 상류 에러 본문은 로그에만, 예외엔 상태 코드만
카테고리categories.py고정 8종 + 기동 시 videoCategories(hl=ko) 1회, 실패해도 기본명으로 동작
스냅샷 스토어store/table.pypk=scope, sk=시각 버킷, attribute_not_exists로 멱등, latest/baseline 조회
파생derive.py, aggregate.py기준선 대비 delta/NEW/이탈, 카테고리 점유율과 시계열 집계
브리핑llm/bedrock.py, llm/prompts.pyConverse REST 직접(httpx), 타임아웃 504/파싱 502로 매핑, now/daily 프롬프트 분리
APIapi/trending.py, trends.py, videos.py, brief.py화면이 그대로 그리는 JSON, /healthz 별도
인프라(원작)infra/stacks/service.pyCloudFront → ALB(prefix list + X-Origin-Verify 2중 방어) → Fargate, Secrets는 기동 시 주입
2API와 운영 계약
계약내용
GET /api/trending최신 30 + rank/delta/NEW + 카테고리명, ?category=로 8종 필터
GET /api/trends?hours=N시각별 카테고리 점유율과 진입/이탈 흐름(기본 48h)
POST /api/brief?mode=now(현재 요약) | daily(기준선 비교), 같은 시각 버킷 동안 캐시
쿼터 경제videos.list는 호출당 1유닛, 시간당 수집이면 하루 24유닛, 일 한도 10,000의 0.24%
키의 거처(원작)이미지에 비밀 없음, Secrets Manager가 컨테이너 기동 시 주입. 워크샵은 셸 환경변수로 같은 원칙
3기술 선택 4
선택이유
수집과 조회의 분리화면 트래픽이 유튜브 쿼터를 건드리지 못한다, 스냅샷이 완충재
멱등 스냅샷스케줄러가 겹쳐 돌아도, 재시작해도 같은 버킷은 한 번, 시계열이 오염되지 않는다
Bedrock REST 직접SDK 없이 Authorization 헤더 + JSON 한 장, "LLM 호출은 결국 HTTP"를 체득
인라인 SVG 차트점유율 막대에 차트 라이브러리는 과잉, 데이터가 곧 rect의 높이
MISSION 00

사전 준비, 두 개의 키

15분

이 캡스톤은 키가 둘입니다: YouTube API 키(급상승 데이터)와 Bedrock Bearer 키(브리핑). 발급부터 스모크까지 여기서 끝냅니다.

1YouTube API 키 발급, 4단계

console.cloud.google.com 로그인 후 상단에서 프로젝트를 선택하거나 새로 만듭니다. ② 좌측 "API 및 서비스 → 라이브러리"에서 YouTube Data API v3를 검색해 사용 설정. ③ "API 및 서비스 → 사용자 인증 정보 → 사용자 인증 정보 만들기 → API 키". "서비스 계정을 통해 API 호출 인증(바인딩)" 옵션이 보이면 체크하지 않고 그대로 생성합니다, Vertex 계열용 옵션이라 유튜브에는 불필요합니다. ④ 생성된 키를 복사합니다. 무료 쿼터(일 10,000 유닛)로 이 캡스톤은 충분합니다.

과금 걱정에 대하여 YouTube Data API v3는 유료 전환이 없는 API입니다. 쿼터를 소진하면 요금이 아니라 403 에러로 멈추고, 결제 계정을 연결하지 않으면 청구 자체가 불가능합니다. 안심 수칙 세 가지: ① 결제 계정을 연결하지 않기(발급에 불필요) ② 키 제한에서 "YouTube Data API v3"만 허용 ③ 개인 Gmail 계정 권장, 회사 Workspace 계정은 콘솔이 정책으로 막혀 있을 수 있습니다.
발급이 어렵다면, 워크샵 공용 키 콘솔 접근이 막혔거나 발급이 늦어지면 현장에서 공유되는 워크샵 공용 키YT_API_KEY에 넣고 진행하세요. 이 캡스톤의 호출량은 새로고침당 1~2유닛이라 공용 키 하나로 전원이 여유 있게 돕니다.
2키 보관과 스모크
Terminal 1/3, 두 키를 .env에 (리포 밖)
mkdir -p ~/capstone
grep -q YT_API_KEY ~/capstone/.env 2>/dev/null || cat >> ~/capstone/.env << 'YTEOF'
YT_API_KEY=<발급받은 YouTube API 키>
YTEOF
grep -q AWS_BEARER_TOKEN_BEDROCK ~/capstone/.env 2>/dev/null || cat >> ~/capstone/.env << 'BREOF'
AWS_BEARER_TOKEN_BEDROCK=<발급받은 Bedrock API 키>
BREOF

저장 후 파일을 열어 placeholder를 실제 키로 바꾸세요. 다른 캡스톤에서 Bearer를 이미 넣었다면 그대로 유지됩니다(중복 방지 가드).

Terminal 2/3, 로드
set -a; source ~/capstone/.env; set +a
Terminal 3/3, 유튜브 스모크 (급상승 1위 제목)
curl -s "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&regionCode=KR&maxResults=1&key=$YT_API_KEY" \
  | jq -r '.items[0].snippet.title'
출력 예시
(지금 한국 급상승 1위 영상의 제목이 한 줄로)
Terminal, 스캐폴드 (재실행 안전)
mkdir -p ~/capstone/capstone-4 && cd ~/capstone/capstone-4
[ -f cdk.json ] || npx cdk init app --language typescript
(npx cdk bootstrap > /tmp/bootstrap.log 2>&1 &)
grep -qxF '.env' .gitignore 2>/dev/null || echo '.env' >> .gitignore
git config user.name "capstone" && git config user.email "capstone@lab.local"
mkdir -p frontend
git add -A && git commit -m "chore: scaffold" 2>/dev/null || true
CHECKPOINT
MISSION 01

설계, 브레인스토밍

15분

수집/저장/파생의 분리, REST 직접 브리핑, 듀얼 테마까지 요구 9조항으로 넘깁니다.

이 미션의 바탕 학습: Ch1의 세션 운용과 Capstone Setup의 superpowers 워크플로.

Terminal (~/capstone/capstone-4)
cd ~/capstone/capstone-4 && claude
Claude 세션 입력, 전체 복사
/superpowers:brainstorming 다음 요구사항으로 "Trend Radar", 유튜브 급상승 수집/분석 서비스를 설계하자.
1) 구조: backend/(Python 3.11+ FastAPI, app/collector 수집, app/store 스냅샷 저장, app/derive 파생,
   app/api 라우트, app/llm 브리핑, config.py) + frontend/index.html(정적 SPA 한 장, 빌드 없이
   fetch + 렌더). FastAPI가 정적 파일을 함께 서빙해 :8000 하나로 완결
2) 수집기: httpx로 YouTube Data API v3 videos.list(chart=mostPopular, regionCode=KR, maxResults=30,
   part=snippet,statistics)를 주기 호출. 주기는 config로, 워크샵 모드 60초(원작은 3600초).
   videoCategories(hl=ko)는 기동 시 1회, 실패해도 기본 카테고리명으로 동작.
   statistics 값은 문자열 숫자이므로 비정상 값은 0으로(부분 실패 격리, 카드 한 장이 사이클을 못 죽인다).
   상류 에러 본문은 로그에만, 예외와 API 응답에는 상태 코드만(프로젝트 정보 노출 격리)
3) 스냅샷 스토어: 인메모리 링버퍼(최근 48개). 각 스냅샷은 {captured_at, items[30]}이고
   같은 시각 버킷은 한 번만 저장(멱등). 원작은 이 자리를 DynamoDB(pk=scope, sk=시각 버킷,
   attribute_not_exists 멱등 쓰기)로 채운다, 인터페이스를 같게 설계해 교체 가능하게
4) 파생(derive): 최신 스냅샷과 기준선(직전 스냅샷)을 비교해 rank delta(▲n/▼n), NEW(첫 등장),
   이탈 수를 계산. 카테고리별 점유율(30개 중 비율)도 파생
5) API 계약: GET /healthz, GET /api/trending(최신 30 + delta/NEW + 카테고리명),
   GET /api/trending?category={id}(8개 고정 카테고리 필터: Music/Gaming/Entertainment/
   News&Politics/Sports/Film&Animation/Science&Tech/Comedy), GET /api/trends?hours=N
   (시계열: 시각별 카테고리 점유율과 진입/이탈 수), POST /api/brief?mode=now|daily
   (now=현재 스냅샷 요약, daily=기준선과의 비교 브리핑)
6) 브리핑(LLM): Bedrock Converse를 SDK 없이 httpx 직접 REST로 호출한다.
   POST https://bedrock-runtime.ap-northeast-2.amazonaws.com/model/global.anthropic.claude-sonnet-4-6/converse
   Authorization: Bearer(환경변수 AWS_BEARER_TOKEN_BEDROCK), body는 system + messages + inferenceConfig
   (maxTokens 800). 타임아웃/파싱 실패는 상태 코드만 담은 에러로. 결과는 같은 시각 버킷 동안 캐시.
   브리핑 내용: 주제 클러스터 3~4개와 왜 뜨는지, 카테고리 분포에서 읽히는 흐름,
   제작/시청 관점 인사이트 2~3줄, 한국어
7) 화면(텍스트 스펙): 라이트/다크 듀얼 테마(시스템 기본 + 토글 + localStorage, 색은 CSS 변수).
   헤더(타이틀, 마지막 수집 시각, 수동 새로고침, 테마 토글), 통계 바(합산 조회수, 채널 수,
   최다 카테고리, 이탈 수), 카테고리 칩 8개, 30장 카드 그리드(순위 배지 + ▲▼ delta 배지 + NEW 마커,
   16:9 썸네일, 제목 2줄 말줄임, 채널, 조회수/좋아요 축약, 클릭 시 유튜브 새 탭),
   시계열 영역(최근 N시간 카테고리 점유율 누적 막대, 외부 차트 라이브러리 없이 인라인 SVG),
   브리핑 카드(now/daily 모드 버튼)
8) 키의 거처: 두 키(YT_API_KEY, AWS_BEARER_TOKEN_BEDROCK)는 환경변수로만, 코드/응답/로그에 노출 금지.
   화면은 우리 API만 호출한다
9) 구현 순서는 Phase 1(수집기 + 스냅샷 스토어 + trending API, uvicorn과 curl 검증) →
   Phase 2(카드 그리드 + 카테고리 필터 + delta/NEW 배지) → Phase 3(시계열 API + SVG 차트) →
   Phase 4(브리핑 now/daily + 캐시 + 마감). 시간 제약 2시간: 테스트 생략, curl과 브라우저로 검증
결정이 필요하면 이 범위 안에서 최소한으로만 물어봐.
자동화 옵션, auto mode 세션에 auto mode on을 선언해 두면 superpowers가 brainstorming → write-plan → execute-plan의 단계 전환과 승인 대기를 자동으로 이어 갑니다. 시간이 빠듯할 때 유용합니다. 단계마다 직접 검토하려면 기본(수동)을 유지하고, 되돌리려면 auto mode off입니다.

브레인스토밍 후 /superpowers:write-plan으로 계획을 만들고 Phase 4개 구성을 승인하세요. 계획이 위 구현 청사진(세 경로/구조 지도/계약)과 대체로 합치하는지 확인하세요.

CHECKPOINT
MISSION 02

Phase 1, 수집기와 스토어

35분

수집 루프가 돌고, 스냅샷이 멱등하게 쌓이고, /api/trending이 delta까지 얹어 주면 이 Phase는 끝입니다.

이 미션의 바탕 학습: Ch1의 지시 → 실행 → 검증 루프. 여유가 있다면 Ch2의 code-reviewer 서브에이전트에게 수집기/스토어 리뷰를 맡겨 보세요.

Claude 세션 입력
/superpowers:execute-plan Phase 1만 구현하고 멈춰줘. backend venv + requirements(fastapi, uvicorn, httpx, apscheduler), collector(videos.list + 기동 시 카테고리 1회 + _stat_int 격리), 스냅샷 링버퍼(멱등, 최근 48개), derive(delta/NEW/이탈/점유율), /api/trending(+?category=)와 /healthz. 수집 주기 60초(config). uvicorn 실행과 curl 검증 방법까지.
Terminal, 실행과 스모크
set -a; source ~/capstone/.env; set +a   # 두 키 로드
cd backend && .venv/bin/uvicorn app.main:app --port 8000 &
sleep 65   # 수집 한 사이클
curl -s localhost:8000/api/trending | head -c 400; echo
출력 예시
[{"rank":1,"delta":0,"new":false,"title":"(급상승 1위 제목)","channel":"...",
  "views":1834211,"likes":52310,"category":"Music","thumbnail":"https://i.ytimg.com/..."} ...]
개념 짚기 (Level 200), 멱등 스냅샷과 부분 실패 격리 스케줄러는 언젠가 겹쳐 돌고, 프로세스는 언젠가 재시작됩니다. "같은 시각 버킷은 한 번만"(원작은 DynamoDB의 attribute_not_exists)이라는 멱등 쓰기가 시계열의 무결성을 지킵니다. 그리고 통계 값 하나가 비정상이어도 0으로 다듬고 계속 갑니다(_stat_int), 카드 한 장 때문에 수집 사이클 전체가 죽으면 흐름에 구멍이 나니까요. 마지막으로 상류 에러 본문(GCP 프로젝트 번호가 담길 수 있는)은 로그에만 남기고 밖에는 상태 코드만, 에러조차 정보를 새지 않게.
막힐 때 열어보기, 멱등 링버퍼 핵심 힌트
store.py 골격 (참고 조각)
from collections import OrderedDict
class SnapshotStore:
    def __init__(self, cap=48):
        self._buf = OrderedDict()   # bucket(예: "2026-08-05T14") -> snapshot
        self.cap = cap
    def put(self, bucket: str, items: list) -> bool:
        if bucket in self._buf:      # 멱등: 같은 버킷은 한 번만
            return False
        self._buf[bucket] = {"captured_at": bucket, "items": items}
        while len(self._buf) > self.cap:
            self._buf.popitem(last=False)
        return True
    def latest(self):   return next(reversed(self._buf.values()), None)
    def baseline(self): # 직전 스냅샷 = delta의 기준선
        vals = list(self._buf.values())
        return vals[-2] if len(vals) >= 2 else None
DEFINITION OF DONE
MISSION 03

Phase 2 + 3, 레이더 화면

40분

30장 그리드에 ▲▼와 NEW가 살아 붙고, 8칩 필터와 점유율 시계열까지 뜨면 레이더가 완성됩니다.

이 미션의 바탕 학습: Ch2의 병렬 감각. 화면을 그리는 동안 시계열 집계 보강을 서브에이전트로 병행할 수 있습니다.

Claude 세션 입력, Phase 2
/superpowers:execute-plan Phase 2만 구현해줘. frontend/index.html 정적 SPA 한 장(빌드 없이 fetch + 렌더): 요구 7의 화면 스펙(듀얼 테마 토글 + 시스템 기본, 헤더, 통계 바 4칸, 카테고리 칩 8개, 30장 카드 그리드에 순위/▲▼ delta/NEW 배지, 축약 숫자, 클릭 시 유튜브 새 탭). FastAPI가 이 파일을 정적 서빙하게 mount까지.
Terminal, 미리보기
# 브라우저에서 /proxy/8000/ 새로고침, 수집 주기(60초)마다 delta가 변한다
Claude 세션 입력, Phase 3
/superpowers:execute-plan Phase 3(시계열)을 구현해줘. /api/trends?hours=N: 시각 버킷별 카테고리 점유율과 진입/이탈 수. 화면 하단에 인라인 SVG 누적 막대(카테고리별 색, 축 라벨은 시각), 외부 차트 라이브러리 금지.
개념 짚기 (Level 200), 기준선이 정보를 만든다 30개 목록 자체는 어느 순간의 사진일 뿐입니다. 직전 스냅샷(기준선)과의 비교가 붙는 순간 ▲3, NEW, "이탈 2개" 같은 정보가 생깁니다. 원작의 baseline 조회가 offsets를 받는 것도 같은 이유, "1시간 전 대비"와 "24시간 전 대비"는 다른 이야기를 들려주니까요. daily 브리핑이 흥미로운 것도 이 비교 덕분입니다.
이 미션의 3대 함정 ① 403 quotaExceeded/keyInvalid = 키 오탈자이거나 콘솔에서 YouTube Data API v3 사용 설정 누락. ② 카테고리가 숫자로 나온다 = 기동 시 카테고리 로드 실패 또는 매핑 누락, 고정 8종 기본명이 안전망입니다. ③ delta가 전부 NEW = 스토어가 요청마다 초기화되는 것, 스토어는 앱 수명과 함께 살아야 합니다(모듈 전역 또는 app.state).
DEFINITION OF DONE
MISSION 04

Phase 4, 브리핑과 개장

30분

SDK 없이 REST 한 장으로 Bedrock을 불러 now/daily 브리핑이 뜨면, 레이더가 말을 시작합니다.

이 미션의 바탕 학습: Ch3에서 본 Bedrock 라우팅과 자격의 배경, 그 위에서 도는 REST 계약.

Claude 세션 입력
/superpowers:execute-plan Phase 4(브리핑)를 구현해줘. llm 모듈: httpx로 POST https://bedrock-runtime.ap-northeast-2.amazonaws.com/model/global.anthropic.claude-sonnet-4-6/converse, Authorization: Bearer(환경변수 AWS_BEARER_TOKEN_BEDROCK), body는 system+messages+inferenceConfig(maxTokens 800), 타임아웃은 504/파싱 실패는 502로. POST /api/brief?mode=now|daily(요구 6의 내용 구성), 같은 시각 버킷 캐시. 화면 브리핑 카드에 now/daily 버튼과 결과 렌더까지.
Terminal, 브리핑 스모크
curl -s -X POST "localhost:8000/api/brief?mode=now" | head -c 300; echo
curl -s -X POST "localhost:8000/api/brief?mode=daily" | head -c 300; echo
개념 짚기 (Level 200), LLM 호출은 결국 HTTP boto3가 하는 일을 벗겨 보면 서명된 HTTP 요청입니다. Bearer 키를 쓰면 서명(SigV4)마저 사라져 Authorization 헤더 + JSON body만 남습니다. 원작 bedrock.py가 55줄인 이유입니다. 이 감각이 있으면 어떤 언어, 어떤 런타임에서도 LLM을 부를 수 있습니다. 단, 실패 매핑은 남습니다: 타임아웃은 504, 이상한 응답은 502, 그리고 에러에 상류 본문을 그대로 싣지 않기(정보 격리).
브리핑이 침묵하거나 인증 에러라면 ① uvicorn을 띄운 셸에 Bearer가 로드됐는지(M0의 로드 라인 후 재실행) ② 403/401이면 키 오탈자 또는 만료 ③ 매번 새로 생성된다면 시각 버킷 캐시가 안 걸린 것, 같은 버킷이면 같은 답이어야 LLM 비용이 지켜집니다.

레이더 → 필터 → 시계열 → 브리핑(now/daily)까지 한 바퀴 돌리고, 화면 스크린샷을 Slack에 공유하세요. 그게 개장입니다. 원작은 여기에 DynamoDB 스냅샷과 CloudFront → ALB(prefix list + X-Origin-Verify 2중 방어) → Fargate 배포, Secrets Manager 주입까지 얹혀 운영 중입니다, 다음 단계가 궁금하다면 그 방향입니다. Ch5의 헤드리스 패턴으로 수집 상태 점검을 파이프라인화하는 것도 좋은 확장입니다.

DEFINITION OF DONE
BONUS

레이더 퀄리티 강화 라운드, 시야를 넓혀라

+α 자유

급상승 30이 떴다면 이제 레이더를 내 것으로 만드는 시간입니다. 강화 카드 중 끌리는 것을 골라 하나씩 주문하세요. 요령은 같습니다: 한 번에 하나, 화면으로 확인하고, 마음에 들면 커밋과 재배포.

A강화 카드 1, 국가 전환
Claude 세션 입력
국가 전환을 추가하자. 헤더에 KR / US / JP 토글 → /api/trending?region= 파라미터로
regionCode를 넘겨 각국 급상승 30을 받아온다. 카테고리 매핑 캐시는 국가별로 분리하고,
통계 바와 필터 칩도 전환된 국가 기준으로 다시 계산. 선택은 localStorage에 기억.
B강화 카드 2, 카테고리 딥다이브
Claude 세션 입력
카테고리 딥다이브를 추가하자. 필터 칩을 한 번 더 누르면 딥다이브 모드:
/api/trending?category={id}가 videos.list의 videoCategoryId 파라미터로
그 카테고리만의 급상승 25개를 새로 받아와 그리드를 교체한다.
상단에 "Music 딥다이브" 배지와 나가기 버튼, 쿼터는 여전히 호출당 1유닛.
C강화 카드 3, 관점 브리핑
Claude 세션 입력
브리핑에 관점 프리셋을 추가하자. 브리핑 카드에 마케터 / 크리에이터 / 시청자 버튼 3개:
같은 30개 데이터를 넣되 프롬프트의 관점 지시만 바꿔서(마케터=캠페인 기회, 크리에이터=콘텐츠 공식,
시청자=오늘 볼 것 추천) /api/brief?persona= 로 호출. 결과 상단에 선택된 관점 배지.
D강화 카드 4, 순위 변동의 감각
Claude 세션 입력
새로고침 사이의 변화를 보여주자. 직전 응답의 순위를 메모리(또는 localStorage)에 두고,
다시 불렀을 때 각 카드에 NEW / ▲2 / ▼1 배지를 단다. 사라진 영상은 이번엔 표시하지 않지만
"이탈 N개" 카운트를 통계 바에. 급상승은 스냅샷이 아니라 흐름이라는 감각을 화면에.
E강화 카드 5, 나만의 레이더로

여기부터는 정해진 카드가 없습니다. 당신이 편성 PD입니다. 급상승을 보며 궁금했던 것 하나를 골라, 위 카드들처럼 직접 요구사항을 써서 주문하세요. 아이디어가 필요하면: 채널 단골 랭킹(30개 중 중복 채널), 제목 키워드 워드클라우드, 좋아요/조회수 비율 정렬, 숏폼 필터(길이 60초 이하), 게시 시각 분포.

Claude 세션 입력, 빈칸을 내 아이디어로
내 레이더에 [내가 원하는 기능]을 추가하자. 규칙:
1) 데이터: [필요한 필드와 출처, 추가 API 파라미터가 있다면 명시]
2) 화면: [어디에 어떻게 보이는지]
3) 계약: 유튜브 키는 서버에만, 화면은 우리 API만 본다는 원칙은 깨지 않는다
4) 쿼터: 새 호출이 생기면 호출당 유닛을 계산해 알려줘
구현 전에 이 규칙을 네 계획으로 다시 말해주고, 승인하면 진행해.
강화 라운드의 요령 ① 카드 하나 = 커밋 하나, 되돌아갈 지점을 만들며 갑니다. ② 화면으로 확인하고 다른 점을 말로 다듬으세요. ③ 마지막엔 cdk deploy로 재배포해 Slack의 내 URL을 최신으로.
BONUS DONE, 진행률 미집계
OPTION

하네스 엔지니어링, 작품을 성숙시키고 등급을 받다

+15분 옵션

지금까지 만든 것은 결과물입니다. 이 옵션은 그 결과물을 만든 작업 환경, 즉 하네스 (CLAUDE.md, hooks, skills, commands, agents)를 표준 구조로 성숙시키고(project-init), 그 품질을 6개 차원(정확성, 안전성, 완전성, 실행 가능성, 일관성, 검증 가능성)에서 채점받는(harness-eval) 단계입니다. superpowers의 3단(brainstorm, write-plan, execute-plan) 뒤에 4단 성숙화와 5단 평가를 붙이는 셈입니다. 캡스톤 공통의 마무리 의식입니다.

1플러그인 2종 설치
Terminal, 전체 복사
claude plugin marketplace add https://github.com/whchoi98/project-init
claude plugin install project-init@project-init
claude plugin marketplace add https://github.com/whchoi98/harness-eval
claude plugin install harness-eval@harness-eval
claude plugin list

설치 후 새 Claude 세션을 시작하세요, superpowers 때와 같은 규칙입니다.

2성숙화, /project-init:init-project와 /project-init:sync-docs
Claude 세션 입력 (새 세션, worktree에서)
/project-init:init-project .

이 프로젝트의 하네스는 아직 얇습니다(SPA 하나와 CDK 스택). 기존 프로젝트 감지 모드가 있는 것은 보존하고, 없는 것(문서 스캐폴딩, 시크릿 스캔 훅, /review 커맨드, 테스트 골격 등)만 채웁니다. CLAUDE.md가 이미 있으면 덮어쓰기 전에 물어봅니다. 끝나면 이어서:

Claude 세션 입력
/project-init:sync-docs
출력 예시 (요약)
## Sync Report
### Quality Scores (Before -> After)
| ./CLAUDE.md           | D (48) | B (82) | +34 |
| ./lambda/CLAUDE.md    | F (10) | C (61) | +51 |
### Changes Made
- Files created: 4 / Files updated: 2 / Runbooks missing: 1
3평가, /harness-eval로 등급 받기
Claude 세션 입력
/harness-eval:quick
출력 예시
{"mode": "quick", "scores": {"overall": 7.0, "grade": "B"}, "checklist": {"pass": 12, "warn": 3, "fail": 1}}

이 캡스톤의 비자명한 지식은 키의 거처입니다. "왜 유튜브 키는 서버에만 있는가, 왜 카테고리 매핑을 캐시하는가"를 CLAUDE.md의 패턴으로 기록해 두면 sync-docs 점수가 오르고, 다음에 이 저장소를 여는 Claude가 같은 원칙으로 확장합니다. 여유가 있다면 결정을 기록으로 남기세요:

Claude 세션 입력
/add-adr server-side-key-isolation
/add-runbook trend-radar-deploy
시간과 신뢰 가드 잔여 시간이 빠듯하면 설치와 /harness-eval:quick(30초 체크리스트)만으로 충분합니다, /project-init:init-project는 17단계라 수 분이 걸립니다. /harness-eval:standard의 동적 분석은 대상 프로젝트의 훅과 테스트를 실제로 실행합니다. 방금 본인이 만든 프로젝트라 안전하지만, 남의 저장소를 평가할 때는 --static-only 플래그가 예의이자 안전입니다.
OPTION DONE, 진행률 미집계

미션 종료

수집은 루프로, 저장은 멱등하게, 변화는 기준선으로, LLM은 REST로. 흐름을 읽는 서비스의 문법입니다.

가져가는 것내용
스냅샷이 완충재화면 트래픽과 수집을 분리하면 쿼터, 비용, 응답 속도가 한 번에 잡힌다
기준선의 힘목록은 사진, 비교가 정보. ▲▼/NEW/이탈은 전부 "직전과의 차이"에서 나온다
REST 감각SDK를 벗기면 Authorization 헤더와 JSON, LLM 호출의 본질은 HTTP 한 장
NEXT

포털로

다른 캡스톤(1 Press Start, 2 Market Desk on Web, 3 Frame It)도 같은 리듬입니다. 워크샵 포털에서 다음 미션을 고르세요.