CAPSTONE 2 / MARKET DESK ON WEB / 135 MIN

웹 시황 대시보드

FastAPI + React + Bedrock SSE, 브라우저에서 뛰는 시황 데스크

시세와 차트가 브라우저에 흐르고, AI 분석이 타자기처럼 스트리밍되는 웹 시황 데스크를 빈 폴더에서 빌드합니다. FastAPI 백엔드가 yfinance/pykrx를 캐시 뒤에 정리하고, React 대시보드가 45초마다 숨 쉬고, Bedrock의 종목 분석이 SSE로 한 글자씩 도착합니다. 배포 없이 로컬 완결, 다른 참가자와의 의존성도 없습니다.

소요 시간 135분 미션 5개 + DoD 산출물 브라우저에서 뛰는 내 시황 데스크 Update 2026.08
MISSION BRIEFING

목표물과 기조

숙지

다른 캡스톤과 같은 기조입니다: 빈 폴더에서 superpowers 워크플로로 설계하고 Phase 단위로 빌드합니다. 목표물은 실존 프로젝트 stock-monitoring(FastAPI + React + Bedrock, AWS ECS 운영)을 135분 스코프로 압축한 웹 시황 데스크입니다. 실데이터이며, 45초마다 바뀝니다.

레이어구성핵심 원칙
백엔드FastAPI, /api/market/*, /api/stocks/*, 인메모리 TTL 캐시업스트림 호출은 캐시 뒤로, 같은 키 동시 요청은 한 번만(single-flight)
프런트React(Vite) + react-query + lightweight-charts화면은 우리 API만 본다, 45초 폴링, AI만 fetch 직접
AIBedrock converse_stream, SSE(phase → delta → final)AWS_BEARER_TOKEN_BEDROCK, 키 없으면 503과 패널 비활성
완결성make run 한 번으로 :8000 통합 서빙, 배포 없음프록시(/proxy/8000/)로 확인, 스크린샷이 피날레

1. Dashboard (메인 화면)

기능설명
경제지표 바WTI, Gold, 환율, 금리, Bitcoin 등 config의 지표를 실시간 표시
시장 지수S&P 500, NASDAQ, DOW / KOSPI, KOSDAQ - 카드 5장, 등락색
종목 테이블US/KR 탭 전환, Symbol/Name/Price/Change/%/Volume, 상승 초록/하락 빨강
자동 갱신react-query 45초 폴링, 장 마감 시간대는 백엔드가 600초 절전

2. StockDetail (종목 상세)

기능설명
가격 헤더현재가, 등락률, 거래량 (StockHeader)
캔들 차트lightweight-charts, 1W/1M/3M/1Y 기간 탭 (PriceChart)
수익률과 52주기간 수익률 4종(ReturnsRow) + 52주 범위 내 현재 위치(Week52Bar)
AI 종목 분석버튼 → SSE 스트리밍, 기술적 분석/투자 포인트/리스크 3절 (AIPanel)
L학습 목표, Level 200

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

목표검증되는 순간
업스트림 API를 캐시와 single-flight 뒤로 정리하는 백엔드 계약 설계M2에서 같은 curl 두 번, 두 번째가 즉시 도착할 때
폴링(react-query)과 스트리밍(SSE)을 구분해 쓰는 프런트 데이터 전략M3의 45초 폴링과 M4의 델타 누적이 다른 코드 경로로 공존할 때
Bedrock 스트리밍(converse_stream)을 제품 기능으로 포장하는 법M4에서 phase 배지와 타자기 렌더가 동작할 때
superpowers Phase 리듬으로 풀스택을 조립하는 법M1의 계획이 M2~M4에서 그대로 소화될 때
R챕터 연결 지도, 배운 내용이 재등장하는 곳

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

챕터이 캡스톤에서 재등장하는 곳
Ch1 설치부터 Headless까지M0~전체: 세션 운용, 지시 → 실행 → 검증 루프, CLAUDE.md를 존중하는 습관
Ch2 SubagentsM2~M3: code-reviewer 서브에이전트로 Phase 산출물 리뷰, 백엔드/프런트 병렬 진행
Ch3 Admin SetupM0/M4: Bedrock 라우팅과 자격의 배경, Bearer 키가 조직 통제선 안에서 도는 이유
Ch4 Settings, 훅과 스킬OPT: 하네스 성숙화, 훅과 스킬을 프로젝트 자산으로 승격하는 마무리 의식
Ch5 CLI Reference확장 방향: 헤드리스 패턴으로 /api/health 스모크를 파이프라인화
Ch6 Agent SDK확장 방향: 백엔드에 in-process 도구로 Claude를 심는 다음 단계의 방향
규칙, 캡스톤 공통 ① 코드와 파일 구성을 직접하지 않고, Claude에게 지시해서 만듭니다 ② 각 Phase의 DoD를 통과해야 다음으로 ③ 막히면 "막힐 때 열어보기"의 힌트를 씁니다. 다른 참가자와의 의존성은 없습니다.
BLUEPRINT

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

참고

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

0요청 흐름 한 눈에
두 개의 데이터 경로, 폴링과 스트리밍
[폴링 경로]  45초마다 (장 마감 시 600초)
Browser ── react-query ──> FastAPI ──> TTL Cache ──(miss, single-flight)──> yfinance / pykrx
   ^                          |            |
   +──── JSON 완결 응답 ──────+      (hit) 즉시 반환, 업스트림 무호출

[스트리밍 경로]  버튼 눌렀을 때만
Browser ── fetch(SSE) ──> FastAPI ── converse_stream ──> Bedrock (Sonnet 4.6)
   ^                          |
   +── phase → delta* → final +   (한 글자씩, react-query를 쓰지 않는 유일한 경로)

Level 200의 핵심 구분입니다: 완결된 결과를 주기적으로 당기는 경로(폴링)와 부분 결과를 밀어 받는 경로(스트리밍)는 캐시 전략도, 프런트 도구도 다릅니다. 이 캡스톤은 둘을 한 앱에서 병용합니다.

1화면과 컴포넌트 지도 (원작 실물)
화면구성 컴포넌트전환
Dashboard지표 바, 지수 카드, 종목 테이블(US/KR 탭)종목 클릭 → StockDetail
StockDetailStockHeader, FundamentalCards, PriceChart(캔들), ReturnsRow, Week52Bar, OrderBook, InvestorPanel, StockNews, AIPanelreact-router, 뒤로 가기 복귀
ArticleAnalysis기사 본문 + AI 분석(react-markdown 렌더)뉴스에서 진입

워크샵 스코프는 핵심 구성만: StockHeader + PriceChart + ReturnsRow + Week52Bar + AIPanel. OrderBook/InvestorPanel/StockNews/ArticleAnalysis는 바이브 코딩 라운드의 확장 과제로 남깁니다.

2API 계약 (원작 라우트 실측)
라우트역할
GET /api/health살아있음 확인
GET /api/market/overview지수 5종 + 경제지표 (Dashboard 상단)
GET /api/market/quotesUS/KR 종목 시세 일괄
GET /api/stocks/{symbol}가격 헤더 + 기간 수익률 + 52주 범위
GET /api/stocks/{symbol}/chart?range=캔들 OHLC (1w/1m/3m/1y, 원작은 기간별 TTL 캐시)
POST /api/ai/stocks/{symbol}SSE 스트리밍 분석 (원작은 기사 분석 POST /api/ai/articles도 제공)
3데이터와 운영 계약
계약내용
갱신 주기주가 45초, 뉴스 120초, 장 마감 시간대 600초 절전 (market-hours-aware)
캐시원작은 L1 메모리 + L2 DynamoDB 티어드(TTL: 차트 기간별, 펀더멘털 12h, AI 6h, L2 24h) + single-flight 잠금 + 60초 시세 오버레이. 워크샵은 L1 메모리 + single-flight
AI 스트리밍호출 프리미티브는 converse_stream 하나(블로킹 invoke_model은 삭제됨). SSE 이벤트 phase → delta* → final, 시나리오별 maxTokens 상한 분리(잘림 신호 ai_stream_truncated)
프런트 데이터react-query가 45초 폴링으로 화면을 살리되, AI 스트리밍만 fetch 직접: react-query는 "완결된 결과 하나"를 캐시하는 모델이라 델타 누적에 맞지 않는다(원작 주석)
키의 거처Bedrock 인증은 boto3 기본 해석에 맡긴다 = AWS_BEARER_TOKEN_BEDROCK 환경변수를 그대로 인식. 자격 실패는 503으로 매핑, 앱은 정상
4기술 선택 5
선택이유
FastAPI라우트 = 계약, pydantic 응답 모델로 프론트가 그대로 그린다
react-query폴링/캐시/로딩 상태를 선언형으로, 45초 갱신이 한 줄
lightweight-charts금융 캔들 차트의 표준, 가볍고 기간 전환이 빠르다
SSEAI 응답을 한 글자씩, WebSocket보다 단순한 단방향 스트림
make run프론트 빌드를 backend/static에 넣어 :8000 하나로 통합 서빙
MISSION 00

사전 준비

10분

Python과 Node 확인, 프로젝트 폴더, 그리고 Bearer 키(.env)까지. Phase 4의 AI 스트리밍에 필요합니다.

Terminal, 전체 복사
python3 --version && node --version    # Python 3.11+, Node 20+
mkdir -p ~/capstone/capstone-2 && cd ~/capstone/capstone-2
git init -q 2>/dev/null; git config user.name "capstone"; git config user.email "capstone@lab.local"

다른 캡스톤에서 이미 만들었다면 ~/capstone/.env가 있으니 1/2는 건너뛰세요.

Terminal 1/2, .env에 Bearer 토큰 저장
mkdir -p ~/capstone
grep -q AWS_BEARER_TOKEN_BEDROCK ~/capstone/.env 2>/dev/null || cat >> ~/capstone/.env << 'ENVEOF'
AWS_BEARER_TOKEN_BEDROCK=<발급받은 Bedrock API 키>
ENVEOF
Terminal 2/2, 로드 + Converse 스모크
set -a; source ~/capstone/.env; set +a
aws bedrock-runtime converse \
  --region ap-northeast-2 \
  --model-id global.anthropic.claude-sonnet-4-6 \
  --messages '[{"role":"user","content":[{"text":"ping"}]}]' \
  --inference-config '{"maxTokens":30}' \
  --query 'output.message.content[0].text' --output text
CHECKPOINT
MISSION 01

설계, 브레인스토밍

15분

API 계약, 캐시, SSE 스트리밍, Phase 순서까지 요구 9조항으로 넘깁니다.

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

Terminal (~/capstone/capstone-2)
cd ~/capstone/capstone-2 && claude
Claude 세션 입력 1/2, 설계
/superpowers:brainstorming 다음 요구사항으로 "Market Desk on Web", 웹 시황 대시보드를 설계하자.
1) 구조: backend/(Python 3.11+ FastAPI, app/api 라우트, app/services 데이터, app/core/config.py 상수,
   venv + requirements.txt: fastapi, uvicorn, yfinance, pykrx, httpx, boto3) +
   frontend/(Vite + React + TypeScript, 의존성: @tanstack/react-query, lightweight-charts)
2) 데이터: yfinance(미국 주식 + 지수 ^GSPC/^IXIC/^DJI + 경제지표), pykrx(한국 주식, 지수는 yfinance
   ^KS11/^KQ11). 종목/지표 목록은 config.py 상수로, 실데이터 목록은 설계 직후 이어서 제공한다.
   시작은 US 20 + KR 20으로 잘라 쓰고 전체 50 + 50은 config에서 여는 구조로.
   개별 심볼 실패는 건너뛰고, US 시세는 yf.download 벌크로
3) API 계약(FastAPI 라우트): GET /api/health, GET /api/market/overview(지수 5 + 지표),
   GET /api/market/quotes(US/KR 시세), GET /api/stocks/{symbol}(가격 헤더 + 기간 수익률 + 52주 범위),
   GET /api/stocks/{symbol}/chart?range=1w|1m|3m|1y(캔들 OHLC). 응답은 프론트가 그대로 그릴 수 있는 JSON
4) 캐시: 인메모리 TTL 캐시 하나로 시작(시세 45초, 차트는 기간별로 길게), 같은 키 동시 요청은 한 번만
   부르는 single-flight 잠금. 장 마감 시간대에는 갱신 주기를 600초로 늦춘다
5) Dashboard 화면: 상단 경제지표 바, 지수 카드 5장(등락색), 종목 테이블(US/KR 탭, Symbol/Name/Price/
   Change/%/Volume, 상승 초록/하락 빨강), react-query로 45초 자동 갱신
6) StockDetail 화면: 테이블에서 클릭 진입. StockHeader(현재가, 등락률, 거래량), lightweight-charts
   캔들 차트(1W/1M/3M/1Y 기간 탭), ReturnsRow(기간 수익률 4종), Week52Bar(52주 범위 내 현재 위치),
   뒤로 가기로 대시보드 복귀
7) AI 분석(SSE 스트리밍): POST /api/ai/stocks/{symbol}가 Bedrock converse_stream
   (global.anthropic.claude-sonnet-4-6, maxTokens 1024)으로 기술적 분석/투자 포인트/리스크를 한국어로
   생성하며 text/event-stream으로 흘린다. 이벤트는 phase → delta(부분 텍스트)* → final 순서.
   프론트 AIPanel은 델타를 누적해 타자기처럼 렌더하고, 이 호출만 react-query 대신 fetch를 직접 쓴다
   (react-query는 완결된 결과 캐시 모델이라 점진 델타에 맞지 않는다).
   인증은 환경변수 AWS_BEARER_TOKEN_BEDROCK(발급 API 키), boto3 클라이언트에 자격 인자를 넣지 않고
   리전 ap-northeast-2. Bedrock IAM 정책은 만들지 않으며, 키가 없으면 503을 주고 패널만 비활성
8) UI 무드(텍스트 스펙): 다크 딥네이비, 숫자 우측 정렬, 상승 초록 ▲ / 하락 빨강 ▼,
   AI 패널은 스트리밍 중 phase 배지(fetching/analyzing)를 보여준다
9) 구현 순서는 Phase 1(백엔드 코어: overview/quotes/{symbol}/chart + 캐시, uvicorn과 curl 검증) →
   Phase 2(Dashboard, dev 서버로 미리보기) → Phase 3(StockDetail + 캔들 차트) →
   Phase 4(AI SSE + 프론트 빌드를 backend/static으로 넣어 :8000 통합 서빙).
   시간 제약 2시간: 테스트 생략, curl과 브라우저로 검증. 호가/투자자 동향/뉴스는 후순위(확장 과제로만 남긴다)
결정이 필요하면 이 범위 안에서 최소한으로만 물어봐.
Claude 세션 입력 2/2, config 실데이터 (설계 대화에 이어서)
이어서, 방금 설계에 쓸 config.py 실데이터야. 이 상수들을 config.py에 그대로 반영해줘.
이름 매핑(US_STOCK_NAMES)과 섹터 매핑(US_STOCK_SECTORS)은 네가 아는 대로 채워.

REFRESH_INTERVAL = 45
CLOSED_REFRESH_INTERVAL = 600  # 장 마감 시 절전 주기
NEWS_REFRESH_INTERVAL = 120

US_INDICES = {"^GSPC": "S&P 500", "^IXIC": "NASDAQ", "^DJI": "DOW"}
KR_INDICES = {"^KS11": "KOSPI", "^KQ11": "KOSDAQ"}

US_STOCKS = [
  "AAPL","MSFT","GOOGL","AMZN","NVDA","META","TSLA","BRK-B","JPM","V",
  "JNJ","UNH","WMT","MA","PG","HD","XOM","CVX","LLY","ABBV",
  "PFE","KO","PEP","MRK","COST","AVGO","AMD","ORCL","CRM","NFLX",
  "ADBE","CSCO","ACN","TXN","INTC","QCOM","INTU","AMAT","BKNG","ISRG",
  "MDLZ","ADP","REGN","VRTX","GILD","PANW","LRCX","MU","KLAC","SNPS",
]

KR_STOCKS = {  # 코드: 이름 (섹터는 아래 어휘로 네가 매핑)
  "005930":"Samsung Electronics","000660":"SK Hynix","373220":"LG Energy Solution",
  "005380":"Hyundai Motor","000270":"Kia","207940":"Samsung Biologics",
  "006400":"Samsung SDI","035420":"NAVER","035720":"Kakao","005490":"POSCO Holdings",
  "068270":"Celltrion","028260":"Samsung C&T","105560":"KB Financial","055550":"Shinhan Financial",
  "012330":"Hyundai Mobis","066570":"LG Electronics","003670":"POSCO Future M","051910":"LG Chem",
  "096770":"SK Innovation","034730":"SK","000810":"Samsung Fire","003550":"LG",
  "032830":"Samsung Life","009150":"Samsung Electro","086790":"Hana Financial","010130":"Korea Zinc",
  "033780":"KT&G","011200":"HMM","247540":"Ecopro BM","377300":"Kakao Pay",
  "030200":"KT","017670":"SK Telecom","018260":"Samsung SDS","036570":"NCsoft",
  "316140":"Woori Financial","003490":"Korea Shipbuilding","034020":"Doosan Enerbility",
  "011170":"Lotte Chemical","024110":"Industrial Bank of Korea","010950":"S-Oil",
  "006800":"Mirae Asset Securities","004020":"Hyundai Steel","000720":"Hyundai E&C",
  "002790":"Amore Pacific","138040":"Meritz Financial","259960":"Krafton",
  "326030":"SK Biopharm","323410":"Kakao Bank","361610":"SK IE Technology","352820":"Hive",
}
KR 섹터 어휘: Semiconductor, Battery, Auto, Auto Parts, Bio, Internet, Steel, Holding, Financial,
Insurance, Securities, Fintech, Electronics, Materials, Chemical, Energy, Telecom, IT Services,
Gaming, Shipbuilding, Industrial, Shipping, Construction, Cosmetics, Entertainment, Tobacco,
Non-Ferrous, Components

INDICATORS = {  # 심볼: (이름, 단위)
  "CL=F": ("WTI Oil","$"), "GC=F": ("Gold","$"), "SI=F": ("Silver","$"), "HG=F": ("Copper","$"),
  "EURUSD=X": ("EUR/USD",""), "KRW=X": ("USD/KRW","W"), "JPY=X": ("USD/JPY",""),
  "CNY=X": ("USD/CNY",""), "^TNX": ("US 10Y","%"), "BTC-USD": ("Bitcoin","$"), "ETH-USD": ("Ethereum","$"),
}

SECTOR_INDICATOR_MAP = {  # 종목 섹터 -> 관련 지표 (Detail의 관련 지표 매핑)
  "Technology": ["^IXIC","BTC-USD"], "Semiconductor": ["^IXIC","BTC-USD"],
  "Internet": ["^IXIC","BTC-USD"], "Communication": ["^IXIC","BTC-USD"],
  "Energy": ["CL=F","HG=F"], "Chemical": ["CL=F","HG=F"], "Materials": ["CL=F","HG=F"],
  "Financial": ["^TNX","EURUSD=X"], "Insurance": ["^TNX","EURUSD=X"], "Fintech": ["^TNX","BTC-USD"],
  "Consumer": ["GC=F","EURUSD=X"], "Auto": ["KRW=X","CL=F"], "Healthcare": ["GC=F","^TNX"],
  "Bio": ["GC=F","^TNX"], "Battery": ["HG=F","SI=F"], "Steel": ["HG=F","CNY=X"],
  "Telecom": ["^TNX","KRW=X"], "Holding": ["^TNX","KRW=X"], "Electronics": ["^IXIC","KRW=X"],
}

NEWS_FEEDS = {
  "yahoo": "https://finance.yahoo.com/news/rssindex",
  "yahoo_markets": "https://feeds.finance.yahoo.com/rss/2.0/headline?s=^GSPC&region=US&lang=en-US",
  "hankyung": "https://www.hankyung.com/feed/economy",
  "mk": "https://www.mk.co.kr/rss/30100041/",
}
자동화 옵션, 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분

FastAPI가 overview / quotes / 종목 상세 / 차트를 캐시 뒤에서 돌려주면 이 Phase는 끝입니다.

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

Claude 세션 입력
/superpowers:execute-plan Phase 1만 구현하고 멈춰줘. backend venv + requirements, config.py(제공한 실데이터, US/KR 각 20개 활성), services(yfinance 벌크 + pykrx, 개별 실패 스킵), 인메모리 TTL 캐시 + single-flight, 라우트 4종(overview, quotes, stocks/{symbol}, chart?range=). uvicorn 실행과 curl 검증 방법까지.
Terminal, 실행과 스모크
cd backend && .venv/bin/uvicorn app.main:app --port 8000 &
sleep 3
curl -s localhost:8000/api/market/overview | head -c 300; echo
curl -s "localhost:8000/api/stocks/AAPL/chart?range=1m" | head -c 200; echo
개념 짚기 (Level 200), 캐시와 single-flight TTL 캐시는 "45초 안에는 같은 답을 다시 묻지 않는다"는 약속이고, single-flight는 "같은 질문이 동시에 40개 와도 업스트림엔 한 번만 간다"는 잠금입니다. 워크샵장에서 전원이 동시에 새로고침해도 yfinance가 평온한 이유가 이 두 줄에 있습니다. 원작은 여기에 DynamoDB L2를 얹어 컨테이너가 재시작돼도 캐시가 살아남게 합니다.
막힐 때 열어보기, single-flight 캐시 핵심 힌트
cache.py 골격 (참고 조각)
import time, threading
_store, _locks = {}, {}
def get_or_fetch(key: str, ttl: int, fetch):
    now = time.time()
    hit = _store.get(key)
    if hit and now - hit[0] < ttl:
        return hit[1]
    lock = _locks.setdefault(key, threading.Lock())
    with lock:                       # 같은 키 동시 요청은 한 번만
        hit = _store.get(key)
        if hit and time.time() - hit[0] < ttl:
            return hit[1]
        val = fetch()
        _store[key] = (time.time(), val)
        return val
DEFINITION OF DONE
MISSION 03

Phase 2 + 3, 대시보드와 상세

45분

React 대시보드가 45초마다 숨 쉬고, 종목을 클릭하면 캔들 차트가 있는 상세 화면이 열립니다.

이 미션의 바탕 학습: Ch2의 병렬 감각. 프런트를 빌드하는 동안 백엔드 응답 필드 보강을 서브에이전트로 병행할 수 있습니다.

Claude 세션 입력, Phase 2
/superpowers:execute-plan Phase 2만 구현해줘. frontend(Vite React TS): 요구 5의 Dashboard(지표 바, 지수 카드 5장 등락색, US/KR 탭 종목 테이블), react-query 45초 폴링, dev 프록시(:5173 → :8000). 실행 방법을 알려줘.
Terminal, 미리보기
cd frontend && npm run dev   # 브라우저에서 /proxy/5173/
Claude 세션 입력, Phase 3
/superpowers:execute-plan Phase 3(StockDetail)을 구현해줘. 테이블 클릭 → react-router 진입: StockHeader(현재가/등락률/거래량), lightweight-charts 캔들(1W/1M/3M/1Y 기간 탭), ReturnsRow(기간 수익률 4종), Week52Bar(52주 범위 내 위치), 뒤로 가기 복귀까지.
개념 짚기 (Level 200), 폴링 vs 스트리밍 폴링(react-query refetchInterval)은 완결된 스냅샷을 주기적으로 당깁니다, 시세처럼 "최신 한 장"이면 충분할 때. SSE는 서버가 부분 결과를 밀어 줍니다, AI 답변처럼 "생성되는 과정"이 가치일 때. WebSocket은 양방향이 필요할 때만 꺼내는 무거운 도구입니다. 이 앱이 폴링과 SSE를 병용하고 WebSocket을 쓰지 않는 것 자체가 하나의 설계 결정입니다.
이 미션의 3대 함정 ① 프론트에서 API가 404 = dev 프록시 미설정, vite.config의 proxy로 /api를 :8000에 물리세요. ② 차트가 빈 화면 = OHLC 시간 포맷 불일치, lightweight-charts는 time을 유닉스 초 또는 "YYYY-MM-DD"로 받습니다. ③ KR 종목이 0원 = 장 마감 시간대의 정상 동작, 백엔드도 600초 절전 주기로 도는 중입니다.
DEFINITION OF DONE
MISSION 04

Phase 4, AI 스트리밍과 개장

30분

분석 버튼을 누르면 Bedrock의 답이 한 글자씩 흘러드는 것, 이 캡스톤의 하이라이트입니다.

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

Claude 세션 입력
/superpowers:execute-plan Phase 4(AI SSE)를 구현해줘. POST /api/ai/stocks/{symbol}: Bedrock converse_stream(global.anthropic.claude-sonnet-4-6, maxTokens 1024, 리전 ap-northeast-2, 자격 인자 없이 환경변수 AWS_BEARER_TOKEN_BEDROCK, IAM 정책 금지)으로 기술적 분석/투자 포인트/리스크 3절을 text/event-stream으로: 이벤트 phase → delta* → final. 프론트 AIPanel은 fetch 직접 + 델타 누적 타자기 렌더 + phase 배지. 키 없으면 503과 패널 비활성. 마지막으로 npm run build 산출물을 backend/static으로 넣어 :8000 통합 서빙(make run 또는 스크립트)까지.
Terminal, 키 로드 후 통합 실행
set -a; source ~/capstone/.env; set +a   # Bearer 키 로드
make run    # 프론트 빌드 → :8000 통합 서빙, 브라우저에서 /proxy/8000/
개념 짚기 (Level 200), SSE 와이어 포맷 스트리밍이 도는지 눈으로 확인하고 싶다면 curl로 원문을 보세요: curl -N -X POST localhost:8000/api/ai/stocks/AAPL. event: phase 한 줄, 이어서 event: deltadata: {"text": "..."}가 반복되다가 event: final로 닫힙니다. 프런트의 타자기는 이 delta들을 이어 붙인 것뿐입니다, 마법이 아니라 계약입니다.
스트리밍이 침묵하거나 인증 에러라면 ① uvicorn을 띄운 셸에 키가 로드됐는지(위 로드 라인 후 재실행) ② 503이 즉시 오면 키 미로드/오탈자, UnrecognizedClient는 만료 ③ 델타가 한 번에 몰려서 오면 프록시 버퍼링, SSE 응답에 X-Accel-Buffering: no 헤더와 미디어타입 text/event-stream을 확인하세요.

대시보드 → 종목 상세 → 분석 스트리밍까지 한 바퀴 돌리고, 흐르는 화면의 스크린샷을 Slack에 공유하세요. 그게 개장입니다. 원작은 여기에 티어드 캐시(DynamoDB L2)와 ECS Fargate 배포까지 얹혀 운영 중입니다, 다음 단계가 궁금하다면 그 방향입니다. 그리고 Ch5의 헤드리스 패턴으로 /api/health 스모크를 파이프라인화하면 운영의 첫 발입니다.

DEFINITION OF DONE
BONUS

데스크 퀄리티 강화 라운드, 기능을 올려라

+α 자유

핵심 구성이 돌았다면 이제 데스크를 내 것으로 만드는 시간입니다. 원작에 실존하는 기능들을 강화 카드로 준비했습니다, 끌리는 것을 골라 하나씩 주문하세요. 요령은 같습니다: 한 번에 하나, 화면으로 확인하고, 마음에 들면 커밋.

A강화 카드 1, 호가창
Claude 세션 입력
StockDetail에 호가창(OrderBook)을 추가하자. GET /api/stocks/{symbol}/orderbook:
현재가를 중심으로 매도 10단계(파란 막대, 왼쪽으로) / 매수 10단계(빨간 막대, 오른쪽으로),
공개 실데이터가 없으니 random.seed(int(price * 100))로 가격에 고정된 그럴듯한 시뮬레이션.
프론트는 3열(매도잔량 | 가격 | 매수잔량) 레이아웃, 45초 폴링에 동참.
B강화 카드 2, 투자자 동향
Claude 세션 입력
StockDetail에 투자자 동향(InvestorPanel)을 추가하자. GET /api/stocks/{symbol}/investors:
개인/외국인/기관의 최근 10일 순매수 추정치 표. 실데이터가 없으니 일별 거래량과 캔들 방향에서
개인 60 / 외국인 30 / 기관 10 비율로 추정하고 개인은 역방향 가정(원작 방식).
순매수 초록 / 순매도 빨강, 날짜 내림차순.
C강화 카드 3, 종목 뉴스와 기사 분석
Claude 세션 입력
종목 뉴스(StockNews)와 기사 AI 분석 화면(ArticleAnalysis)을 추가하자.
1) GET /api/stocks/{symbol}/news: Yahoo Finance RSS에서 해당 심볼 뉴스 5건(제목, 링크, 시각)
2) 뉴스 클릭 → /article 라우트: httpx로 본문 추출(외부 URL만 허용하는 SSRF 가드) 후
   POST /api/ai/articles가 SSE로 요약/시장 영향/투자 인사이트를 스트리밍(기존 delta 계약 재사용)
3) 영어 기사는 한국어 번역을 맨 앞에.
D강화 카드 4, 라이트/다크 테마
Claude 세션 입력
라이트/다크 듀얼 테마를 추가하자. 기본은 prefers-color-scheme, 헤더 토글로 전환,
선택은 localStorage에 기억. 색은 전부 CSS 변수로 빼서 두 테마 모두에서
등락색(초록/빨강)과 차트 캔들의 대비가 유지되게. lightweight-charts의 배경/그리드 색도 테마를 따라가게.
E강화 카드 5, 나만의 데스크로

여기부터는 정해진 카드가 없습니다. 당신이 프로덕트 오너입니다. 시황을 보며 아쉬웠던 것 하나를 골라, 위 카드들처럼 직접 요구사항을 써서 주문하세요. 아이디어가 필요하면: 관심종목 즐겨찾기, 등락률 알림 하이라이트, 섹터 등락 바, 시장 요약(Top 상승/하락), 지표 스파크라인, 뉴스 키워드 필터.

Claude 세션 입력, 빈칸을 내 아이디어로
내 데스크에 [내가 원하는 기능]을 추가하자. 규칙:
1) API 계약: [필요한 라우트와 응답 형태 한두 문장]
2) 화면: [어디에 어떻게 보이는지, 갱신 주기]
3) 데이터: [출처, 실데이터가 없으면 시뮬레이션 방식]
4) 기존 캐시/폴링/스트리밍 계약은 깨지 않는다
구현 전에 이 규칙을 네 계획으로 다시 말해주고, 승인하면 진행해.
강화 라운드의 요령 ① 카드 하나 = 커밋 하나, 되돌아갈 지점을 만들며 갑니다. ② 화면으로 확인하고 다른 점을 말로 다듬으세요. ③ 마지막엔 make run으로 통합 서빙을 다시 굳혀 Slack의 스크린샷을 최신으로.
BONUS DONE, 진행률 미집계
BONUS

바이브 코딩 라운드, 원작을 향해

+α 자유

Phase 빌드가 끝났다면 이제 바이브 코딩입니다: 완성 화면의 묘사를 주고 "이렇게 나오게 해줘"를 반복하는 루프. 아래 세 장면은 원작이 실제로 도달한 모습을 글로 옮긴 것입니다. 내 화면과 다른 점을 찾아 하나씩 주문하세요.

1장면 A, Dashboard의 마감 디테일
타깃 묘사, 프롬프트에 붙여 쓰세요
- 상단 경제지표 바: 지표들이 가로로 흐르고 등락은 색과 ▲▼로
- 지수 카드 5장: 값 크게, 등락률 작게, 카드 보더나 포인트 컬러가 등락을 따른다
- 종목 테이블: US/KR 탭, 숫자 전부 우측 정렬 모노 감각, 행 호버 하이라이트
- footer 또는 헤더에 "마지막 갱신 시각"과 다음 갱신까지의 감각
2장면 B, StockDetail의 마감 디테일
타깃 묘사, 프롬프트에 붙여 쓰세요
- StockHeader: 심볼과 이름, 현재가 크게, 등락은 색+▲▼, 거래량은 축약(1.2M)
- 캔들 차트: 기간 탭(1W/1M/3M/1Y) 중 활성 탭 하이라이트, 상승 초록/하락 빨강 캔들
- ReturnsRow: 1W/1M/3M/1Y 수익률이 한 줄에, 양수 초록/음수 빨강
- Week52Bar: 52주 저가~고가 바 위에 현재가 마커
- 확장 후보(원작에 있는 것): 호가창 OrderBook, 투자자 동향 InvestorPanel, 종목 뉴스 StockNews,
  기사 AI 분석 화면 ArticleAnalysis - 끌리는 것 하나를 같은 리듬으로 추가
3장면 C, AI 패널의 마감 디테일
타깃 묘사, 프롬프트에 붙여 쓰세요
- 분석 버튼을 누르면 phase 배지가 fetching → analyzing 으로 바뀌고
- 본문은 delta가 도착하는 대로 타자기처럼 자라며, 마크다운(## 절, 굵은 키워드)이 살아 있다
- 스트리밍 중 다시 누르면 진행 중인 요청을 취소하고 새로 시작
- 키가 없을 때는 "AI 분석 비활성" 안내만 조용히
바이브 코딩의 요령 ① 한 번에 한 화면, 한 번에 한 디테일. ② 실행해 보고 다른 점을 말로 알려주는 반복이 핵심입니다, "델타가 뭉쳐서 온다"처럼. ③ 마음에 든 순간마다 커밋해 되돌아갈 지점을 만드세요. 스펙은 방향을 잡고, 바이브는 마감을 만듭니다.
BONUS DONE, 진행률 미집계
OPTION

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

+15분 옵션

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

이 미션의 바탕 학습: Ch4의 훅과 스킬을 팀 자산으로 승격하는 감각, 그리고 Reference 3의 플러그인 생태계.

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 .

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

Claude 세션 입력
/project-init:sync-docs
출력 예시 (요약)
## Sync Report
### Quality Scores (Before -> After)
| ./CLAUDE.md           | D (48) | B (82) | +34 |
| ./backend/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}}

이 캡스톤의 비자명한 지식은 스트리밍 계약입니다. "왜 AI만 fetch를 직접 쓰는가, 왜 장 마감엔 600초로 늦추는가"를 CLAUDE.md의 패턴으로 기록해 두면 sync-docs 점수가 오르고, 다음에 이 저장소를 여는 Claude가 같은 원칙으로 확장합니다. 여유가 있다면 결정을 기록으로 남기세요:

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

미션 종료

시세는 캐시 뒤에, 키는 서버에, AI는 스트림으로. 웹 시황 데스크의 문법입니다.

가져가는 것내용
캐시가 곧 예의업스트림은 캐시 뒤에서 한 번만, single-flight로 동시 요청도 한 번만
스트리밍 계약phase → delta → final, 그리고 "점진 델타에는 react-query가 아니라 fetch"라는 도구 선택의 근거
절전 설계장이 닫히면 600초로 늦춘다, 시스템은 세상의 리듬을 알아야 한다
NEXT

포털로

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