
비 오는 날 밤. 갑자기 천둥이 엄청 크게 쳤다.
"와 진짜 크다... 나만 들은 건가? 다른 사람들도 들었나?"
트위터에서 "천둥" 검색하면 반응은 나온다. 근데 우리 동네 사람인지 모르겠고, 실시간도 아니고, 그냥 검색 결과일 뿐이다.
같은 동네에서, 같은 순간을, 같이 경험하고 있다는 걸 실시간으로 볼 수 있으면?
그래서 만들었다. Hear That? — 천둥이 치면 같은 동네 사람들의 반응을 실시간으로 보는 웹앱.
라이브: https://hear-that.vercel.app
GitHub: https://github.com/moonjun1/hear-that
이 프로젝트는 gstack + Claude Code로 만들었다. 솔직히 말하면, 코드의 대부분은 AI가 짰다. 내가 한 건 "뭘 만들지" 결정하고, "이거 이상한데?" 피드백 주고, "다음 이거 해" 지시한 것.
gstack은 Garry Tan이 만든 오픈소스 AI 빌더 프레임워크다. Claude Code 위에서 돌아가는 스킬 시스템인데, /office-hours로 아이디어 브레인스토밍하고, /qa로 QA 테스트하고, /design-review로 디자인 리뷰하고, /ship으로 배포하는 식이다.
4월 5일 밤 11시 — "천둥이 치는데 다른 사람 반응이 궁금하다"고 Claude에게 말함
11시 30분 — /office-hours 스킬로 아이디어 정리. "Hear That?"이라는 이름이 정해짐. 디자인 문서가 나옴.
12시 — GitHub repo 생성, 이슈 17개, 마일스톤 7개가 자동으로 세팅됨
12시 30분 — Day 1 코드 완성. Next.js + Supabase + Mapbox 세팅, 핵심 컴포넌트 13개.
새벽 1시 — Day 2-7 코드 전부 완성. PR 8개 머지. Vercel 배포.
새벽 2시 — 실제 기상청 API로 번개 데이터가 들어오기 시작. 실제로 충남 쪽에 번개가 치고 있었다.
4월 6일 — QA, 디자인 리뷰, 모바일 최적화. 버그 수정 25건. PR 20개 추가.
총: 하루 반 만에 52커밋, 29파일, 2,492줄, PR 28개.
| 레이어 | 기술 |
|---|---|
| 프론트엔드 | Next.js 16 (App Router) + TypeScript + Tailwind CSS |
| 백엔드/DB | Supabase (Postgres + Realtime WebSocket) |
| 지도 | Mapbox GL JS |
| 지역 묶기 | H3 (Uber의 육각형 그리드 시스템) |
| 날씨 | 기상청 낙뢰관측자료 조회서비스 API |
| 배포 | Vercel |
| OG 이미지 | @vercel/og |
| 분석 | Google Analytics 4 |
Supabase를 고른 이유 — 실시간 WebSocket이 핵심이었다. 누가 이모지를 탭하면 같은 동네 사람 피드에 즉시 떠야 한다. Supabase Realtime이 Postgres Changes를 WebSocket으로 브로드캐스트해준다. Firebase보다 SQL 쿼리가 자유롭고, 무료 tier가 넉넉하다.
Mapbox를 고른 이유 — Thunder Wave 애니메이션. 번개 지점에서 동심원이 343m/s로 퍼져나가는 걸 canvas로 그려야 했다. WebGL custom layer가 가능한 건 Mapbox뿐이었다. 네이버 지도나 카카오맵은 오버레이만 가능해서 이 애니메이션이 안 된다.
H3를 고른 이유 — "같은 동네"를 정의하는 문제. 행정구역(구/동)으로 나누면 경계에 있는 사람들이 갈라진다. H3 육각형 그리드는 경계가 자연스럽고, resolution 5로 설정하면 반경 ~9km로 "우리 동네" 느낌이 딱 맞다.
기상청 낙뢰관측자료 API(getLgt)는 개별 낙뢰마다 WGS84 위도/경도를 소수점 6자리까지 준다. 정밀도가 약 0.1m. 10분 지연이지만 준실시간이다.
문제는 바다에서 친 번개도 나온다는 것. 위도 33~38.6, 경도 125~131.9로 한국 육지만 필터링했다.
Vercel 서버리스 환경에서 let lastPollTime = 0 같은 메모리 변수는 인스턴스마다 다르다. 1분 캐시를 걸어도 다른 인스턴스에서는 캐시가 없다.
해결: 매 폴링마다 DB의 weather_events를 전부 지우고(RPC 함수) 새로 넣는 "delete all + insert fresh" 방식으로 바꿨다.
React 18+ strict mode는 useEffect를 두 번 실행한다. initialized.current ref로 막으면 첫 번째 마운트에서 true가 되고 두 번째(실제) 마운트에서 구독이 스킵된다.
해결: useEffect(deps: [neighbors]) — 위치가 세팅되면 구독 시작, cleanup에서 해제. ref 대신 React 라이프사이클을 따르도록.
echo "KEY" | vercel env add 하면 줄바꿈(\n)이 같이 들어간다. Supabase anon key URL에 %0A가 붙어서 WebSocket 연결이 실패했다.
해결: printf 'KEY'로 줄바꿈 없이 넣기.
솔직히, 엄청 도움이 됐다.
/office-hours — 아이디어를 디자인 문서로 바꿔줬다. "천둥이 치면 반응 보고 싶다"를 테이블 스키마, API 설계, 7일 빌드 플랜까지 만들어줬다. 세컨드 오피니언으로 "Thunder Wave" 아이디어도 여기서 나왔다.
/qa — headless 브라우저로 전체 사이트를 테스트한다. WebGL이 안 되는 한계가 있었지만, 콘솔 에러, API 응답, viewport 경고 같은 건 잡아줬다.
/design-review — 소스 코드 기반으로 디자인 이슈 27건을 찾아줬다. "AI 슬롭" (큰 이모지를 디자인 요소로 쓰는 것) 같은 건 혼자서는 못 알아챘을 것 같다.
/grill-me — 이 블로그 글의 방향도 여기서 정했다. "목적이 뭐야?" 하나 물어보는 게 30분 고민보다 빠르다.
| 항목 | 수치 |
|---|---|
| 총 개발 시간 | ~36시간 (하루 반) |
| 커밋 | 52개 |
| PR | 28개 (전부 squash merge) |
| 파일 | 29개 |
| 코드 | 2,492줄 |
| 컴포넌트 | 13개 |
| 유틸 | 9개 |
| 버그 수정 | 25건 |
| 디자인 리뷰 | 27건 발견, 25건 수정 |
| 모바일 리뷰 | 20건 발견, 9건 수정 |
1. AI는 "뭘 만들지"는 못 정한다. 코드는 잘 짜는데, "천둥 칠 때 반응이 보고 싶다"는 인간의 경험에서 나온다. AI는 실행자이지 발명가가 아니다.
2. "바로 만들어" 대신 "설계부터"가 맞다. /office-hours로 디자인 문서를 먼저 만들고 시작하니까, 중간에 방향을 잃지 않았다. 7일 플랜을 하루 반에 끝낸 것도 설계가 있어서 가능했다.
3. 사이드 프로젝트는 재미가 동력이다. "이거 돈 될까?" 생각하면 안 만든다. "천둥 치면 다른 사람 반응 궁금하다" 이 호기심 하나로 36시간을 버텼다.
4. Vercel + Supabase 무료 tier가 생각보다 넉넉하다. Supabase 무료 200 동시 접속, Vercel 무료 배포, Mapbox 월 50,000 로드, 기상청 일 10,000건. 사이드 프로젝트에는 충분하다.
천둥이 치는 밤, "다른 사람들도 들었나?" 궁금했을 뿐인데 앱이 하나 나왔다.
AI 시대에 사이드 프로젝트 만드는 건 진입 장벽이 거의 없다. 아이디어만 있으면 된다. 코드는 AI가 짜주고, 배포는 Vercel이 해주고, DB는 Supabase가 해준다.
중요한 건 "뭘 만들지" 결정하는 것. 그건 여전히 사람의 몫이다.
다음에 천둥이 치면, hear-that.vercel.app 열어봐. 같은 동네 사람들이 뭐라고 하는지 볼 수 있다.
태그: #사이드프로젝트 #Next.js #Supabase #Mapbox #기상청API #AI개발 #ClaudeCode #gstack #날씨앱 #실시간