CAPSTONE 5 / QUAKE WATCH / 120 MIN

지구의 맥박, 실시간 지진 워치

USGS 피드 → 멱등 이벤트 스토어 → 세계지도 SVG → AI 해설, 그리고 CloudFront 개장

지금 이 순간에도 지구는 움직입니다. 전 세계 규모 2.5 이상의 지진을 실시간 피드로 수집해 세계지도 위에 펄스로 띄우고, Bedrock이 "지난 24시간, 지구는"을 해설합니다. 키 발급이 필요 없는 공공 데이터라 시작이 빠르고, 마감은 CloudFront → ALB → Fargate 실배포입니다. 120분, 배포까지 완결합니다.

소요 시간 120분 미션 5개 + DoD 산출물 공개 URL로 열리는 내 지진 워치 Update 2026.08
MISSION BRIEFING

지구를 모니터링하는 법

숙지

빈 폴더에서 superpowers 워크플로로 설계하고 Phase 단위로 빌드합니다. 2시간 트랙이라 키 발급이 없고(USGS는 공공 피드), 마감은 로컬이 아니라 CloudFront 실배포입니다. 데이터는 실제 지구의 기록이며, 수집 주기마다 바뀝니다.

레이어구성핵심 원칙
수집USGS 2.5_day.geojson을 60초 폴링키 없음, 피드 하나가 전체 상류. 비정상 필드는 다듬고 계속
저장이벤트 id 멱등 딕셔너리(최대 500건)같은 지진은 한 번만, 신규 진입을 사이클마다 기록
화면등장방형 세계지도 SVG + 필터 + 테이블좌표 변환 두 줄이 지도의 전부, 라이브러리 없음
AI와 개장REST 직접 브리핑, CDK로 CF → ALB → FargateBearer는 Secrets로 기동 주입, 이미지에 비밀 없음

1. 워치 (메인 화면)

기능설명
세계지도진앙을 원으로: 반지름은 규모, 색은 깊이(얕음 주황 → 깊음 보라), 최근 1시간은 펄스
통계 바24h 건수, 최대 규모, 활동 최다 지역, 마지막 수집 시각
이벤트 테이블시각(KST), 규모, 지역, 깊이 - 최신순, 지도와 같은 데이터
필터규모 슬라이더(2.5~6.0)와 시간 창(6h/24h), 듀얼 테마

2. 해설과 개장

기능설명
브리핑"지난 24시간, 지구는" - 전체 흐름, 주목 이벤트 2~3개, 활동 급증 지역, 한국어
브리핑 캐시같은 10분 버킷 동안 재사용, LLM 호출은 아껴 쓴다
실배포CloudFront → prefix list SG + X-Origin-Verify → ALB → Fargate
피날레CloudFront URL을 Slack에, 누구나 열리는 내 지진 워치
L학습 목표, Level 200

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

목표검증되는 순간
이벤트 id 기반 멱등 수집을 설계하는 법M2에서 같은 지진이 폴링마다 와도 스토어가 한 건만 유지할 때
지리 좌표를 화면 좌표로 바꾸는 최소 수학M3에서 변환 두 줄로 진앙이 제자리에 찍힐 때
수치 데이터를 서사로 푸는 브리핑 프롬프트M4에서 통계 요약이 읽고 싶은 해설로 돌아올 때
컨테이너 앱을 CF → ALB → Fargate로 개장하는 법내 CloudFront URL이 다른 사람 브라우저에서 열릴 때
R챕터 연결 지도, 배운 내용이 재등장하는 곳
챕터이 캡스톤에서 재등장하는 곳
Ch1 설치부터 Headless까지M0~전체: 세션 운용, 지시 → 실행 → 검증 루프
Ch2 SubagentsM2~M3: 수집기 리뷰를 code-reviewer에게, 지도와 테이블 병행
Ch3 Admin SetupM4: Bearer 자격과 Secrets 주입이 도는 통제선의 배경
Ch4 Settings, 훅과 스킬OPT: 하네스 성숙화, 훅과 스킬의 프로젝트 자산화
Ch5 CLI Reference확장 방향: 수집 상태 점검을 헤드리스 파이프라인으로
Ch6 Agent SDK확장 방향: 큰 지진 감지 시 브리핑을 코드에서 부르는 알림 봇
규칙, 캡스톤 공통 ① 코드와 파일 구성을 직접하지 않고, Claude에게 지시해서 만듭니다 ② 각 Phase의 DoD를 통과해야 다음으로 ③ 막히면 "막힐 때 열어보기"의 힌트를 씁니다. 다른 참가자와의 의존성은 없습니다.
BLUEPRINT

구현 청사진

참고

M1 설계가 이 구조로 수렴하도록 방향을 잡아 주는 구현 지도입니다. 계획이 이상하게 흐르면 여기로 돌아와 대조하세요.

0세 개의 경로 한 눈에
수집 루프, 조회 경로, 출고 경로
[수집 루프]  60초마다
Scheduler ──> Collector: GET 2.5_day.geojson ──> 정규화(id, mag, place, time, lon/lat/depth)
                                                      │
                    Event Store <── 멱등 upsert(같은 id는 한 번만) ──+── 신규 진입 id 기록

[조회 경로]  브라우저가 당길 때
Browser ── fetch ──> FastAPI: /api/quakes?hours=&min_mag= ──> 필터 + 통계 + 목록
   ^                                                              (USGS를 직접 치지 않는다)
   +── 지도 원(반지름=규모, 색=깊이) + 테이블 ────────────────────────+

[출고 경로]  개장
docker build ──> ECR push ──> CDK: CloudFront ──> (prefix list SG + X-Origin-Verify) ALB ──> Fargate
                                                    Secrets Manager ──(기동 주입)──> AWS_BEARER_TOKEN_BEDROCK
1상류 계약 (USGS 피드 실물)
항목내용
피드 URLearthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson (규모 2.5+, 최근 24h)
이벤트 키feature.id (예: us7000abcd) - 멱등의 기준, 같은 지진은 갱신만
핵심 필드properties.mag / place / time(UTC 밀리초), geometry.coordinates = [경도, 위도, 깊이km]
좌표 변환x = (경도+180)/360 × 지도폭, y = (90-위도)/180 × 지도높이 (등장방형)
폴링 예산정적 피드라 사실상 한도 없음, 그래도 60초면 충분하고 캐시 친화적
2기술 선택 4
선택이유
이벤트 id 멱등 스토어피드는 24h 창이라 매 폴링 대부분이 재등장, 키 기반 upsert가 시계열을 오염에서 지킨다
등장방형 투영두 줄짜리 수학으로 충분히 정확한 세계지도, 라이브러리는 과잉
Bedrock REST 직접SDK 없이 Authorization 헤더 + JSON, "LLM 호출은 결국 HTTP"
CF → ALB → Fargate 개장 형식prefix list SG와 X-Origin-Verify 이중 방어를 갖춘, 공개 URL까지 가는 최단 경로
MISSION 00

사전 준비

5분

이 트랙은 외부 키 발급이 없습니다. Bedrock Bearer와 AWS 자격만 확인하면 출발입니다.

Terminal
mkdir -p ~/capstone/capstone-5 && cd ~/capstone/capstone-5
set -a; source ~/capstone/.env; set +a          # AWS_BEARER_TOKEN_BEDROCK 로드
python3 -c "import os; print('Bearer:', 'OK' if os.environ.get('AWS_BEARER_TOKEN_BEDROCK') else 'MISSING')"
aws sts get-caller-identity --query Account --output text   # 배포용 자격 확인
CHECKPOINT
MISSION 01

설계, 브레인스토밍

15분

수집/저장/지도/브리핑/배포까지 요구 9조항으로 넘깁니다.

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

Terminal (~/capstone/capstone-5)
claude
Claude 세션 입력, 전체 복사
/superpowers:brainstorming 다음 요구사항으로 "Quake Watch", 전 세계 실시간 지진 모니터를 설계하자.
1) 구조: backend/(Python 3.11+ FastAPI, app/collector 수집, app/store 스냅샷, app/api 라우트,
   app/llm 브리핑, config.py) + frontend/index.html(빌드 없는 정적 SPA 한 장). FastAPI가 정적 파일을
   함께 서빙해 :8000 하나로 완결. 컨테이너는 Dockerfile 한 장
2) 수집기: httpx로 USGS 피드 GET
   https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson 을 60초 주기 폴링.
   각 feature에서 id, properties.mag / place / time(UTC ms), geometry.coordinates[경도, 위도, 깊이km]만
   정규화. 비정상 필드는 0 또는 "unknown"으로 다듬고 계속(부분 실패 격리)
3) 스토어: 이벤트 id를 멱등 키로 하는 인메모리 딕셔너리(최대 500건, 오래된 것부터 제거).
   같은 id는 갱신만 하고 중복 삽입하지 않는다. 신규 진입 id 목록을 사이클마다 기록
4) API 계약: GET /healthz, GET /api/quakes?hours=24&min_mag=2.5 (시간 창과 규모 필터,
   최신순 목록 + 요약 통계: 건수, 최대 규모, 활동 최다 지역), POST /api/brief (아래 6)
5) 화면(텍스트 스펙): 라이트/다크 듀얼 테마(토글 + localStorage). 상단 통계 바(24h 건수, 최대 규모,
   최다 지역, 마지막 수집 시각). 중앙에 등장방형(equirectangular) 세계지도, 외부 라이브러리 없이
   인라인 SVG: 대륙 윤곽은 간이 폴리라인이면 충분, 진앙은 원으로 - 반지름은 규모에 비례(mag*2.2px),
   색은 깊이(얕음 주황 → 깊음 보라), 최근 1시간 이벤트는 펄스 애니메이션. 좌표 변환은
   x=(경도+180)/360*폭, y=(90-위도)/180*높이. 하단에 최근 이벤트 테이블(시각 KST 변환, 규모, 지역, 깊이)과
   규모 필터 슬라이더(2.5~6.0)
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), maxTokens 700.
   입력은 최근 24h 요약 통계와 상위 이벤트 목록, 출력은 한국어 "지난 24시간, 지구는" 브리핑:
   전체 흐름 한 문단, 주목 이벤트 2~3개 해설, 활동 급증 지역. 같은 10분 버킷 동안 결과 캐시
7) 키의 거처: AWS_BEARER_TOKEN_BEDROCK은 환경변수로만. USGS는 키가 없다, 화면은 우리 API만 본다
8) 배포: CDK로 CloudFront → (prefix list SG + X-Origin-Verify) ALB → ECS Fargate 를 구성해 배포한다. Secrets Manager가 Bearer를 기동 시 주입, 이미지에 비밀 없음
9) 구현 순서: Phase 1(수집기 + 스토어 + /api/quakes, curl 검증) → Phase 2(지도 SVG + 테이블 + 필터)
   → Phase 3(브리핑 + 캐시) → Phase 4(cdk deploy와 CloudFront 개장). 시간 제약 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, 수집기와 스토어

30분

피드가 60초마다 돌고, 이벤트가 id 멱등으로 쌓이고, /api/quakes가 필터와 통계를 얹어 주면 끝입니다.

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

2시간의 열쇠, 인프라 선배포 cdk deploy는 첫 회 12~15분이 걸립니다. Phase 1을 시작하자마자 별도 터미널에서 인프라 배포를 먼저 걸어 두세요(앱 이미지는 나중에 갱신). 빌드와 배포 대기가 겹치면 M4는 이미지 푸시와 서비스 갱신만 남아 10분 안에 개장합니다.
Terminal 2, 인프라 선배포 (백그라운드)
cd ~/capstone/capstone-5 && npx aws-cdk@2 deploy --require-approval never   # Claude가 infra를 만든 직후 실행
Claude 세션 입력
/superpowers:execute-plan Phase 1만 구현하고 멈춰줘. backend venv + requirements(fastapi, uvicorn, httpx, apscheduler), USGS 수집기(60초, 정규화, 부분 실패 격리), 이벤트 id 멱등 스토어(최대 500), /api/quakes(hours, min_mag 필터 + 통계)와 /healthz. Dockerfile과 infra(CDK: CloudFront + prefix list SG + X-Origin-Verify + ALB + Fargate + Secrets)까지 골격 생성. uvicorn 실행과 curl 검증 방법 포함.
Terminal, 스모크
cd backend && .venv/bin/uvicorn app.main:app --port 8000 &
sleep 5
curl -s "localhost:8000/api/quakes?hours=24&min_mag=2.5" | python3 -c "import sys,json; d=json.load(sys.stdin); print('건수:', d['stats']['count'], '| 최대:', d['stats']['max_mag'])"
개념 짚기 (Level 200), 창(window) 피드와 멱등 upsert 이 피드는 "최근 24시간"이라는 입니다. 매 폴링마다 대부분의 이벤트가 다시 오고, 이미 본 지진의 규모가 수정되어 오기도 합니다(USGS는 검토 후 값을 갱신합니다). 그래서 삽입이 아니라 id 기반 upsert여야 합니다 - 새 id는 추가, 아는 id는 갱신. 이 한 줄의 규율이 "신규 진입"이라는 정보를 만들고, 중복 펄스를 막습니다.
DEFINITION OF DONE
MISSION 03

Phase 2, 세계지도

40분

변환 두 줄로 진앙이 제자리에 찍히고, 규모 슬라이더와 테이블까지 살아나면 워치가 완성됩니다.

이 미션의 바탕 학습: Ch2의 병렬 감각. 지도를 그리는 동안 테이블과 필터를 서브에이전트로 병행할 수 있습니다.

Claude 세션 입력
/superpowers:execute-plan Phase 2를 구현해줘. frontend/index.html 정적 SPA 한 장: 요구 5의 화면 스펙(듀얼 테마, 통계 바, 등장방형 세계지도 SVG - 간이 대륙 윤곽 + 진앙 원(반지름=규모, 색=깊이) + 최근 1시간 펄스, KST 이벤트 테이블, 규모 슬라이더와 시간 창 토글). FastAPI 정적 서빙 mount까지. 외부 지도/차트 라이브러리 금지.
Terminal, 미리보기
# 브라우저에서 /proxy/8000/ 열기, 슬라이더를 움직이면 지도와 테이블이 함께 걸러진다
개념 짚기 (Level 200), 투영은 함수다 지도 라이브러리가 하는 일의 본질은 (경도, 위도) → (x, y) 함수 하나입니다. 등장방형이면 x = (lon+180)/360 × W, y = (90-lat)/180 × H, 끝. 고위도가 넓어 보이는 왜곡이 있지만 이벤트 점 찍기에는 충분합니다. 이 감각이 있으면 어떤 지리 데이터든 SVG 한 장에 올릴 수 있고, 정밀 지도가 필요해지는 순간이 곧 라이브러리를 들일 순간입니다.
이 미션의 3대 함정 ① 점이 전부 왼쪽 위에 몰린다 = coordinates 순서를 [위도, 경도]로 읽은 것, GeoJSON은 [경도, 위도, 깊이]입니다. ② 시간이 이상하다 = time은 UTC 밀리초, KST 변환(+9h)과 /1000을 확인하세요. ③ 지진이 안 보인다 = 버그가 아닐 수 있습니다, 조용한 날엔 조용합니다. min_mag를 2.5로 내려 확인하세요.
DEFINITION OF DONE
MISSION 04

Phase 3 + 4, 브리핑과 개장

30분

숫자가 읽고 싶은 해설이 되고, 내 CloudFront URL이 열리면 미션 완료입니다.

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

Claude 세션 입력, 브리핑
/superpowers:execute-plan Phase 3(브리핑)을 구현해줘. llm 모듈: httpx POST converse(Bearer, maxTokens 700, 타임아웃 504/파싱 502 매핑), POST /api/brief - 입력은 24h 통계와 상위 이벤트, 출력은 요구 6의 한국어 브리핑, 10분 버킷 캐시. 화면 브리핑 카드와 버튼까지.
Claude 세션 입력, 개장
/superpowers:execute-plan Phase 4(개장)를 진행해줘. 선배포한 인프라에 앱 이미지를 빌드/푸시하고 서비스 갱신, CloudFront 도메인 출력. X-Origin-Verify와 Secrets 주입이 살아 있는지 확인 절차 포함.
Terminal, 개장 확인
DOMAIN=$(aws cloudformation describe-stacks --stack-name QuakeWatch --query "Stacks[0].Outputs[?OutputKey=='CloudFrontDomain'].OutputValue" --output text)
curl -s "https://$DOMAIN/healthz" && echo " <- 열렸습니다, 이 URL을 Slack에"
개념 짚기 (Level 200), 숫자를 서사로 "24시간 동안 M2.5+ 지진 143건, 최대 5.8" - 정확하지만 아무도 안 읽습니다. 브리핑 프롬프트의 요령은 역할(지구과학 해설가), 재료(통계 + 상위 이벤트), 형식(문단 구성), 금지(과장, 예측)를 명시하는 것입니다. 특히 지진은 공포를 팔지 않는 절제가 품질입니다 - "불안 조성 금지, 사실 기반"을 프롬프트에 못 박으세요. 같은 데이터라도 프롬프트 계약이 결과물의 품격을 정합니다.
개장이 막힐 때 ① ALB 직접 접근이 403 = 정상입니다, X-Origin-Verify 이중 방어가 일하는 중(CloudFront로만 오세요). ② 502 = 컨테이너 헬스체크 경로(/healthz)와 포트 매핑을 확인. ③ 브리핑만 실패 = Secrets의 Bearer가 태스크에 주입되는지 (aws ecs describe-task-definition에서 secrets 블록) 확인하세요.
DEFINITION OF DONE
BONUS

워치 퀄리티 강화 라운드, 감각을 올려라

+α 자유

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

A강화 카드 1, 지역 프리셋
Claude 세션 입력
지역 프리셋을 추가하자. 헤더에 전체 / 환태평양 / 일본 주변 / 한반도 주변 버튼:
각 프리셋은 경위도 바운딩 박스로 필터하고, 지도는 그 영역을 확대(viewBox 전환)한다.
프리셋 선택은 통계 바와 테이블에도 함께 반영, localStorage에 기억.
B강화 카드 2, 판 경계 오버레이
Claude 세션 입력
지각판 경계를 지도에 겹치자. 판 경계의 간이 폴리라인 데이터를 plates.js에 상수로 내장하고
(정밀할 필요 없음, 주요 경계 윤곽이면 충분) 얇은 점선 레이어로 그린다.
토글 버튼으로 켜고 끄기. 진앙이 경계를 따라 늘어서는 그림이 이 카드의 보상이다.
C강화 카드 3, 신규 이벤트 라이브 배지
Claude 세션 입력
새로고침 사이의 변화를 보여주자. /api/quakes 응답에 신규 진입 id 목록(new_ids)을 포함하고,
화면은 직전 응답과 비교해 새 이벤트에 NEW 배지와 3초 하이라이트를 준다.
헤더에 "이번 사이클 신규 N건" 카운터. 지구가 방금 움직였다는 감각을 화면에.
D강화 카드 4, 깊이 단면 뷰
Claude 세션 입력
깊이 단면을 추가하자. 지도 아래에 가로축 = 경도, 세로축 = 깊이(0~700km 로그 스케일)인
산점 SVG를 하나 더: 같은 데이터를 다른 축으로 보면 섭입대의 경사가 드러난다.
지도의 필터와 완전히 연동, 점 호버 시 규모와 지역 툴팁.
E강화 카드 5, 나만의 워치로

여기부터는 정해진 카드가 없습니다. 당신이 관측소장입니다. 지도를 보며 궁금했던 것 하나를 골라, 위 카드들처럼 직접 요구사항을 써서 주문하세요. 아이디어가 필요하면: 시간대별 히스토그램, 규모-빈도 분포(구텐베르크-리히터), 최근 7일 창 전환(2.5_week.geojson), 진앙 클릭 → USGS 상세 링크, 소리 알림, DynamoDB로 스토어 승격(TTL 24시간이면 "창" 개념이 테이블 차원에서 재현되고, 재배포와 다중 태스크에도 상태가 유지됩니다).

Claude 세션 입력, 빈칸을 내 아이디어로
내 워치에 [내가 원하는 기능]을 추가하자. 규칙:
1) 데이터: [필요한 필드와 출처, 피드가 달라지면 URL 명시]
2) 화면: [어디에 어떻게 보이는지]
3) 계약: 화면은 우리 API만 본다, 멱등 스토어 원칙은 깨지 않는다
4) 배포: 완성되면 재배포까지
구현 전에 이 규칙을 네 계획으로 다시 말해주고, 승인하면 진행해.
강화 라운드의 요령 ① 카드 하나 = 커밋 하나, 되돌아갈 지점을 만들며 갑니다. ② 화면으로 확인하고 다른 점을 말로 다듬으세요. ③ 마지막엔 재배포로 Slack의 내 URL을 최신으로.
BONUS DONE, 진행률 미집계
OPTION

하네스 엔지니어링

+15분

완주한 프로젝트를 표준 하네스로 성숙시킵니다 (project-init으로 구조를 잡고 harness-eval로 6개 차원 등급을 받습니다).

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

Claude 세션 입력
/project-init:init-project .
Claude 세션 입력, 이어서
/harness-eval:quick

등급표에서 낮은 차원 하나를 골라 /add-adr 또는 /add-runbook으로 보강하고 재평가해 보세요.

OPTION DONE, 진행률 미집계

미션 종료

피드는 창으로, 저장은 멱등하게, 좌표는 함수로, 숫자는 서사로. 그리고 개장은 CloudFront로.

가져가는 것내용
창 피드의 멱등 upsert재등장과 값 갱신이 기본인 상류에서 id 규율이 데이터를 지킨다
투영의 최소 수학지도 = (lon, lat) → (x, y) 함수, 두 줄이면 세계가 SVG에 실린다
절제된 브리핑재료, 형식, 금지를 계약한 프롬프트가 품격 있는 해설을 만든다
NEXT

포털로

워크샵 포털에서 다음 미션을 고르세요.