MAP PROTOTYPE
QUAKECURRENT / CYCLES 01—05
01—05 VERIFIED
ITERATION IS THE PRODUCT

작은 동작을 만들고,
증거로 다음 결정을 내린다.

이 페이지는 결과물의 장식이 아니라 구현 기록입니다. 같은 지진 수직 슬라이스를 다섯 번 순환하며 무엇을 만들고, 자동화하고, 검토했는지 보여줍니다.

01

PROTOTYPE

가장 얇은 수직 슬라이스로 위험을 먼저 확인했다.

USGS 24시간 피드 하나, 지도 하나, 상세 패널 하나만 연결했다. 여러 재난 데이터와 AI 설명은 의도적으로 다음 사이클로 미뤘다.

  • 한국어 사건 정보 + MapLibre 3D 지구본 + deck.gl 포인트
  • 사건 선택과 위치·규모·깊이 상세
  • API 장애 시 명확히 표시되는 데모 스냅샷
02

PLAN

화면보다 계약과 실패 경로를 먼저 고정했다.

브라우저가 USGS를 직접 호출하지 않도록 독립 FastAPI 경계를 두고, REST 스냅샷 뒤에는 작은 WebSocket 변경 신호만 흐르게 설계했다.

  • FastAPI OpenAPI → TypeScript REST 클라이언트
  • WebSocket 별도 타입 + 마지막 sequence 기반 재연결
  • source + external_id 멱등 키와 content hash
03

AUTOPILOT

수집부터 갱신 신호까지 사람이 없어도 반복된다.

Celery Beat가 60초마다 수집을 예약하고 worker가 정규화·upsert한다. 내용이 실제로 달라진 사건만 sequence를 만들고 Redis로 발행한다.

  • PostgreSQL/PostGIS + Alembic 마이그레이션
  • Celery Beat / worker / Redis 로컬 스택
  • 신규·수정 사건만 전달하는 compact change signal
04

REVIEW

보이는 완성도와 데이터 무결성을 같은 기준으로 검토했다.

결정론적 fixture, 계약 테스트, 프로덕션 빌드, 로컬 Docker 스택과 외부 타일 소스를 함께 확인했다. 한국어 안내와 수치용 글꼴 역할도 분리했다. 아래 수치는 이번 로컬 실행에서 기록한 결과다.

  • 동일 피드 재수집 시 중복 신규 행 0건
  • WebSocket 종료 후 마지막 sequence 기반 catch-up
  • 프로덕션 빌드·베이스맵 렌더링·한국어 사건 정보 검증
CYCLE 01 · REVIEW EVIDENCE

느낌이 아니라 실행 결과로 닫는 사이클

IDEMPOTENCY0 DUPLICATES2차 fixture: 2 unchanged / 0 signal
RECONNECTPASSafter_seq REST catch-up + WS resume
WEB BUILDPASSCycle 01 local · web 7/7 · SSR 2/2
API TESTS15 / 15live PostGIS · REST · WebSocket
CLIENT TESTS9 / 9REST contract + WebSocket state machine
SNAPSHOT45 KB120 events · compact REST payload
MAP CHUNK404 KBgzip · lazy-loaded after shell
BASEMAP SOURCE200 OKCARTO raster · inline style
REVIEW에서 발견해 닫은 결함
R-01

지도 캔버스가 목록 높이를 따라 8,675px까지 늘어났다.

앱 셸을 100dvh로 고정하고 목록·상세만 독립 스크롤 영역으로 분리했다.

R-02

브라우저가 클라이언트의 WebSocket close code 1011을 거부했다.

서버 예약 코드 대신 애플리케이션 범위의 4001을 사용하고 회귀 테스트를 추가했다.

R-03

PostgreSQL sequence를 빈틈없는 카운터로 잘못 가정했다.

롤백이 번호를 소비할 수 있으므로 연속성이 아닌 단조 증가 cursor로 검증하도록 바꿨다.

R-04

외부 벡터 스타일과 조기 projection 적용이 지도 초기화 경로를 불안정하게 만들었다.

검증된 CARTO 래스터 스타일을 인라인하고 MapLibre style.load 이후에만 projection을 적용했다.

R-05

한국어 로딩·좌표 문구가 데이터용 모노스페이스를 상속했다.

LINE Seed Sans KR을 자체 포함하고 한글 안내는 sans, 영문 코드와 수치는 mono라는 역할로 분리했다.

R-06

벡터 스타일은 준비됐지만 실제 지구 표면 레이어가 렌더링되지 않았다.

외부 스타일·글리프 의존성을 제거하고 경량 래스터를 기본 베이스맵으로 고정해 렌더 경로를 단순화했다.

배포 지표는 이번 사이클에서 제외했습니다. 인프라 선택이 확정된 뒤 동일한 Review 단계에 production telemetry를 추가합니다.

CYCLE 02 · CLOSED · 2026-07-29

URL을 공유 가능한 제품 상태로 만들고, 자동 검증으로 닫았다.

현재 24시간 스냅샷은 약 45KB라 클라이언트 필터가 더 단순하다. gzip payload가 250KB를 넘거나 중급 모바일의 필터 계산 p95가 50ms를 넘으면 API query와 pagination 승격을 다시 검토한다.

01
PROTOTYPE

시간·최소 규모·깊이를 바꾸면 원본 24시간 스냅샷은 유지하고 목록·통계·지도·선택만 하나의 파생 결과로 갱신한다.

02
PLAN

URL을 공유 가능한 상태 경계로 선택했다. 기본값은 생략하고 다른 query와 hash는 보존하며, 즉시 필터 토글은 replaceState로 기록 과잉을 막는다.

03
AUTOPILOT

순수 URL 모델, SSR 계약, 결정론적 데모 스냅샷, 데스크톱·모바일 브라우저 흐름을 자동화하고 API와 웹 검증을 CI 두 작업으로 정의했다.

04
REVIEW

1440×900과 390×844에서 직접 확인하고 테스트 경쟁 조건, 지도 범위 표기, 텍스트 대비, 모션 감소, 늦은 지도 로딩 복구를 수정했다.

FILTER MODEL14 / 14whitelist · defaults · round-trip
API LOCAL15 / 1512 deterministic + 3 live stack
CLIENT9 / 9REST contract + realtime state
SSR3 / 3shell · shared URL · build log
BROWSER10 / 10desktop + Pixel 7 profiles
VIEWPORT2 / 21440×900 · 390×844
CONSOLE0 ERRORSlocal production browser review
CIPASSAPI + web · run #30410815313
CYCLE 02 REVIEW에서 닫은 결함
C2-R01

6시간 필터에서도 지도 설명이 PAST 24 HOURS로 남았다.

활성 시간 범위를 지도 컴포넌트 계약에 추가하고 브라우저 회귀 검증에 포함했다.

C2-R02

규모색을 작은 숫자 글자색으로 사용해 명도 대비가 부족했다.

숫자는 잉크색으로 고정하고 규모색은 테두리와 지도 신호에만 남겼다.

C2-R03

선택 위치 이동이 운영체제의 모션 감소 설정을 무시했다.

필수 애니메이션 지정을 제거하고 늦게 성공한 style.load가 임시 오류를 해제하도록 했다.

C2-R04

필터 URL에서 같은 경로의 브랜드 링크로 이동하면 상태가 남을 수 있었다.

서버가 계산한 필터 키로 대시보드를 재마운트하고 실제 링크 이동 테스트를 추가했다.

C2-R05

모바일 history 테스트가 hydration과 경쟁해 간헐적으로 실패했다.

실제 UI로 첫 상태를 만든 뒤 popstate를 발생시켜 제품 흐름과 테스트 시점을 일치시켰다.

C2-R06

실행 전인 원격 CI를 이미 반복 실행되는 것처럼 표현했다.

로컬 통과와 CI 구성을 분리해 기록하고 첫 push 전 상태임을 명시했다.

C2-R07

지도 하단의 활성 시간 라벨과 베이스맵 출처가 겹쳤다.

데스크톱에서 시간 라벨만 출처 위로 분리하고, 모바일 상단 배치는 그대로 유지한 뒤 실제 경계 상자로 겹침 0건을 확인했다.

위 수치는 2026-07-29 로컬 실행 결과다. GitHub Actions Verify #30410815313에서 commit dce4594의 API와 웹 작업이 모두 통과했다. 이는 원격 CI 증거이며 배포 성능으로 표현하지 않는다. OpenAPI 재생성 drift gate와 production telemetry는 다음 사이클의 검토 항목이다.

CYCLE 03 · CLOSED · 2026-07-29

계약의 변경을 사람이 기억하는 대신 파이프라인이 증명한다.

FastAPI가 유일한 REST 원본이다. 정렬된 OpenAPI 스냅샷이 Python과 TypeScript 사이의 명시적 경계가 되고, 생성물 한쪽만 바뀌면 커밋하기 전과 CI에서 모두 실패한다.

01
PROTOTYPE

수기 bootstrap 타입을 실제 FastAPI OpenAPI 문서로 재생성해 누락돼 있던 304·404·422 응답과 검증 오류 타입을 드러냈다.

02
PLAN

FastAPI를 원본, 정렬된 openapi.json을 언어 경계, TypeScript를 파생 산출물로 정했다. WebSocket 프레임은 별도 계약으로 유지한다.

03
AUTOPILOT

한 명령으로 스냅샷과 타입을 갱신하고, API CI는 서버→스냅샷을, 웹 CI는 스냅샷→타입을 독립적으로 검사한다.

04
REVIEW

필드를 메모리에서 변경해 TypeScript 반영을 검증하고, 줄 끝 차이는 무시하되 실제 내용 차이만 파일명과 함께 실패하도록 확인했다.

REST PATHS5 / 5health · readiness · events
SCHEMAS11domain · validation · error
OPERATION IDs5 / 5explicit · unique · stable
GENERATED TS446 LOCopenapi-typescript 7.13.0
API LOCAL15 / 15deterministic contract suite
CLIENT12 / 12REST · WS · generator
DRIFT GATES2 / 2API snapshot · generated types
CIPASSAPI + web · run #30412360163
CYCLE 03 REVIEW에서 닫은 계약 결함
C3-R01

생성 파일이라고 적힌 타입이 실제로는 148줄짜리 수기 축약본이었다.

실제 FastAPI 문서에서 446줄 타입을 생성해 검증 오류와 모든 응답 계약을 포함했다.

C3-R02

기본 operationId가 Python 함수명과 경로에 결합돼 있었다.

공개 REST 동작 다섯 개에 명시적·유일한 operationId를 부여하고 계약 테스트로 고정했다.

C3-R03

하나의 CI 작업에서 두 언어를 검사하면 Python과 Node 설치가 중복된다.

API 작업은 스냅샷, 웹 작업은 TypeScript 생성물만 검사하도록 경계를 나눴다.

C3-R04

실제 304와 404 응답이 OpenAPI 문서에 선언되지 않았다.

조건부 스냅샷의 304와 사건 상세의 404 오류 모델을 응답 계약에 추가했다.

위 수치는 2026-07-29 로컬 검증 결과다. GitHub Actions Verify #30412360163에서 commit 31f9277의 API 스냅샷 gate, TypeScript 생성물 gate, 기존 API·웹·브라우저 검증이 모두 통과했다. Python 전이 의존성 lock은 Cycle 04로 이어졌고 production telemetry는 배포 결정 이후의 검토 항목이다.

CYCLE 04 · CLOSED · 2026-07-29

Python 의존성 해석 결과까지 고정해 같은 커밋을 다시 만들 수 있게 했다.

pyproject.toml은 지원 범위를 설명하고 uv.lock은 선택된 전체 의존성 그래프를 기록한다. Python 3.12를 기준선으로 유지하면서 3.13까지 같은 OpenAPI 계약과 결정론적 테스트 결과를 검증한다.

01
PROTOTYPE

pyproject.toml의 범위 의존성은 호환 가능한 직접 의존성만 설명할 뿐, 설치 시점마다 달라질 수 있는 전이 의존성의 정확한 버전까지 고정하지 못했다.

02
PLAN

Python 3.12를 기준선으로 두고 3.12·3.13을 모두 지원한다. uv 0.12.0과 uv.lock을 저장소에 고정해 같은 커밋이 같은 의존성 그래프를 재현하도록 정했다.

03
AUTOPILOT

CI는 uv lock --check로 선언과 lock의 일치를 검사한 뒤 uv sync --locked --extra dev로 개발 환경을 만든다. Docker는 uv sync --locked --no-editable로 런타임 환경만 재현한다.

04
REVIEW

새 환경에서 Python 3.12.13과 3.13.12를 각각 설치해 OpenAPI 스냅샷과 결정론적 API 테스트를 검증했다. 공개 CI에서도 두 Python 버전, 잠금 기반 컨테이너, 기존 웹 검증이 모두 통과했다.

LOCKFILEPASSuv.lock · 선언과 해석 결과 고정
UV0.12.0로컬 · CI · Docker 도구 버전
PYTHON 3.123.12.13새 환경 · 지원 기준선
PYTHON 3.133.13.12새 환경 · 상위 지원 버전
OPENAPI2 / 23.12 PASS · 3.13 PASS
API TESTS15 PASSEDPython 버전별 동일 결과
LIVE STACK3 DESELECTED외부 서비스 테스트 분리
CIPASSAPI 행렬 · 컨테이너 · 웹
CYCLE 04 REVIEW에서 닫았거나 보류한 재현성 결함
C4-R01

범위 의존성만으로는 전이 패키지의 버전과 해시를 재현할 수 없었다.

uv.lock을 커밋하고 uv lock --check를 자동 검증 경계로 추가했다.

C4-R02

로컬 기본 Python 3.13과 CI의 Python 3.12 사이에 검증 공백이 있었다.

3.12를 프로젝트 기준선으로 명시하고 3.12.13·3.13.12의 새 환경에서 같은 계약과 테스트 결과를 확인했다.

C4-R03

일반 pip 설치는 실행할 때마다 의존성 해석 결과가 달라질 수 있었다.

개발·CI는 --locked --extra dev, Docker 런타임은 --locked --no-editable 동기화로 역할을 분리했다.

C4-R04

로컬 Docker daemon이 실행 중이 아니어서 컨테이너 빌드를 검증할 수 없었다.

로컬 통과로 과장하지 않고 공개 저장소의 CI container 작업에서 동일한 lock 기반 빌드와 비루트 실행을 확인했다.

위 수치는 2026-07-29 로컬의 격리된 새 환경에서 확인한 결과다. Python 3.12.13과 3.13.12에서 OpenAPI 검증이 각각 통과했고, 각 환경의 결정론적 API 테스트는 15 passed / 3 deselected였다. 공개 GitHub Actions Verify #30418181319에서 commit da111c3의 Python 3.12·3.13 계약 작업, 잠금 기반 API 컨테이너 빌드와 비루트 실행, 기존 웹·브라우저 검증이 모두 통과했다. 배포는 여건이 확정될 때까지 계속 보류하며 이 결과를 운영 성능으로 표현하지 않는다.

CYCLE 05 · CLOSED · 2026-07-29

깊이를 줄이지 않고도 빠르게 파악하도록 프로젝트 진입점을 설계했다.

지도 프로토타입은 제품의 증거로, Build Log는 과정의 증거로 유지한다. /project는 두 화면 사이에서 문제·구현 범위·기술 판단·검증 결과를 압축하고 공개 코드와 CI까지 연결하는 채용용 브리프다.

01
PROTOTYPE

지도 프로토타입은 구현 결과를 바로 보여주지만 문제·구현 범위·기술 판단을 빠르게 파악하기 어려웠고, 긴 Build Log는 첫 진입점으로 쓰기에는 정보량이 많았다.

02
PLAN

Server Component 기반 /project를 채용용 허브로 두고 지도 프로토타입, 상세 Build Log, 공개 코드와 CI를 각각 한 단계 안에서 열도록 정보 경계를 나눴다.

03
AUTOPILOT

소개 페이지를 client state와 WebGL이 없는 Server Component로 만들고 SSR 계약, 메타데이터, 실제 저장소·CI 링크를 자동 검증 경계에 포함했다.

04
REVIEW

운영 성과로 오해할 수 있는 표현을 제거하고 구현 범위·보류 항목을 함께 적었다. 키보드 skip link, 의미 있는 landmark, 모바일 단일 열과 새 공유 카드를 확인했다.

PROJECT BRIEFPASSclient state 없는 서버 렌더링
IMPLEMENTED SCOPE4 AREAS제품 · UI · API/데이터 · 품질/CI
DECISIONS4판단 기준과 승격 조건
EVIDENCE6로컬·공개 CI 검증 결과
SSR4 / 4dashboard · URL · log · brief
SOCIAL CARD1731 × 909프로젝트 카피 · 전용 OG
CLIENT STATE0소개 경로의 로컬 상태
CIPASSVerify #30420200735
CYCLE 05 REVIEW에서 닫은 전달력 결함
C5-R01

제품과 상세 로그 사이에 빠르게 맥락을 읽는 진입점이 없었다.

문제·구현 범위·결정·증거·경계를 한 화면 흐름으로 압축한 /project를 추가했다.

C5-R02

소개를 지도 안에 합치면 100dvh WebGL 레이아웃과 모바일 시트가 충돌한다.

지도 client island는 그대로 두고 소개를 client state 없는 Server Component 라우트로 분리했다.

C5-R03

헤더의 CYCLE 02와 이전 공유 카드가 현재 반복 상태를 잘못 설명했다.

사이클 번호와 무관한 FULL-STACK PROTOTYPE으로 바꾸고 현재 프로젝트 카피의 OG 카드를 연결했다.

C5-R04

과거 측정값이 현재 운영 KPI처럼 읽힐 수 있었다.

재실행 가능한 테스트·계약·CI 수치를 우선하고, 과거 45KB 측정은 Cycle 02의 의사결정 근거로만 한정했다.

위 내용은 2026-07-29 로컬 코드·문서 검토 기준이다. /project는 client state 없는 Server Component로 서버 렌더링되며, 빌드 manifest에서 project page의 route JS 배열이 비어 있음을 확인했다. 타입·lint·프로덕션 빌드와 SSR 4/4가 통과했다. 공개 CI Verify #30420200735에서 commit 75d7be2의 Python 3.12·3.13 계약 검사, 잠금 기반 API 컨테이너와 웹·브라우저 검증이 모두 통과했다. 배포와 production telemetry는 계속 범위에서 제외한다.