지금 트렌드를 다루는 법
숙지다른 캡스톤과 같은 기조입니다: 빈 폴더에서 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 호출은 아껴 쓴다 |
이 캡스톤을 마치면 다음 네 가지를 설명할 수 있고, 다시 만들 수 있습니다.
| 목표 | 검증되는 순간 |
|---|---|
| 주기 수집 파이프라인과 멱등 스냅샷 저장을 설계하는 법 | M2에서 60초마다 스냅샷이 쌓이되 같은 버킷은 한 번만 저장될 때 |
| 기준선 비교로 변화(델타)를 파생하는 법 | M3에서 ▲▼/NEW 배지가 직전 스냅샷과의 비교로 정확히 붙을 때 |
| 외부 SDK 없이 REST 계약만으로 LLM을 부르는 법 | M4에서 httpx 한 장으로 Bedrock Converse가 응답할 때 |
| 서드파티 키를 서버에 격리하는 프록시 패턴 | 브라우저 개발자 도구 어디에도 두 키가 보이지 않을 때 |
이 캡스톤은 새 기술을 가르치지 않습니다. Ch1~6에서 배운 것을 실전 배치합니다.
| 챕터 | 이 캡스톤에서 재등장하는 곳 |
|---|---|
| Ch1 설치부터 Headless까지 | M0~전체: 세션 운용, 지시 → 실행 → 검증 루프 |
| Ch2 Subagents | M2~M3: code-reviewer 서브에이전트로 수집기/파생 로직 리뷰, 백엔드와 화면 병행 |
| Ch3 Admin Setup | M4: Bedrock 라우팅과 자격의 배경, Bearer 키가 조직 통제선 안에서 도는 이유 |
| Ch4 Settings, 훅과 스킬 | OPT: 하네스 성숙화. 원작 youtube-trend의 .claude/(커맨드 3, 스킬 4)가 도달점의 표본입니다 |
| Ch5 CLI Reference | 확장 방향: 헤드리스 패턴으로 수집 사이클 상태를 점검하는 파이프라인 |
| Ch6 Agent SDK | 확장 방향: 브리핑 생성을 코드에서 부르는 상주 리포터의 다음 단계 |
구현 청사진, 원작 상세 분석
참고원작 youtube-trend의 실코드를 분석해 뽑은 구현 지도입니다. M1 설계가 이 구조로 수렴하도록 방향을 잡아 주고, 계획이 이상하게 흐르면 여기로 돌아와 대조하세요.
[수집 루프] 주기마다 (워크샵 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 비용도, 응답 속도도 한 번에 잡힙니다.
| 구성 | 원작 파일 | 핵심 계약 |
|---|---|---|
| 수집기 | collector/youtube.py | httpx 주입(테스트는 MockTransport), 상류 에러 본문은 로그에만, 예외엔 상태 코드만 |
| 카테고리 | categories.py | 고정 8종 + 기동 시 videoCategories(hl=ko) 1회, 실패해도 기본명으로 동작 |
| 스냅샷 스토어 | store/table.py | pk=scope, sk=시각 버킷, attribute_not_exists로 멱등, latest/baseline 조회 |
| 파생 | derive.py, aggregate.py | 기준선 대비 delta/NEW/이탈, 카테고리 점유율과 시계열 집계 |
| 브리핑 | llm/bedrock.py, llm/prompts.py | Converse REST 직접(httpx), 타임아웃 504/파싱 502로 매핑, now/daily 프롬프트 분리 |
| API | api/trending.py, trends.py, videos.py, brief.py | 화면이 그대로 그리는 JSON, /healthz 별도 |
| 인프라(원작) | infra/stacks/service.py | CloudFront → ALB(prefix list + X-Origin-Verify 2중 방어) → Fargate, Secrets는 기동 시 주입 |
| 계약 | 내용 |
|---|---|
| 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가 컨테이너 기동 시 주입. 워크샵은 셸 환경변수로 같은 원칙 |
| 선택 | 이유 |
|---|---|
| 수집과 조회의 분리 | 화면 트래픽이 유튜브 쿼터를 건드리지 못한다, 스냅샷이 완충재 |
| 멱등 스냅샷 | 스케줄러가 겹쳐 돌아도, 재시작해도 같은 버킷은 한 번, 시계열이 오염되지 않는다 |
| Bedrock REST 직접 | SDK 없이 Authorization 헤더 + JSON 한 장, "LLM 호출은 결국 HTTP"를 체득 |
| 인라인 SVG 차트 | 점유율 막대에 차트 라이브러리는 과잉, 데이터가 곧 rect의 높이 |
사전 준비, 두 개의 키
15분이 캡스톤은 키가 둘입니다: YouTube API 키(급상승 데이터)와 Bedrock Bearer 키(브리핑). 발급부터 스모크까지 여기서 끝냅니다.
① console.cloud.google.com 로그인 후 상단에서 프로젝트를 선택하거나 새로 만듭니다.
② 좌측 "API 및 서비스 → 라이브러리"에서 YouTube Data API v3를 검색해 사용 설정.
③ "API 및 서비스 → 사용자 인증 정보 → 사용자 인증 정보 만들기 → API 키". "서비스 계정을 통해 API 호출 인증(바인딩)" 옵션이 보이면 체크하지 않고 그대로 생성합니다, Vertex 계열용 옵션이라 유튜브에는 불필요합니다.
④ 생성된 키를 복사합니다. 무료 쿼터(일 10,000 유닛)로 이 캡스톤은 충분합니다.
YT_API_KEY에 넣고 진행하세요. 이 캡스톤의 호출량은 새로고침당 1~2유닛이라
공용 키 하나로 전원이 여유 있게 돕니다.
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를 이미 넣었다면 그대로 유지됩니다(중복 방지 가드).
set -a; source ~/capstone/.env; set +a
curl -s "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular®ionCode=KR&maxResults=1&key=$YT_API_KEY" \
| jq -r '.items[0].snippet.title'
(지금 한국 급상승 1위 영상의 제목이 한 줄로)
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
설계, 브레인스토밍
15분수집/저장/파생의 분리, REST 직접 브리핑, 듀얼 테마까지 요구 9조항으로 넘깁니다.
이 미션의 바탕 학습: Ch1의 세션 운용과 Capstone Setup의 superpowers 워크플로.
cd ~/capstone/capstone-4 && 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 on을 선언해 두면 superpowers가
brainstorming → write-plan → execute-plan의 단계 전환과 승인 대기를 자동으로 이어 갑니다.
시간이 빠듯할 때 유용합니다. 단계마다 직접 검토하려면 기본(수동)을 유지하고, 되돌리려면
auto mode off입니다.
브레인스토밍 후 /superpowers:write-plan으로 계획을 만들고
Phase 4개 구성을 승인하세요. 계획이 위 구현 청사진(세 경로/구조 지도/계약)과 대체로 합치하는지 확인하세요.
Phase 1, 수집기와 스토어
35분수집 루프가 돌고, 스냅샷이 멱등하게 쌓이고, /api/trending이 delta까지 얹어 주면 이 Phase는 끝입니다.
이 미션의 바탕 학습: Ch1의 지시 → 실행 → 검증 루프. 여유가 있다면 Ch2의 code-reviewer 서브에이전트에게 수집기/스토어 리뷰를 맡겨 보세요.
/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 검증 방법까지.
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/..."} ...]
attribute_not_exists)이라는 멱등 쓰기가 시계열의 무결성을 지킵니다.
그리고 통계 값 하나가 비정상이어도 0으로 다듬고 계속 갑니다(_stat_int), 카드 한 장 때문에
수집 사이클 전체가 죽으면 흐름에 구멍이 나니까요. 마지막으로 상류 에러 본문(GCP 프로젝트 번호가
담길 수 있는)은 로그에만 남기고 밖에는 상태 코드만, 에러조차 정보를 새지 않게.
막힐 때 열어보기, 멱등 링버퍼 핵심 힌트
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
Phase 2 + 3, 레이더 화면
40분30장 그리드에 ▲▼와 NEW가 살아 붙고, 8칩 필터와 점유율 시계열까지 뜨면 레이더가 완성됩니다.
이 미션의 바탕 학습: Ch2의 병렬 감각. 화면을 그리는 동안 시계열 집계 보강을 서브에이전트로 병행할 수 있습니다.
/superpowers:execute-plan Phase 2만 구현해줘. frontend/index.html 정적 SPA 한 장(빌드 없이 fetch + 렌더): 요구 7의 화면 스펙(듀얼 테마 토글 + 시스템 기본, 헤더, 통계 바 4칸, 카테고리 칩 8개, 30장 카드 그리드에 순위/▲▼ delta/NEW 배지, 축약 숫자, 클릭 시 유튜브 새 탭). FastAPI가 이 파일을 정적 서빙하게 mount까지.
# 브라우저에서 /proxy/8000/ 새로고침, 수집 주기(60초)마다 delta가 변한다
/superpowers:execute-plan Phase 3(시계열)을 구현해줘. /api/trends?hours=N: 시각 버킷별 카테고리 점유율과 진입/이탈 수. 화면 하단에 인라인 SVG 누적 막대(카테고리별 색, 축 라벨은 시각), 외부 차트 라이브러리 금지.
Phase 4, 브리핑과 개장
30분SDK 없이 REST 한 장으로 Bedrock을 불러 now/daily 브리핑이 뜨면, 레이더가 말을 시작합니다.
이 미션의 바탕 학습: Ch3에서 본 Bedrock 라우팅과 자격의 배경, 그 위에서 도는 REST 계약.
/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 버튼과 결과 렌더까지.
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
레이더 → 필터 → 시계열 → 브리핑(now/daily)까지 한 바퀴 돌리고, 화면 스크린샷을 Slack에 공유하세요. 그게 개장입니다. 원작은 여기에 DynamoDB 스냅샷과 CloudFront → ALB(prefix list + X-Origin-Verify 2중 방어) → Fargate 배포, Secrets Manager 주입까지 얹혀 운영 중입니다, 다음 단계가 궁금하다면 그 방향입니다. Ch5의 헤드리스 패턴으로 수집 상태 점검을 파이프라인화하는 것도 좋은 확장입니다.
레이더 퀄리티 강화 라운드, 시야를 넓혀라
+α 자유급상승 30이 떴다면 이제 레이더를 내 것으로 만드는 시간입니다. 강화 카드 중 끌리는 것을 골라 하나씩 주문하세요. 요령은 같습니다: 한 번에 하나, 화면으로 확인하고, 마음에 들면 커밋과 재배포.
국가 전환을 추가하자. 헤더에 KR / US / JP 토글 → /api/trending?region= 파라미터로
regionCode를 넘겨 각국 급상승 30을 받아온다. 카테고리 매핑 캐시는 국가별로 분리하고,
통계 바와 필터 칩도 전환된 국가 기준으로 다시 계산. 선택은 localStorage에 기억.
카테고리 딥다이브를 추가하자. 필터 칩을 한 번 더 누르면 딥다이브 모드:
/api/trending?category={id}가 videos.list의 videoCategoryId 파라미터로
그 카테고리만의 급상승 25개를 새로 받아와 그리드를 교체한다.
상단에 "Music 딥다이브" 배지와 나가기 버튼, 쿼터는 여전히 호출당 1유닛.
브리핑에 관점 프리셋을 추가하자. 브리핑 카드에 마케터 / 크리에이터 / 시청자 버튼 3개:
같은 30개 데이터를 넣되 프롬프트의 관점 지시만 바꿔서(마케터=캠페인 기회, 크리에이터=콘텐츠 공식,
시청자=오늘 볼 것 추천) /api/brief?persona= 로 호출. 결과 상단에 선택된 관점 배지.
새로고침 사이의 변화를 보여주자. 직전 응답의 순위를 메모리(또는 localStorage)에 두고,
다시 불렀을 때 각 카드에 NEW / ▲2 / ▼1 배지를 단다. 사라진 영상은 이번엔 표시하지 않지만
"이탈 N개" 카운트를 통계 바에. 급상승은 스냅샷이 아니라 흐름이라는 감각을 화면에.
여기부터는 정해진 카드가 없습니다. 당신이 편성 PD입니다. 급상승을 보며 궁금했던 것 하나를 골라, 위 카드들처럼 직접 요구사항을 써서 주문하세요. 아이디어가 필요하면: 채널 단골 랭킹(30개 중 중복 채널), 제목 키워드 워드클라우드, 좋아요/조회수 비율 정렬, 숏폼 필터(길이 60초 이하), 게시 시각 분포.
내 레이더에 [내가 원하는 기능]을 추가하자. 규칙:
1) 데이터: [필요한 필드와 출처, 추가 API 파라미터가 있다면 명시]
2) 화면: [어디에 어떻게 보이는지]
3) 계약: 유튜브 키는 서버에만, 화면은 우리 API만 본다는 원칙은 깨지 않는다
4) 쿼터: 새 호출이 생기면 호출당 유닛을 계산해 알려줘
구현 전에 이 규칙을 네 계획으로 다시 말해주고, 승인하면 진행해.
cdk deploy로 재배포해 Slack의 내 URL을 최신으로.
하네스 엔지니어링, 작품을 성숙시키고 등급을 받다
+15분 옵션지금까지 만든 것은 결과물입니다. 이 옵션은 그 결과물을 만든 작업 환경, 즉 하네스 (CLAUDE.md, hooks, skills, commands, agents)를 표준 구조로 성숙시키고(project-init), 그 품질을 6개 차원(정확성, 안전성, 완전성, 실행 가능성, 일관성, 검증 가능성)에서 채점받는(harness-eval) 단계입니다. superpowers의 3단(brainstorm, write-plan, execute-plan) 뒤에 4단 성숙화와 5단 평가를 붙이는 셈입니다. 캡스톤 공통의 마무리 의식입니다.
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 때와 같은 규칙입니다.
/project-init:init-project .
이 프로젝트의 하네스는 아직 얇습니다(SPA 하나와 CDK 스택). 기존 프로젝트 감지 모드가 있는 것은 보존하고, 없는 것(문서 스캐폴딩, 시크릿 스캔 훅, /review 커맨드, 테스트 골격 등)만 채웁니다. CLAUDE.md가 이미 있으면 덮어쓰기 전에 물어봅니다. 끝나면 이어서:
/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
/harness-eval:quick
{"mode": "quick", "scores": {"overall": 7.0, "grade": "B"}, "checklist": {"pass": 12, "warn": 3, "fail": 1}}
이 캡스톤의 비자명한 지식은 키의 거처입니다. "왜 유튜브 키는 서버에만 있는가, 왜 카테고리 매핑을 캐시하는가"를 CLAUDE.md의 패턴으로 기록해 두면 sync-docs 점수가 오르고, 다음에 이 저장소를 여는 Claude가 같은 원칙으로 확장합니다. 여유가 있다면 결정을 기록으로 남기세요:
/add-adr server-side-key-isolation
/add-runbook trend-radar-deploy
미션 종료
수집은 루프로, 저장은 멱등하게, 변화는 기준선으로, LLM은 REST로. 흐름을 읽는 서비스의 문법입니다.
| 가져가는 것 | 내용 |
|---|---|
| 스냅샷이 완충재 | 화면 트래픽과 수집을 분리하면 쿼터, 비용, 응답 속도가 한 번에 잡힌다 |
| 기준선의 힘 | 목록은 사진, 비교가 정보. ▲▼/NEW/이탈은 전부 "직전과의 차이"에서 나온다 |
| REST 감각 | SDK를 벗기면 Authorization 헤더와 JSON, LLM 호출의 본질은 HTTP 한 장 |