서비스 Plane과 엔드포인트
Next.js control plane과 분리 가능한 data plane 서비스, 주요 API endpoint의 역할을 정리합니다.
리옵트 데이터는 apps/web을 중심으로 동작하지만, 수집과 조회, 실시간, export는 별도 서비스로 분리할 수 있어요.
로컬 개발에서는 fallback으로 단순하게 시작하고, 운영 환경에서는 필요에 따라 plane을 분리합니다.
서비스 plane
| 서비스 | 역할 |
|---|---|
apps/web | Next.js control plane, 인증 세션, gateway route, docs, UI |
apps/ingest-worker | 로컬 또는 비-Vercel 환경용 background ingest worker |
apps/control-api | 조직, 프로젝트, 대시보드, 빌링 등 control plane API |
apps/query-api | funnel, retention, event analysis 등 query plane API |
apps/realtime-service | SSE 전용 스트림 서비스 |
apps/export-service | export 전용 서비스 |
주요 엔드포인트
| 엔드포인트 | 역할 |
|---|---|
POST /api/track | 이벤트 수집 |
OPTIONS /api/track | CORS preflight |
/api/trpc/* | compatibility tRPC gateway |
/api/control-trpc/* | control plane tRPC gateway |
/api/query-trpc/* | query plane tRPC gateway |
/api/v1/organizations* | 버전 있는 provisioning/control REST |
/api/v1/projects* | 버전 있는 project provisioning REST |
/api/v1/query/* | 서버 자격증명 기반 analytics REST |
GET /api/realtime/[projectId] | SSE 실시간 이벤트 스트림 |
GET /api/export/events | 이벤트 CSV export |
GET /api/health | PostgreSQL, Redis, analytics backend, ingest 상태 확인 |
GET /api/docs/search | docs search index |
POST /api/webhooks/stripe | Stripe billing webhook |
GET /api/reopt-auth/complete | Reopt OAuth 완료 후 로컬 세션 생성 |
GET /api/reopt-auth/e2e/complete | local/test 전용 OAuth completion |
POST /api/queues/process-ingest | Vercel Queue ingest consumer |
health endpoint가 보여주는 것
- PostgreSQL 상태
- Redis 상태
- 선택된 analytics backend 상태와 backend 종류 (
motherduck또는clickhouse) - ingest queue provider
- queue backlog
- worker heartbeat
health는 단순 프로세스 up/down만 보여주지 않습니다. 수집 파이프라인이 실제로 처리 가능한 상태인지 함께 보여줍니다.
export endpoint 동작
EXPORT_SERVICE_URL이 있으면 upstream 서비스로 proxy- 없으면 앱 내부 핸들러로 직접 처리
tRPC gateway 분리
/api/trpc/*는 compatibility gateway입니다.
새 호출은 plane에 맞춰 다음 경로를 우선 사용하세요.
- control plane:
/api/control-trpc/* - query plane:
/api/query-trpc/*
CONTROL_API_URL 또는 QUERY_API_URL이 설정되어 있으면 apps/web은 canonical tRPC 경로뿐 아니라
각 plane의 /api/v1/* REST 경로도 해당 upstream으로 proxy합니다. standalone 서비스도 같은 공유 핸들러를
서빙하므로 fallback과 분리 배포의 응답 계약이 같습니다. 비어 있으면 web process 안의 같은 핸들러가 처리합니다.
Cron과 replay endpoint
운영 작업은 cron route로 노출됩니다.
| 엔드포인트 | 메서드 | 역할 |
|---|---|---|
/api/cron/evaluate-alerts | GET | 알림 조건 평가 |
/api/cron/sync-usage | GET | Redis 사용량을 PostgreSQL로 동기화 |
/api/cron/process-ingest | GET | 로컬/수동 ingest batch 처리 |
/api/cron/process-ingest/replay | GET, POST | 특정 프로젝트/request/date 범위 raw event replay enqueue |
/api/cron/process-ingest/replay-unmaterialized | GET | materialization 누락 raw event replay enqueue |
/api/cron/process-ingest/status | GET | ingest queue 상태와 preview 조회 |
production에서는 Authorization: Bearer $CRON_SECRET이 필요합니다.
각 cron은 Redis lock을 사용합니다. 이미 같은 scope가 실행 중이면 skipped: true, reason: "locked"로 응답합니다.
Vercel Queue consumer
Vercel 배포에서는 app/api/queues/process-ingest/route.ts가 queue consumer 역할을 합니다.
- topic:
ingest-events - max delivery count: 8
- visibility timeout: 600초
- retry delay: 지수 backoff, 최대 300초
- poisoned message는 최대 delivery 이후 acknowledge합니다.
로컬/비-Vercel 환경에서는 apps/ingest-worker가 Redis queue를 처리합니다.
주의할 점
CONTROL_API_URL,QUERY_API_URL,REALTIME_SERVICE_URL,EXPORT_SERVICE_URL을 설정하면apps/web이 해당 서비스로 proxy합니다.pnpm dev와dev:core는 로컬 fallback을 우선 사용하도록 서비스 URL을 비워 두는 구성이 안전합니다.- 분리 배포할 때는 각 plane의 health와 인증 헤더 전달을 함께 확인하세요.
- cron/replay/status route는 운영 데이터 재처리와 삭제성 작업의 입구가 될 수 있으므로
CRON_SECRET을 반드시 강하게 설정하세요.