분석 백엔드 선택
ClickHouse와 MotherDuck 중 분석 백엔드를 선택하고, 로컬/운영 환경변수를 설정합니다.
리옵트 데이터의 분석 라우터는 AnalyticsBackend 추상화 뒤에서 동작합니다.
운영 환경에 맞게 ANALYTICS_BACKEND를 설정하면 ClickHouse 또는 MotherDuck 중 하나를 사용할 수 있어요.
| 값 | 설명 |
|---|---|
motherduck (기본값) | MotherDuck cloud 또는 로컬 DuckDB |
clickhouse | 셀프 호스팅 ClickHouse를 사용하는 선택형 백엔드 |
먼저 선택하세요
ClickHouse를 선택하면 기존 운영 환경과 ClickHouse 전용 SQL을 그대로 유지하기 쉽습니다. MotherDuck을 선택하면 Vercel 중심 배포에서 분석 백엔드 운영 부담을 줄일 수 있습니다.
| 상황 | 추천 |
|---|---|
| 이미 ClickHouse를 운영하고 있고 저장 쿼리가 ClickHouse 방언에 맞춰져 있음 | clickhouse |
| 대량 트래픽과 ClickHouse 전용 함수, Materialized View를 적극 사용함 | clickhouse |
| 초기 트래픽이 적고 관리형 분석 백엔드가 필요함 | motherduck |
| Vercel 단독 배포에서 외부 관리형 저장소를 선호함 | motherduck |
ClickHouse를 선택하는 경우
- 기존 운영 환경 그대로 두고 싶다 (이전 컬럼/스키마/MV가 그대로 동작)
- 대량 트래픽이 예상되며
windowFunnel,argMax,AggregatingMergeTree같은 ClickHouse-specific 기능을 활용한 SQL을 작성 중이다 SQL 탐색기(query.execute)에 저장한 SQL이 ClickHouse 방언 위주 (FINAL,dateDiff,JSONExtractString,argMax등) — MotherDuck도 탐색기를 지원하지만 DuckDB 방언으로 작성해야 하므로 마이그레이션 부담- 셀프 호스트 인프라(Docker, K8s 등)를 운영할 수 있다
MotherDuck를 선택하는 경우
- Vercel 단독 배포에서 분석 백엔드까지 외부 관리형으로 두고 싶다
- 초기 트래픽이 적고 MotherDuck Lite 무료 한도(10GB / 10h 컴퓨트)로 충분하다
- ClickHouse 인프라 운영 부담을 피하고 싶다
환경변수
# 공통
ANALYTICS_BACKEND=motherduck # 기본값, 필요하면 "clickhouse"로 변경
# MotherDuck 클라우드 사용 시
MOTHERDUCK_TOKEN=<MotherDuck Service Token>
MOTHERDUCK_DATABASE=<database name>
# 또는: 로컬 DuckDB 파일/메모리 모드 (offline)
MOTHERDUCK_LOCAL_PATH=./docker/data/analytics.duckdb # 파일 경로
# MOTHERDUCK_LOCAL_PATH=:memory: # in-memory
# DuckDB 리소스 제한 (로컬 네이티브 모드에서만 적용)
DUCKDB_MEMORY_LIMIT=512MB # 기본값 512MB
DUCKDB_THREADS=4 # 기본값 4
# ClickHouse 사용 시 (기존)
CLICKHOUSE_URL=https://...
CLICKHOUSE_USER=default
CLICKHOUSE_PASSWORD=...MOTHERDUCK_LOCAL_PATH가 설정되어 있으면 cloud token보다 우선합니다.
이 모드는 로컬 개발/검증용이며 production 환경 검증에서는 허용하지 않습니다.
개발 환경에서 cloud credential을 제거하지 않고도 로컬 파일로 고정할 수 있어요.
상대 경로는 워크스페이스 루트 기준으로 해석됩니다.
Vercel에 배포할 때는 두 백엔드 변수 모두 Production / Preview / Development 환경에 동일하게 주입해야 합니다 (Edge functions 포함). 로컬 파일 모드는 배포에는 사용하지 않습니다.
로컬 DuckDB 모드 (offline)
MOTHERDUCK_LOCAL_PATH를 사용하면 MotherDuck cloud 계정 없이도 어댑터 SQL을 검증할 수 있습니다.
parity 테스트나 CI에서 클라우드 컴퓨트 비용 없이 두 백엔드 동등성을 확인할 때 유용합니다.
# 1. 분석 백엔드를 motherduck로 지정 + 로컬 파일 경로
export ANALYTICS_BACKEND=motherduck
export MOTHERDUCK_LOCAL_PATH=./docker/data/analytics.duckdb
# 2. 스키마 부트스트랩 (한 번)
pnpm --filter @reopt/db db:motherduck:init
# 3. 평소처럼 dev (PG + Redis만 필요, ClickHouse 컨테이너 안 띄워도 됨)
docker compose up -d reopt-db reopt-redis
pnpm --filter @reopt/db db:push
pnpm dev한계
- 한 시점에 한 프로세스만 DuckDB 파일에 write 가능 — DuckDB는 file-lock
multi-writer를 지원하지 않습니다. 분리 서비스 모드(
dev:full)는 모든 서비스가 같은 파일에 쓰려고 해서 충돌할 수 있으니, 로컬 DuckDB 모드는 주로pnpm dev단일 프로세스 시나리오 또는 테스트용으로 사용하세요. :memory:모드는 프로세스 종료 시 데이터가 사라집니다.
백엔드별 동작 차이
대부분의 라우터는 두 백엔드에서 동일한 결과를 반환합니다. 두 가지 미세한 차이가 있습니다.
1. 즉석 SQL 실행 (query.execute) — 백엔드별 방언
query.execute는 사용자가 입력한 SQL을 직접 실행합니다. 라우터가
backend.kind를 보고 두 방언 중 하나로 스코핑합니다:
실행 전에는 읽기 전용 쿼리인지 검증하고, 현재 프로젝트 데이터만 조회하도록
project_id 조건을 강제합니다. INSERT, UPDATE, DELETE, DDL은 실행할 수 없습니다.
- ClickHouse 백엔드: 기존 그대로
{projectId:String}named placeholderFINAL유지.dateDiff,JSONExtractString,argMax등 ClickHouse 함수를 사용할 수 있습니다.
- MotherDuck 백엔드: 입력 SQL에서
FINAL은 자동 제거,project_id는 adapter가 escape된 literal로 치환. DuckDB 표준 SQL을 작성해야 합니다 (date_diff,json_extract_string,arg_max등).
즉, 같은 입력 SQL이 두 백엔드에서 동일하게 동작하지 않습니다.
백엔드를 바꾸면 SQL 탐색기에 저장된 쿼리도 함께 점검해야 합니다.
라우터 응답 shape(columns/rows/rowCount/elapsed)은 동일합니다.
두 백엔드 공통으로 결과는 최대 10,000행으로 제한되며, MotherDuck 백엔드에서는 30초 타임아웃이 적용됩니다.
2. funnel.funnelTrend의 bucket key (edge case)
funnelTrend는 device의 first_event 시각으로 기간 버킷을 결정합니다.
두 백엔드 모두 "funnel에 포함된 step 이벤트들 중 가장 이른 시각"을
바스킷 키로 씁니다. 따라서 device가 step2를 step1보다 먼저 발생시킨
edge case에서 양쪽 모두 step2의 시각으로 버킷팅합니다(funnel level은
여전히 step1→stepN 순서를 요구함).
MotherDuck로 전환하기
기존 ClickHouse 운영 환경에서 MotherDuck로 옮기려면:
-
MotherDuck 스키마 생성
MOTHERDUCK_TOKEN=... MOTHERDUCK_DATABASE=... \ pnpm --filter @reopt/db db:motherduck:init -
데이터 백필 (프로젝트 단위)
MOTHERDUCK_TOKEN=... MOTHERDUCK_DATABASE=... \ CLICKHOUSE_URL=... CLICKHOUSE_USER=... CLICKHOUSE_PASSWORD=... \ pnpm --filter @reopt/db db:motherduck:backfill-from-clickhouse \ --project-id <projectId> [--since 2026-01-01]이벤트 / 프로필 / 세션 / device_first_seen / daily_active_devices를 모두 옮깁니다.
--since로 증분 백필도 가능합니다. -
트래픽 전환
Vercel 환경변수에서
ANALYTICS_BACKEND=motherduck로 바꾸고 재배포합니다. ingest 파이프라인이 즉시 MotherDuck에 쓰기 시작합니다. -
벤치마크로 응답 시간 비교 (권장)
ANALYTICS_BACKEND=clickhouse pnpm --filter @reopt/trpc bench:retention ANALYTICS_BACKEND=motherduck MOTHERDUCK_TOKEN=... MOTHERDUCK_DATABASE=... \ pnpm --filter @reopt/trpc bench:retention
연결과 성능
MotherDuck 어댑터는 환경에 따라 네이티브 @duckdb/node-api(로컬) 또는
pg.Pool(MotherDuck Postgres wire endpoint, 프로덕션/Vercel)을 자동으로 선택합니다.
두 모드 모두 같은 쿼리 코드가 동작하며, 연결이 실패하면 자동으로 재시도합니다.
- 벌크 삽입:
insertEvents()는 500행 단위 multi-row VALUES INSERT로 배칭합니다. - 리소스 제한: 네이티브 모드에서
DUCKDB_MEMORY_LIMIT(기본 512MB)과DUCKDB_THREADS(기본 4)를 적용합니다. 잘못된 값은 무시되고 DuckDB 기본값을 사용합니다. - 스키마 관리: DDL은
packages/db/src/analytics/duck-schema.ts에 한 번만 정의되어 있고, CLI 부트스트랩(db:motherduck:init)과 런타임 첫 연결 시 모두 이 파일을 참조합니다.
주의할 점
- MotherDuck Lite 플랜은 10GB storage / 10시간 컴퓨트/월 한도가 있습니다.
- 운영 트래픽이 한도를 초과하면 분석 라우터가 실패할 수 있습니다.
- 저장된 SQL은 백엔드를 바꿀 때 함께 마이그레이션해야 합니다.
MOTHERDUCK_LOCAL_PATH=:memory:는 프로세스 종료 시 데이터가 사라집니다. production에서는MOTHERDUCK_TOKEN과MOTHERDUCK_DATABASE를 사용하세요.- 로컬 DuckDB 파일은 한 번에 한 프로세스만 write할 수 있습니다.
다음 단계
- 수집 파이프라인은 데이터 수집과 거버넌스
- 직접 SQL 화면은 비교, 브레이크다운, 쿼리