⌂ 목차 ← 이전 다음 →원본 페이지

Mission 2. Market Desk on Web

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)

Step L · 학습 목표, Level 200

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

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

Step 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 설계가 이 구조로 수렴하도록 방향을 잡아 주고, 계획이 이상하게 흐르면 여기로 돌아와 대조하세요.

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

Step 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는 바이브 코딩 라운드의 확장 과제로 남깁니다.

Step 2 · API 계약 (원작 라우트 실측)

라우트역할
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도 제공)

Step 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으로 매핑, 앱은 정상

Step 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 · 데스크 퀄리티 강화 라운드, 기능을 올려라 (+α 자유)

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

Step A · 강화 카드 1, 호가창

Claude 세션 입력

StockDetail에 호가창(OrderBook)을 추가하자. GET /api/stocks/{symbol}/orderbook:
현재가를 중심으로 매도 10단계(파란 막대, 왼쪽으로) / 매수 10단계(빨간 막대, 오른쪽으로),
공개 실데이터가 없으니 random.seed(int(price * 100))로 가격에 고정된 그럴듯한 시뮬레이션.
프론트는 3열(매도잔량 | 가격 | 매수잔량) 레이아웃, 45초 폴링에 동참.

Step B · 강화 카드 2, 투자자 동향

Claude 세션 입력

StockDetail에 투자자 동향(InvestorPanel)을 추가하자. GET /api/stocks/{symbol}/investors:
개인/외국인/기관의 최근 10일 순매수 추정치 표. 실데이터가 없으니 일별 거래량과 캔들 방향에서
개인 60 / 외국인 30 / 기관 10 비율로 추정하고 개인은 역방향 가정(원작 방식).
순매수 초록 / 순매도 빨강, 날짜 내림차순.

Step 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) 영어 기사는 한국어 번역을 맨 앞에.

Step D · 강화 카드 4, 라이트/다크 테마

Claude 세션 입력

라이트/다크 듀얼 테마를 추가하자. 기본은 prefers-color-scheme, 헤더 토글로 전환,
선택은 localStorage에 기억. 색은 전부 CSS 변수로 빼서 두 테마 모두에서
등락색(초록/빨강)과 차트 캔들의 대비가 유지되게. lightweight-charts의 배경/그리드 색도 테마를 따라가게.

Step E · 강화 카드 5, 나만의 데스크로

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

Claude 세션 입력, 빈칸을 내 아이디어로

내 데스크에 [내가 원하는 기능]을 추가하자. 규칙:
1) API 계약: [필요한 라우트와 응답 형태 한두 문장]
2) 화면: [어디에 어떻게 보이는지, 갱신 주기]
3) 데이터: [출처, 실데이터가 없으면 시뮬레이션 방식]
4) 기존 캐시/폴링/스트리밍 계약은 깨지 않는다
구현 전에 이 규칙을 네 계획으로 다시 말해주고, 승인하면 진행해.

강화 라운드의 요령

① 카드 하나 = 커밋 하나, 되돌아갈 지점을 만들며 갑니다. ② 화면으로 확인하고 다른 점을 말로 다듬으세요. ③ 마지막엔 make run으로 통합 서빙을 다시 굳혀 Slack의 스크린샷을 최신으로.

BONUS DONE, 진행률 미집계

BONUS · 바이브 코딩 라운드, 원작을 향해 (+α 자유)

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

Step 1 · 장면 A, Dashboard의 마감 디테일

타깃 묘사, 프롬프트에 붙여 쓰세요

- 상단 경제지표 바: 지표들이 가로로 흐르고 등락은 색과 ▲▼로
- 지수 카드 5장: 값 크게, 등락률 작게, 카드 보더나 포인트 컬러가 등락을 따른다
- 종목 테이블: US/KR 탭, 숫자 전부 우측 정렬 모노 감각, 행 호버 하이라이트
- footer 또는 헤더에 "마지막 갱신 시각"과 다음 갱신까지의 감각

Step 2 · 장면 B, StockDetail의 마감 디테일

타깃 묘사, 프롬프트에 붙여 쓰세요

- StockHeader: 심볼과 이름, 현재가 크게, 등락은 색+▲▼, 거래량은 축약(1.2M)
- 캔들 차트: 기간 탭(1W/1M/3M/1Y) 중 활성 탭 하이라이트, 상승 초록/하락 빨강 캔들
- ReturnsRow: 1W/1M/3M/1Y 수익률이 한 줄에, 양수 초록/음수 빨강
- Week52Bar: 52주 저가~고가 바 위에 현재가 마커
- 확장 후보(원작에 있는 것): 호가창 OrderBook, 투자자 동향 InvestorPanel, 종목 뉴스 StockNews,
  기사 AI 분석 화면 ArticleAnalysis - 끌리는 것 하나를 같은 리듬으로 추가

Step 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의 플러그인 생태계.

Step 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 때와 같은 규칙입니다.

Step 2 · 성숙화, /project-initUnknown directive
Line 634: Unknown text directive “init-project”
와 /project-initUnknown directive
Line 634: Unknown text directive “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

Step 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-evalUnknown directive

Line 692: Unknown text directive “quick”
(30초 체크리스트)만으로 충분합니다, /project-initUnknown directive
Line 693: Unknown text directive “init-project”
는 17단계라 수 분이 걸립니다. /harness-evalUnknown directive
Line 693: Unknown text directive “standard”
의 동적 분석은 대상 프로젝트의 훅과 테스트를 실제로 실행합니다. 방금 본인이 만든 프로젝트라 안전하지만, 남의 저장소를 평가할 때는 --static-only 플래그가 예의이자 안전입니다.

OPTION DONE, 진행률 미집계

미션 종료

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

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

NEXT

포털로

다른 캡스톤(Mission 1. Clawd Jump, Mission 3. Generative Hours)도 같은 리듬입니다. 워크샵 포털에서 다음 미션을 고르세요.

이전 / 다음

Mission 1. Clawd Jump · Mission 3. Generative Hours


원문: Claude Code Deep Dive Workshop · Market Desk on Web, 웹 시황 대시보드  · 기준: 2026.07