도현우
전체 프로젝트
회사 프로젝트· 벤치링크· 2024 — 2026

Benchlink PMS

admin-v1 → v2 → v3 — Firebase에서 GCP Spanner로 3세대 진화

역할: 테크 리드 · 마이그레이션 설계 · AI 워크플로우 도입
Next.jsTypeScriptGCP SpannerFirebaseCloud FunctionsAWSClaude Code
회사 코드 (NDA) — 위 핵심 코드 스니펫과 아키텍처로 갈음합니다
Headline

12개 도메인 · 460M rows · 30건 배치 — 3개월 마이그레이션을 1개월에

벤치링크 Expert 매칭 플랫폼의 PMS(어드민) 시스템을 React → Next.js+Firebase → Next.js+Spanner로 3단계 마이그레이션. AI 코딩 워크플로우(HDD)를 직접 설계해 3개월 분량의 작업을 1개월에 완수했습니다.

0
마이그레이션 도메인
0M
Expert rows
0
AI 도입 개발 가속
0
AI 마이그레이션 배치
Problem
  • · Firebase Data Connect의 GraphQL 자동생성 스키마로는 복잡한 조인·서브쿼리·조건부 필터링 표현이 불가
  • · AI 기능 개발(임베딩 처리/응답 생성)을 위한 Elasticsearch 연계 시 마이크로 쿼리 설계와 Raw SQL 작성에 제약 발생
  • · Expert 테이블 460M rows 규모에서 인덱스 정밀 제어가 필요했으나 Firebase Storage·Firestore·Cloud Functions 의존도로 결합도 높음
Action
  • · Firebase → Spanner 마이그레이션 마스터 플랜 작성 — 12개 도메인을 21+개 배치로 원자적 분할
  • · Spanner ORM 부재 환경에서 도메인별 Raw SQL 빌더와 공통 whereClause·params 모듈 직접 설계
  • · Firebase Storage(서버사이드 Admin SDK 업로드 전환), Firestore(분산 락·아웃리치 제한 유지), Auth(JWT 자체 인증으로 교체) 등 컴포넌트별 마이그레이션 전략 수립
  • · Claude Code CLI + 자체 설계한 HDD(Harness-Driven Development) 방법론으로 모든 배치 작업 진행 — 컨텍스트 소실에 강한 상태머신 구조
Result
  • · 12개 도메인 100% 마이그레이션 완료 (Auth, Lookup, PmsUser, Client, Expert, Project, Topic/Query, Interview, Invoice 등)
  • · 예상 3개월 → 1개월 단축 (3배 가속)
  • · Claude Code CLI $600 투자로 ROI 약 300%
  • · 사내 Vibe Coding · HDD 방법론 표준화 및 전파

Firebase → GCP Spanner 마이그레이션

Next.js (App Router)
프론트엔드
Cloud Spanner
주 DB · 인터리브 인덱스 + STORING 절 커버링
Cloud Functions
서버리스 비즈니스 로직
Cloud Tasks
비동기 파이프라인
Firebase Storage
Admin SDK 서버 업로드 (보안 강화)
Firestore
분산 락 · 아웃리치 제한용 유지
Firebase Auth 제거 → JWT 자체 인증 / Firebase Analytics만 그대로 유지
Core Code

핵심 코드 스니펫

Spanner Raw SQL — Expert 검색 (실제 운영 코드)
// experts.ts — 서버 액션
const [rows] = await db.run({
  sql: `
    SELECT
      e.id,
      e.full_name,
      e.email AS primaryEmail,
      e.tier,
      (SELECT COUNT(*) FROM interview i
        WHERE i.expert_id = e.id
          AND i.interview_status = 'Completed') AS completedInterviews,
      (SELECT ee.company_name FROM expert_experience ee
        WHERE ee.id = e.id
        ORDER BY ee.start_date DESC LIMIT 1) AS latestCompany,
      (SELECT CAST(ee.start_date AS STRING) FROM expert_experience ee
        WHERE ee.id = e.id
        ORDER BY ee.start_date DESC LIMIT 1) AS latestStartDate
    FROM expert e
    LEFT JOIN recruited_by rb ON rb.expert_id = e.id
    LEFT JOIN \`user\` u ON u.id = rb.user_id
    ${whereClause}
    LIMIT @limit OFFSET @offset
  `,
  params: spannerParams,
});
ORM 부재 환경에서 도메인별 SQL 빌더로 직접 작성. whereClause·params 공통 모듈로 중복 제거.
AI 마이그레이션 배치 상태 영속화
---
batch_id: 21A
domain: Expert
status: COMPLETED
assigned_persona: Backend
files_touched:
  - apps/web/app/actions/experts.ts
  - apps/web/lib/spanner/where-builder.ts
  - apps/web/api/expert-suggestion-results/[id]/route.ts
dynamic_rules:
  - "expert.full_name 은 SELECT 시 항상 포함"
  - "Spanner는 \`user\` 테이블명 백틱 필수"
ddl_changes:
  - "CREATE INDEX idx_expert_status_created ON Expert(status, created_at DESC) STORING (full_name, email, tier)"
---
각 배치는 docs/states/batch_N.md로 영속화 → Claude 컨텍스트가 소실되어도 다음 세션에서 100% 복원 가능.
다른 프로젝트 보기