주식 대시보드 소개
2주차엔 1주차에 쌓은 노원구 실거래 DB로 "남한테 보여줘도 되는 한 장짜리 대시보드"를 만들었어요. 그때 제일 마음에 들었던 게 "HTML은 껍데기, 숫자는 JSON" 구조였는데, 3주차 주제가 마침 "부동산 말고 이번엔 주식으로 같은 걸 해보라" 여서, "그럼 2주차에 만든 그 틀을, 종목만 갈아끼우면 진짜로 재사용되나?"를 확인하고 싶었습니다.
그래서 이번 목표는 두 겹으로 잡았어요.
(재사용 시험) 2주차 부동산 파이프라인을 다른 지역(고양시 일산동구) 에 그대로 돌려서, "입력만 바꾸면 대시보드가 나온다"는 강의 말이 진짜인지 먼저 찍어보기.
(3주차 본과제) 핸드아웃대로 한국·미국 주식·ETF 공유현황판 만들기 — 수익률·변동성을 한 화면에서 비교하는 현황판.
강의에서 강사님이 3주차 내내 못 박은 문장이 있었어요 — "대시보드는 주식 API를 직접 호출하지 말고, 검증된 JSON을 읽어라." 그리고 "수익률과 순위는 투자 권유가 아니라 비교·탐색용 지표다." 이 두 개가 이번 과제의 가드레일이었습니다. (실습에서 매수·매도·보유 판단은 아예 제외.)
진행 방법
쓴 도구는 이렇게 됩니다.
Claude Code (실습 파트너 겸 코드 작성)
uv가상환경 + DuckDB(분석용 컬럼 DB)FinanceDataReader (주식·ETF 가격 수집 — API 키 불필요)
ECharts (오픈소스 JS 차트) — 이번엔 CDN 대신
./vendor/echarts.min.js로 로컬에 박아 재사용Playwright (완성 화면 렌더 검증·풀페이지 캡처)
강사님이 준 재사용 핸드아웃 프롬프트 (입력확인 → 지표+JSON → ECharts 현황판 → 최종검증, 4단계)
재사용 시험: 일산동구 데이터 활용
주식으로 넘어가기 전에, 2주차 방식이 진짜 재사용되는지부터 봤어요. "고양시 일산동구 거래 분석 데이터 만들어줄 수 있어?" 한 줄로 시작했는데, 노원구 때 쓴 수집 스크립트가 노원구(11350) 전용이라 일산동구(41285)용으로 스크립트만 새로 찍고, DB는 노원구를 안 건드리게 data/ilsan.duckdb로 분리했습니다. 국토부 실거래가로 매매 3,014건 / 전세 5,709건(2025-01~2026-06)을 수집했고, 결 과는 2주차와 똑같은 골격 — KPI 6장 + 차트 7개 + QA표였어요.
KPI: 수도권 매매지수 YoY +6.74%, 전세지수 YoY +5.61%, 전세가율 62.54%, 일산동 매매거래량 200건(2026-06), 매매 중위가 5.24억, 전세 중위 보증금 3.6억
노원구(2주차 6.4억)보다 일산동 매매 중위가(5.24억)가 낮게 나왔고, KB 통계가 시/도·권역 단위라 일산동은 "서울"이 아니라 "수도권"을 대표 권역으로 잡아야 했습니다(2주차에서 몸으로 배운 그 제약).
📸 캡처 자리 ① — 일산동구 정적 대시보드 상단(
ilsan-dashboard-preview.png)
"입력만 바꾸면 된다"가 6분 만에 실증되니, 주식으로 넘어갈 용기가 생겼어요.
핸드아웃 데이터 확인
핸드아웃 파일 경로를 넘기고(03강-handout (1).md), 첫 프롬프트대로 "뭘 입력으로 쓸지" 부터 확인했습니다. 그런데 여기서 바로 벽을 만났어요. 핸드아웃은 입력 3안(①1주차 DuckDB raw_stock_price ②공통 CSV ③공통 샘플 JSON)을 줬는데:
Open optionsauto
=== raw_stock_price 티커 수 ===
[(2, 724)] # 티커 2종·724행뿐 (8개 미만)
['005930', '069500'] # 삼성전자, KODEX200
=== 공통 주식 CSV 존재? ===
(sample-data에도, Downloads에도 없음)
DB엔 티커가 2개뿐이고, 공통 CSV는 아예 없었습니다. 3안이 다 막힌 거죠. 그래서 핸드아웃 §8 "개별 확장 실습(universe 확대)" 경로로 틀어서, FinanceDataReader로 12종을 새로 수집하기로 했습니다(한국 8 + 미국 4). 여기서 강사 숙제였던 "각자 목표(ETF 포트폴리오 등)를 정해오라"가 떠올라 ETF 포트폴리오도 고민했는데, 이번엔 핸드아웃 기본 구성(균형 혼합: 한국 8 + 미국 4, 주식·ETF 섞기) 으로 갔어요. 재사용 틀을 검증하는 게 이번 목적이라, 종목 구성은 표준을 따르는 게 맞다 싶었습니다. (ETF 포트폴리오는 다음 숙제로 남겨뒀어요.)
지표 계산 및 JSON 생성
핸드아웃의 지표·JSON 프롬프트를 그대로 썼습니다. 핵심은 "HTML이 읽을 JSON을, 계산이 검증된 상태로 먼저 만든다" 예요.
Open optionsauto
방금 확인한 주식·ETF 가격 데이터로 분석 지표와 dashboard JSON을 만들어줘.
## 2. 원천 가격 정리
- 컬럼을 market, asset_type, ticker, name, sector, currency, date, close, volume, source로 매핑해.
- ticker는 문자열로 유지해. (005930의 앞자리 0 유지)
- ticker·date 중복 제거, 날짜 오름차순 정렬. close 없거나 0 이하면 제외.
- 공통 테이블명은 market_prices와 asset_metrics야.
## 3. 지표 계산 (ticker별)
- current_price: 가장 최근 close
- return_1m_pct, return_3m_pct, return_6m_pct: 기준일 이전 가장 가까운 close와 비교
- volatility_60d_pct: 최근 60개 일간 수익률 표준편차 x sqrt(252) x 100
- avg_volume_20d: 최근 20 거래일 volume 평균
- 과거 기준 가격이 없으면 해당 수익률은 null (임의로 채우지 마)
## 4. dashboard JSON
- schemaVersion: study23_stock_dashboard.v1
- snapshotDate: 입력에서 실제로 확인한 최대 날짜
- summary: assetCount, medianReturn3mPct, medianReturn6mPct, medianVolatility60dPct
- items: market, assetType, ticker, name, sector, currency, currentPrice,
return1mPct/3m/6m, volatility60dPct, avgVolume20d
- JSON에 NaN, Infinity, -Infinity 쓰지 마. 계산 불가값은 null.
- 추천·매수·매도·보유 권유 문구를 넣지 마.
## 5. 검증
- market_prices ticker 수 = asset_metrics 행 수 = JSON items 수가 같은지 확인.
005930의 앞자리 0을 문자열로 지키는 것, 계산 불가값을 억지로 안 채우고 null로 두는 것 — 이 두 줄이 나중에 화면 신뢰도를 좌우하더라고요. 수집·지표계산·JSON 생성을 stock_dashboard_pipeline.py 한 파일로 묶어 한 방에 돌렸고, 결과 JSON은 이런 모양(자산 하나가 딱 이 한 덩어리)입니다.
Open optionsjson
{
"schemaVersion": "study23_stock_dashboard.v1",
"snapshotDate": "2026-06-30",
"summary": { "assetCount": 12, "medianReturn3mPct": 14.82,
"medianReturn6mPct": 7.54, "medianVolatility60dPct": 51.45 },
"items": [
{ "market": "KR", "assetType": "stock", "ticker": "000660",
"name": "SK하이닉스", "sector": "반도체", "currency": "KRW",
"currentPrice": 2650000.0,
"return1mPct": 13.59, "return3mPct": 203.55, "return6mPct": 307.07,
"volatility60dPct": 93.49, "avgVolume20d": 5748705.3 }
],
"note": "데이터 시각화·비교용 지표이며 투자 추천이 아님. 수집: FinanceDataReader."
}
ECharts로 현황판 조합
그다음 핸드아웃의 현황판 프롬프트로 화면을 붙였어요. 화면은 주식 API를 직접 안 부르고, 방금 만든 JSON만 fetch 합니다.
Open optionsauto
방금 만든 dashboard JSON을 읽는 주식·ETF 공유현황판을 ECharts로 만들어줘.
- KPI 4개: assetCount, medianReturn3mPct, medianReturn6mPct, medianVolatility60dPct
- 산점도: x=return3mPct, y=volatility60dPct, 점 크기=avgVolume20d의 log, 색=market
- 막대: 자산별 return3mPct 오름차순
- 표: name·ticker·market·assetType·currentPrice·currency·1·3·6M·변동성
- 필터(시장 KR/US, 유형 stock/etf)를 바꾸면 KPI·산점도·막대·표가 같이 바뀌게.
- null은 '-'로, NaN·Infinity ·undefined는 화면에 안 보이게. 차트 div에 명시 높이·resize.
- 수익률 상승·하락 색은 한국 사용자가 자연스럽게(상승=빨강/하락=파랑).
- 추천·매수·매도·보유 권유 문구 금지.
완성 화면엔 KPI 4장 + 산점도(수익률×변동성, 점 크기=거래량) + 막대(3개월 수익률 오름차순) + 12종 표가 들어갔고, 위에 시장(전체/한국/미국)·유형(전체/주식/ETF) 필터를 달았습니다.
📸 캡처 자리 ② — 주식·ETF 공유현황판 전체(
stock-dashboard-preview.png)
📸 캡처 자리 ③ — 미국 필터를 눌렀을 때 KPI·산점도·표가 4종으로 같이 바뀌는 화면
결과와 배운 점
최종적으로 주식 현황판엔 한국 8종 + 미국 4종, 총 12종이 담겼어요(스냅샷 2026-06-30).
한국: 삼성전자·SK하이닉스·현대차·NAVER·카카오 / KODEX 200·KODEX 코스닥150·TIGER 미국S&P500(ETF)
미국: Apple·NVIDIA·Microsoft / SPY(ETF)
요약 KPI: 3개월 수익률 중앙값 +14.82%, 6개월 +7.54%, 60일 변동성 중앙값 51.45%
그리고 이번 과제 최고의 배움은 여기서 나왔습니다.
1. "정합성 PASS"와 "현실성"은 완전히 다른 층이다. 검증은 2주차처럼 깔끔하게 통과했어요 — 티커 수 12 = asset_metrics 12행 = JSON items 12, 임의 종목 2개(SK하이닉스·현대차)를 골라 DB 재계산 = JSON = 화면 숫자가 정확히 일치, 미국 필터 누르면 4종·미국+ETF는 SPY 1종으로 KPI·표가 동시에 바뀜. 전부 PASS. 그런데 그 "정확한" 숫자가 SK하이닉스 3개월 +203%, 6개월 +307%, 현재가 265만원 같은 눈에 띄게 튀는 값이었어요. 파이프라인은 한 치도 안 틀렸는데, 원본 값 자체가 말이 되는지는 아무도 검산 안 한 거죠. 2주차 QA표가 "화면=원본"만 봤다면, 3주차는 "그 원본이 현실적인가"라는 한 층이 더 필요하다는 걸 배웠습니다. 실제로 Claude가 딱 한 종목을 콕 집어 이렇게 플래그하더라고요 — "현대차 1개월 -31.54%는 FDR 원본 가격 그대로입니다. 낙폭이 커 보이니 핸드아웃의 '수정주가 적용 여부' 항목 차원에서 눈으로 한 번 확인해두세요(계산 자체는 정확)." "계산은 맞고, 값은 의심하라" — 이 감각이 이번에 제일 크게 남았어요.
2. 2주차의 JSON 계약이 진짜로 재사용됐다. 부동산에서 만든 "차트 하나 = JSON 한 덩어리(제목·질문·출처·단위·기준기간·표본수)" 구조를, 주식에선 스키마 이름만 study23_stock_dashboard.v1로 갈고 그대로 썼어요. "HTML은 껍데기, 숫자는 JSON" 이 부동산→주식으로 넘어가도 그대로 통한다는 게 이번에 증명됐습니다. 심지어 그날 저녁 일산 대시보드까지 셋을 같은 틀로 찍었으니, 이제 이 골격은 제 표준이 됐어요.
3. 핸드아웃대로 안 될 때 파이프라인이 있으면 우회가 된다. 준비된 입력(DB 2종·CSV 없음)이 다 막혔는데도, 1주차 수집 코드가 있어서 FDR로 12종을 새로 긁어 30분 안에 복구했습니다. 다만 이것도 결국 "1주차에 종목을 2개만 넣어둔 대가" 였어요(2주차 노원구만 수집했던 그 실수와 판박이). 수집 단계에서 아낀 게 늘 다음 주 발목을 잡네요.
나만의 꿀팁 세 개.
ECharts는 CDN 말고
vendor/에 박아두기. 이번에echarts.min.js를 프로젝트 안에 두고 재사용했는데, 오프라인·사내망(CSP 제약)에서도 그냥 열리고, 버전이 안 흔들려서 마음이 편했어요.필터 연동 검증은 클릭이 막히면 DOM으로. Playwright
browser_click이 인자 오류로 막혔는데,browser_evaluate로 버튼을 DOM에서 직접 눌러 "미국 필터→4종" 갱신을 확인했습니다. (막히면 우회로가 늘 있더라고요.)원본 DB는 절대 안 건드리기. 일산은
ilsan.duckdb로 분리, 주식은 신규market_prices/asset_metrics테이블 —raw_stock_price원본은 손 안 댐. 재실행이 무서우면 대시보드를 자꾸 안 고치게 돼요.
도움이 필요한 부분. 위 1번과 이어지는데, 데이터 "현실성"을 파이프라인에서 자동으로 걸러내는 방법이 궁금해요. 정합성(합계·중복·null)은 코드로 쉽게 검 증되는데, "이 수익률/가격이 상식적인가"는 지금은 사람 눈에 의존합니다. 수정주가 반영 여부나 이상치(±xx% 급등락) 자동 플래그를 어떻게 넣는지, 다른 분들은 어떻게 하시는지 팁이 있으면 배우고 싶습니다.
앞으로의 계획. 다음(마지막 주)은 통합이라고 하니, 부동산 대시보드와 주식 현황판을 한 곳에 묶어볼 생각이에요. 그리고 이번에 미뤄둔 진짜 "내 ETF 포트폴리오" 각도(국내외 대표 ETF·채권·금·리츠로 자산배분 관점 비교)를 이번엔 실제로 만들어보려고요. 1주차의 온비드 공매 자동수집처럼, 매일 갱신되는 입력 + JSON 계약 대시보드를 붙이면 "살아있는 현황판"이 되겠다는 그림이 이제 확실히 보입니다.
도움 받은 자료
지피터스 스터디 23기 3주차 강의 및 핸드아웃(주식 공유현황판 만들기)
FinanceDataReader — 한국·미국 주식·ETF 가격 수집(오픈소스, API 키 불필요)
Apache ECharts 공식 예제(오픈소스 JS 차트)
DuckDB — 분석용 컬럼형 DB(읽기 전용 조회 +
market_prices/asset_metrics저장)국토교통부 아파트 매매·전월세 실거래가 API / KB부동산 월간 통계(일산 준비운동용)