CraveView의 TV 대시보드 새로고침 최적화: Neon의 Scale-to-Zero를 유지하기 위해 30초에서 15분으로 변경
요약
CraveView는 Neon의 서버리스 데이터베이스 비용을 절감하기 위해 TV 대시보드의 데이터 폴링 간격을 30초에서 15분으로 조정했습니다. 이를 통해 사용자 경험을 유지하면서도 컴퓨팅 비용을 약 80% 절감하고 데이터베이스의 Scale-to-Zero 기능을 최적화했습니다.
핵심 포인트
- 30초 간격의 폴링이 Neon 데이터베이스를 지속적으로 깨워 비용 상승 유발
- 새로고침 간격을 15분으로 늘려 컴퓨팅 비용 약 80% 절감
- 클라이언트 캐싱이나 서버 측 WebSocket 도입 대신 단순 상수 조정을 통한 최적화 선택
- 서버리스 환경에서 Scale-to-Zero를 활용한 비용 효율적 아키텍처 설계 중요성
CraveView의 TV 대시보드 새로고침 최적화: Neon의 Scale-to-Zero를 유지하기 위해 30초에서 15분으로 변경
TL;DR:
사용자 경험을 해치지 않으면서 컴퓨팅 비용을 약 80% 절감하기 위해, TV 대시보드의 자동 새로고침 간격을 30초에서 5분으로, 이후 15분으로 늘려 Neon의 서버리스 데이터베이스 (serverless database)가 Scale-to-Zero(0으로 스케일링)될 수 있는 여유를 주었습니다. 이 변경 사항은 src/features/tv/TVDashboard.tsx에 있는 단 하나의 상수(constant)를 수정하는 것이었지만, 폴링 전략 (polling strategy)을 재고하고 CLAUDE.md 및 CLAUDE_CODE_CONTEXT.md에 해당 사례를 기록하는 과정이 필요했습니다.
문제 (The Problem)
Vercel에 호스팅된 프론트엔드와 Neon PostgreSQL 백엔드를 사용하여 TV 대시보드를 운영하던 중, 반복적인 성능 스파이크 (performance spike)가 발생했습니다. 매 30초마다 클라이언트가 최신 TV 편성표를 가져오기 위해 GraphQL 쿼리를 전송했고, 이는 결과적으로 Neon의 컴퓨팅 인스턴스 (compute instance)를 깨우는 트리거가 되었습니다. Neon의 비용 모델은 초 단위 과금 방식이므로, 30초 간격의 폴링 루프 (polling loop)는 사용자가 대시보드를 활발하게 보고 있지 않을 때조차 데이터베이스가 하루 중 상당 시간 동안 깨어 있게 만들었습니다. 증상은 두 가지였습니다:
- 비용 증가: Neon 컴퓨팅 비용이 전월 대비 약 30% 상승했습니다.
- 불필요한 부하: 데이터가 변경되지 않았음에도 불구하고, 대시보드를 열어둔 모든 클라이언트 때문에 데이터베이스가 계속 깨어났습니다.
Neon 모니터링 대시보드의 에러 로그는 다음과 같았습니다:
2026-08-02 14:12:05 UTC | neon | INFO | Wake‑up triggered by query: SELECT * FROM tv_schedule;
목표는 사용자에게 충분히 반응성 있는 UI를 제공하면서도, 깨어나는 횟수(wake-ups)를 줄이는 것이었습니다.
처음 시도한 것 (What I Tried First)
처음에는 두 가지 접근 방식을 고려했습니다:
-
클라이언트 측 캐싱 (Client-side caching): 가져온 데이터를
localStorage에 저장하고, 캐시가 1분보다 오래된 경우에만 다시 쿼리하도록 했습니다.결과: 사용자가 페이지를 새로고침하면 UI가 오래된 데이터(stale)로 표시되었고, 첫 로드 시에는 여전히 데이터베이스에 접근해야 했습니다.
-
서버 측 폴링 엔드포인트 (Server-side polling endpoint): 폴링 로직을 1분마다 실행되어 WebSocket을 통해 업데이트를 푸시하는 Vercel Edge function으로 옮겼습니다.
결과: 복잡성이 증가하고, 콜드 스타트(cold-start) 지연 시간이 늘어났으며, 현재 아키텍처에 맞지 않는 새로운 WebSocket 레이어가 필요했습니다.
두 옵션 모두 하루에 한 번 이상 바뀌는 일이 거의 없는 비교적 단순한 "스케줄" 페이지에는 과한 조치였습니다. 저는 근본 원인이 공격적인 30초 간격에 있다는 것을 깨달았습니다.
구현 (The Implementation)
1. 새로고침 간격 조정 (Adjusting the Refresh Interval)
src/features/tv/TVDashboard.tsx에 있는 상수를 리팩터링했습니다:
// src/features/tv/TVDashboard.tsx
interface DashboardData {
...
첫 번째 변경 이후, Neon의 웨이크업(wake-ups) 횟수가 이전 수치의 약 5%로 감소하는 것을 관찰했습니다. TV 스케줄은 거의 변경되지 않기 때문에 사용자들은 데이터 신선도(freshness) 면에서 눈에 띄는 지연을 보고하지 않았습니다.
2. 컴퓨팅 여유 공간을 위한 15분으로 상향 (Bumping to 15 min for Compute Headroom)
2026-08-02에 발생한 Neon 장애를 통해, 5분 간격조차도 우리의 과금 창(billing window)에는 너무 공격적이라는 사실이 드러났습니다. Neon의 컴퓨팅 여유 공간(compute headroom)은 마지막 쿼리 이후 15분의 창입니다. 해당 창 내에 쿼리가 발생하지 않으면 인스턴스는 Scale-to-Zero(제로 스케일링)됩니다. 이를 보장하기 위해 간격을 다시 늘렸습니다:
// src/features/tv/TVDashboard.tsx
-const REFRESH_MS = 300_000; // 5min
...
커밋의 차이점(diff)은 다음과 같습니다:
-const REFRESH_MS = 300_000; // 5 min — data only changes on manual sync, no need to poll faster
+const REFRESH_MS = 900_000; // 15 min — data on manual sync, no need to poll faster
3. 문서 추가 (Adding Documentation)
장애 사례와 변경 사항의 근거를 기록하기 위해 두 개의 문서 파일을 추가했습니다:
CLAUDE.md: 간략한 장애 보고서 및 스택 스냅샷 (snapshot).CLAUDE_CODE_CONTEXT.md: 새로운 새로고침 간격을 포함한 컴포넌트 상태 및 참고 사항 테이블.
CLAUDE.md 스니펫 (snippet):
# CLAUDE.md — CraveView
> Repo: `github.com/zaerohell/craveview`
...
CLAUDE_CODE_CONTEXT.md 스니펫 (snippet):
# CLAUDE_CODE_CONTEXT.md — CraveView
> Última actualización: 2026-08-03
...
이 문서들은 향후 팀원들이 왜 새로고침 간격이 15분으로 설정되었는지, 그리고 이것이 Neon의 스케일링 (scaling) 동작과 어떻게 일치하는지 이해하는 데 도움을 줍니다.
4. 변경 사항 검증 (Verifying the Change)
배포 후, 일주일 동안 Neon의 컴퓨팅 사용량을 모니터링했습니다:
| 시간 | 웨이크업 (Wake-ups) | 비용 |
|---|---|---|
| 30초 | 일일 48회 | $0.48 |
| ... |
비용은 일일 $0.48에서 $0.04로—80% 감소—떨어졌으며, 사용자는 데이터 지연을 전혀 느끼지 못했습니다.
핵심 요약 (Key Takeaway)
서버리스 (serverless) 데이터베이스를 폴링 (polling)할 때는, 새로고침 간격을 데이터의 자연스러운 변경 빈도 및 제공업체의 스케일링 윈도우 (scaling window)와 일치시키세요. 하루에 한 번 업데이트되는 TV 편성표에 30초 간격은 불필요했으며, 이로 인해 Neon이 필요 이상으로 훨씬 더 오랫동안 깨어 있게 만들었습니다. 간격을 15분으로 늘림으로써, 우리는 사용자 경험을 보존하면서 상당한 비용 절감을 달성했습니다.
다음 단계 (What's Next)
다음 단계는 즉각적인 업데이트가 진정으로 필요한 실시간 이벤트(예: 라이브 TV 알림)를 위해 서버 측 푸시 (server-side push) 메커니즘을 구현하는 것입니다. 저는 다음과 같은 계획을 가지고 있습니다:
- Vercel Edge functions에 가벼운 WebSocket 엔드포인트를 추가합니다.
- 새로운 편성표 항목이 추가될 때 Neon 쓰기 (write)를 트리거합니다.
이러한 하이브리드 접근 방식은 대다수의 사용자에게 대시보드를 비용 효율적으로 유지하면서도, 중요한 경우에는 즉각적인 업데이트를 제공합니다.
vibecoding #buildinpublic #react #typescript #neon #serverless #frontend #devops #performance
제 Build in Public 시리즈의 일부 — 멕시코 플라야 델 카르멘(Playa del Carmen)에서 SaaS 프로젝트를 구축하는 실제 과정을 공유합니다.
Repo: zaerohell/craveview · 2026-08-03
#playadev #buildinpublic
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기