목차
번역 관리는 세 가지 시스템을 동기화 상태로 유지하려고 시도하기 전까지는 단순해 보입니다: 개발자가 코드를 작성하는 Git 리포지토리, 번역가가 작업하는 데이터베이스, 그리고 앱이 런타임에 번역을 가져오는 CDN. 한 시스템에서 키를 변경하면 나머지 두 시스템도 알아야 합니다 — 신뢰할 수 있게, 빠르게, 그리고 데이터 손실 없이.
이것이 동기화 엔진을 구축하여 해결하려 한 문제입니다. 이 글에서는 아키텍처, 메시지 유형, 충돌 감지 시스템, 그리고 이 모든 것을 작동시키는 신뢰성 보장에 대해 설명합니다.
동기 번역 워크플로우의 문제점
Better i18n 개발 초기에는 번역 동기화가 동기 방식이었습니다. 개발자가 코드를 푸시하면, 웹훅 핸들러가 인라인으로 변경 사항을 처리하고, 데이터베이스를 업데이트하고, CDN 파일을 재생성하고, 응답을 반환했습니다. 작동했습니다 — 그러다가 작동하지 않게 되었습니다.
실패 모드는 예측 가능했습니다:
- 타임아웃. 5,000개의 키가 있는 리포지토리는 diff 처리에 시간이 걸립니다. GitHub 웹훅에는 10초 타임아웃이 있습니다. 대규모 프로젝트에서 동기화가 자동으로 실패했습니다.
- 부분 업데이트. 데이터베이스가 업데이트된 후 CDN 업로드가 실패하면 번역이 동기화되지 않은 상태가 됩니다. 누군가가 수동으로 재동기화를 트리거할 때까지 사용자는 오래된 콘텐츠를 보게 됩니다.
- 가시성 없음. 동기화가 실패하면 무슨 일이 있었는지 기록이 없었습니다. 디버깅하려면 서버 로그를 읽고 타임스탬프를 상관시켜야 했습니다.
트리거를 작업에서 분리하고, 자동 재시도를 제공하며, 모든 작업에 대한 완전한 가시성을 제공하는 아키텍처가 필요했습니다.
Cloudflare Queues 도입
동기화 엔진의 백본으로 Cloudflare Queues를 선택했습니다. Queues는 내구성 있고 순서가 있는 메시지 전달을 최소 한 번 시맨틱으로 제공합니다 — 정확히 필요한 것이었습니다.
아키텍처는 간단합니다:
GitHub Webhook → API Handler → Queue (메시지 enqueue) → Worker (메시지 처리)
↓
Activity Log + Database + CDN
API 핸들러는 최소한의 작업을 수행합니다: 웹훅 유효성 검사, REPO_PUSH_SYNC 메시지 enqueue, 200 반환. 실제 처리는 큐 소비자에서 비동기적으로 발생합니다 — 메시지를 가져와 실행하는 Cloudflare Worker입니다.
이 분리는 세 가지 즉각적인 이점이 있습니다:
- 웹훅 응답이 빠릅니다. 대규모 리포지토리도 더 이상 타임아웃이 없습니다.
- 실패가 자동으로 재시도됩니다. 워커가 충돌하거나 API 호출이 실패하면 메시지가 지수 백오프로 재전달됩니다.
- 작업이 관찰 가능합니다. 모든 메시지는 구조화된 활동 로그를 생성합니다.
10가지 메시지 유형, 하나의 소비자
동기화 엔진은 각각 고유한 핸들러를 가진 10가지 서로 다른 메시지 유형을 처리합니다:
동기화 작업:
SYNC_START— 전체 또는 증분 GitHub 동기화. 파일을 가져오고, 키를 비교하고, 데이터베이스를 업데이트하며, 선택적으로 새 번역으로 풀 리퀘스트를 생성합니다.REPO_PUSH_SYNC— 푸시 웹훅 이벤트를 위한 최적화된 경로. 푸시에서 변경된 파일만 처리하므로 증분 동기화가 거의 즉각적입니다.
CDN 작업:
CDN_SETUP— 프로젝트가 CDN을 연결할 때 초기 매니페스트와 빈 언어 파일을 생성합니다.CDN_UPLOAD— 단일 JSON 번역 파일을 R2 스토리지에 씁니다.CDN_MERGE— 새 번역을 기존 CDN 파일에 병합합니다. 부분 게시에 중요합니다 — 변경되지 않은 번역을 제거하지 않고 새 번역을 추가하려 합니다.CDN_CLEANUP— 프로젝트의 모든 R2 파일을 삭제합니다. 프로젝트 삭제 또는 사용자가 처음부터 시작하려 할 때 사용됩니다.
AI 작업:
AI_CONTEXT_ANALYSIS— Firecrawl을 사용하여 프로젝트의 웹사이트를 스크래핑한 다음 콘텐츠를 Gemini에 공급하여 번역 컨텍스트 모델을 구축합니다. 이 컨텍스트는 기계 번역이 업계별 용어를 이해하도록 돕습니다.REPO_ANALYSIS— GitHub 리포지토리를 스캔하여 프레임워크(React, Next.js, Flutter 등)를 감지하고, 기존 번역을 추출하고, 용어 용어집을 구축합니다.
게시:
PUBLISH_BATCH— 번역 워크플로우의 마지막 단계. 승인된 번역을 가져와 CDN(즉각적인 가용성을 위해)과 GitHub(버전 제어를 위해) 모두에 푸시합니다. 이는 원자적 작업입니다 — 어느 쪽 쓰기가 실패하면 전체 게시가 재시도됩니다.
용어집:
GLOSSARY_SYNC— DeepL과 용어 용어집을 동기화합니다. "workspace"가 프랑스어로 항상 "espace de travail"로 번역되어야 한다고 정의하면, 이 메시지는 DeepL의 용어집이 업데이트되어 모든 향후 기계 번역이 일관성을 유지하도록 합니다.
각 메시지 유형은 격리되어 있습니다. CDN_UPLOAD의 실패가 SYNC_START를 차단하지 않습니다. 느린 AI_CONTEXT_ANALYSIS가 PUBLISH_BATCH를 지연시키지 않습니다. 이 격리가 엔진 신뢰성의 핵심입니다.
작업 시스템
메시지는 저수준입니다. 작업은 사용자와 시스템이 상호 작용하는 고수준 워크플로우입니다. 동기화 엔진은 12가지 작업 유형을 지원합니다:
| 작업 유형 | 트리거 | 생성되는 메시지 |
|---|---|---|
| initial_import | 프로젝트 설정 | SYNC_START, CDN_SETUP |
| incremental_sync | 푸시 웹훅 | REPO_PUSH_SYNC, CDN_MERGE |
| full_sync | 수동 트리거 | SYNC_START, CDN_UPLOAD (언어당) |
| source_sync | 소스 언어 변경 | SYNC_START |
| bulk_translate | 배치 번역 요청 | 다중 CDN_UPLOAD |
| publish | 단일 언어 게시 | PUBLISH_BATCH, CDN_UPLOAD |
| batch_publish | 다중 언어 게시 | 다중 PUBLISH_BATCH |
| cdn_upload | 직접 CDN 쓰기 | CDN_UPLOAD |
| cdn_merge | 부분 CDN 업데이트 | CDN_MERGE |
| cdn_setup | CDN 초기화 | CDN_SETUP |
| cdn_cleanup | 프로젝트 정리 | CDN_CLEANUP |
| glossary_sync | 용어집 업데이트 | GLOSSARY_SYNC |
단일 작업이 여러 메시지를 생성할 수 있습니다. 예를 들어, 8개 언어가 있는 프로젝트에서의 full_sync 작업은 1개의 SYNC_START 메시지와 각 언어 파일당 하나씩 8개의 CDN_UPLOAD 메시지를 생성합니다. 작업은 모든 메시지에 걸쳐 집계 상태를 추적합니다.
45개 이상의 활동 작업: 구조화된 관찰 가능성
모든 메시지 핸들러는 진행되면서 구조화된 활동 작업을 기록합니다. 이것은 자유 텍스트 로그 줄이 아닙니다 — 디버깅 경험과 실시간 UI 모두를 구동하는 타입이 있고 구조화된 이벤트입니다.
일반적인 SYNC_START 흐름은 다음과 같은 활동 추적을 생성합니다:
SYNC_STARTED → FETCH_FILES (GitHub에서 번역 파일 가져오기) → FILES_FETCHED (12개 파일 발견) → COMPARE_KEYS (데이터베이스와 diff 비교) → KEYS_ADDED (47개의 새 키) → KEYS_REMOVED (3개의 더 이상 사용되지 않는 키) → KEYS_UPDATED (12개의 수정된 값) → UPDATE_DATABASE (변경 사항 저장) → PR_GENERATION_STARTED (번역 PR 생성) → PR_CREATED (PR #142 열림) → SYNC_COMPLETED (소요 시간: 3.2초)
45개 이상의 고유한 작업 유형으로 모든 작업에 대한 세분화된 가시성을 얻을 수 있습니다. 무언가 실패하면 마지막으로 기록된 작업이 파이프라인이 어디서 멈추었는지와 어떤 데이터가 이미 처리되었는지를 정확히 알려줍니다.
이 활동 작업들은 동기화 기록 UI도 구동합니다. 팀은 서버 로그를 건드리지 않고 지금까지 실행된 모든 동기화, 수행한 작업, 소요 시간, 성공 여부를 볼 수 있습니다.
충돌 감지 및 해결
충돌은 모든 동기화 시스템에서 가장 어려운 문제입니다. 두 사람이 동일한 번역 키를 편집합니다 — 한 사람은 코드베이스에서, 한 사람은 번역 UI에서. 누가 이기나요?
저희 답변: 아무도 자동으로 이기지 않습니다. 동기화 엔진은 충돌을 감지하고 인간의 해결을 위해 표면화합니다.
감지
COMPARE_KEYS 중에 엔진은 들어오는 각 키를 데이터베이스와 대조합니다. 마지막 성공적인 동기화 이후 리포지토리와 데이터베이스 모두에서 키가 수정된 경우 충돌로 표시됩니다. 엔진은 두 값 모두를 수정 타임스탬프와 함께 저장합니다.
해결
충돌은 전체 컨텍스트와 함께 대시보드에 나타납니다:
- 소스 값 (리포지토리에서)
- 데이터베이스 값 (번역 UI에서)
- 마지막 동기화 값 (공통 조상)
- 각 수정에 대한 타임스탬프
사용자는 소스 값 유지, 데이터베이스 값 유지, 수동 병합 작성 중 선택하여 충돌을 하나씩 또는 일괄로 해결할 수 있습니다. 모든 해결은 활동 작업으로 기록됩니다.
이 접근 방식은 번역 워크플로우에서 가장 일반적인 데이터 손실 시나리오를 방지합니다: 개발자의 코드 푸시가 번역가의 세심하게 검토된 작업을 자동으로 덮어쓰는 것.
신뢰성 보장
동기화 엔진은 네 가지 신뢰성 원칙을 기반으로 설계되었습니다:
최소 한 번 전달. Cloudflare Queues는 모든 메시지가 최소 한 번 전달되도록 보장합니다. 메시지는 워커 재시작, 배포, 인프라 장애를 견딥니다.
멱등 핸들러. 메시지가 두 번 이상 전달될 수 있으므로 모든 핸들러는 멱등입니다. 동일한 콘텐츠로 CDN_UPLOAD를 재처리하면 동일한 결과가 나타납니다. SYNC_START를 재처리하면 현재 데이터베이스 상태와 비교하므로 중복 동기화는 사실상 노옵(no-op)입니다.
순서 처리. 동일 프로젝트의 메시지는 순서대로 처리됩니다. CDN_MERGE는 항상 그것을 생성한 SYNC_START 이후에 실행됩니다. 이는 데이터베이스가 새 키를 반영하기 전에 CDN 파일이 업데이트되는 경쟁 조건을 방지합니다.
백오프를 사용한 자동 재시도. 실패한 메시지는 지수 백오프로 재시도됩니다. API 속도 제한, 네트워크 오류, 일시적인 R2 불가용성과 같은 일시적 오류는 인간 개입 없이 스스로 해결됩니다. 영구적 오류(잘못된 데이터, 누락된 권한)는 기록되고 대시보드에 표면화됩니다.
팀에게 의미하는 바
동기화 엔진은 백그라운드에서 실행됩니다. GitHub 리포지토리를 연결하면 동기화가 그냥 작동합니다. 코드를 푸시하면 몇 초 안에 번역이 업데이트됩니다. 번역을 승인하면 CDN에 게시되고 리포지토리에 원자적으로 커밋됩니다.
무언가 잘못되면 — 분산 시스템에서는 항상 무언가 잘못됩니다 — 엔진은 재시도하고, 기록하고, 문제를 표면화합니다. 자동 실패 없음. 일관성 없는 상태 없음. 손실된 번역 없음.
그것이 올바르게 수행된 비동기 처리의 약속입니다: 팀은 번역에 집중하고, 인프라가 나머지를 처리합니다.