다국어 패턴 비교 테스트
새 Article 생성📚 이 페이지는 다섯 가지 다국어 패턴을 비교합니다:
- Pattern 1 (Article1): Mobility gem - ORM 수준에서 다국어 지원
- Pattern 2 (Article2): 수동 구현 - has_many :translations 패턴
- Pattern 3 (Article3): Globalize 스타일 - 메인 테이블 + translations (자동 fallback)
- Pattern 4 (Article4): JSON 컬럼 - 단일 TEXT 컬럼에 JSON 저장 (추가 테이블 없음)
- Pattern 5 (Article5): Lingo.dev API - 원본 저장 + AI 자동 번역 (캐싱)
🔤 Pattern 1: Mobility Gem
mobility_string_translations & mobility_text_translations
아직 생성된 Article1이 없습니다.
🗂️ Pattern 2: 수동 구현
app23_article2_translations
아직 생성된 Article2가 없습니다.
🌐 Pattern 3: Globalize 스타일
app23_article3s + app23_article3_translations
아직 생성된 Article3가 없습니다.
📦 Pattern 4: JSON 컬럼
title_translations (TEXT/JSON)
아직 생성된 Article4가 없습니다.
🤖 Pattern 5: Lingo.dev API
원본 저장 + AI 자동 번역 (캐싱)
아직 생성된 Article5가 없습니다.
📊 패턴 비교 요약
| 항목 | Mobility (Article1) | 수동 구현 (Article2) | Globalize 스타일 (Article3) | JSON 컬럼 (Article4) | Lingo.dev API (Article5) |
|---|---|---|---|---|---|
| 테이블 구조 | mobility_*_translations (공용) | app23_article2_translations (전용) | 메인 테이블 + app23_article3_translations | 메인 테이블만 (추가 테이블 없음) | 원본 + translations_cache (JSON) |
| 데이터 저장 위치 | 모든 언어 → 공용 테이블 | 모든 언어 → translations 테이블 | 기본 언어 → 메인 테이블 다른 언어 → translations 테이블 |
모든 언어 → TEXT 컬럼 (JSON) | 원본만 저장, 번역은 API → 캐시 |
| 접근 방식 | article1.title (자동) | article2.title_for(:ko) (수동) | article3.title_for_locale(:ko) | article4.title_for(:ko) | article5.title_for_locale(:en) |
| Fallback | 설정 필요 | 직접 구현 | 자동 (메인 테이블) | 첫 번째 값으로 fallback | 원본으로 fallback |
| 추가 테이블 | 공용 2개 | 모델당 1개 | 모델당 1개 | 없음! | 없음! |
| 번역 방식 | 수동 입력 | 수동 입력 | 수동 입력 | 수동 입력 | AI 자동 번역! |
🔤 Pattern 1: Mobility
✅ 장점
- 코드가 간결함 (
article.title로 바로 접근) - I18n.locale 자동 감지
- Rails 8 호환 (Globalize와 달리)
- Fallback 체인 설정 가능
- 쿼리 최적화 내장 (eager loading)
- 여러 백엔드 지원 (Table, KeyValue, JSON 등)
❌ 단점
- gem 의존성 추가
- 공용 테이블 사용 → 데이터 분리 어려움
- 마이그레이션 시 모든 모델 영향
- 디버깅이 어려울 수 있음 (내부 동작 숨김)
- 복잡한 쿼리 시 성능 이슈 가능
📌 적합한 경우
- 빠른 개발이 필요할 때
- 다국어 모델이 많을 때
- gem 의존성이 괜찮을 때
🗂️ Pattern 2: 수동 구현
✅ 장점
- gem 의존성 없음 (순수 Rails)
- 완전한 제어권 (커스터마이징 자유)
- 모든 언어를 동등하게 취급
- 명시적인 코드 → 이해하기 쉬움
- 모델별 독립적인 테이블 → 격리됨
- 테스트 작성이 직관적
❌ 단점
- 보일러플레이트 코드 많음
- 매번 translation 모델 생성 필요
- Fallback 직접 구현해야 함
- N+1 쿼리 주의 필요
- locale 처리 로직 중복 가능
📌 적합한 경우
- gem 의존성을 피하고 싶을 때
- 특수한 다국어 로직이 필요할 때
- 완전한 제어가 필요할 때
🌐 Pattern 3: Globalize 스타일
✅ 장점
- 기본 언어 조회 시 JOIN 불필요 → 빠름
- 자연스러운 fallback (번역 없으면 메인 값)
- 기존 테이블에 다국어 추가 용이
- 기본 언어만 필요한 쿼리가 효율적
- 데이터 일관성 (메인 값 항상 존재)
❌ 단점
- "기본 언어" 개념 필요 (설계 복잡)
- 기본 언어 변경 시 마이그레이션 필요
- 데이터가 두 곳에 분산
- 모든 언어 조회 시 로직 복잡
- Globalize gem은 Rails 8 미지원 → 수동 구현
📌 적합한 경우
- 기존 단일 언어 앱에 다국어 추가
- 기본 언어 조회가 대부분일 때
- 자연스러운 fallback이 필요할 때
📦 Pattern 4: JSON 컬럼
✅ 장점
- 추가 테이블 불필요 (가장 심플!)
- gem 의존성 없음
- 마이그레이션 간단 (컬럼 추가만)
- 데이터가 한 곳에 모임
- 백업/복원이 쉬움
- SQLite에서도 동작 (TEXT 사용)
❌ 단점
- 특정 locale로 검색 어려움
- DB 레벨 validation 불가
- Row 크기 증가
- 인덱싱 제한적
- 부분 업데이트 비효율적
📌 적합한 경우
- 심플함이 최우선일 때
- locale별 검색이 필요 없을 때
- 테이블 수를 최소화하고 싶을 때
- 앱별 독립 다국어가 필요할 때
🤖 Pattern 5: Lingo.dev
✅ 장점
- 번역 자동화 (AI가 번역!)
- 원본만 입력하면 끝
- 추가 테이블 불필요
- 번역 결과 캐싱 → 재호출 없음
- 고품질 LLM 기반 번역
- 새 언어 추가가 쉬움
❌ 단점
- 외부 API 의존성
- API 비용 발생
- 첫 번역 시 지연 (API 호출)
- 오프라인 환경 불가
- 번역 품질 통제 어려움
📌 적합한 경우
- 번역 리소스가 없을 때
- 빠르게 다국어 지원이 필요할 때
- 콘텐츠가 자주 변경될 때
- 전문 번역가 비용이 부담될 때
🗄️ 데이터 저장 구조 비교
Mobility
# mobility_string_translations
translatable_type: "App23::Article1"
translatable_id: 1
key: "title"
locale: "ko" → value: "제목"
locale: "en" → value: "Title"
locale: "ja" → value: "タイトル"
translatable_id: 1
key: "title"
locale: "ko" → value: "제목"
locale: "en" → value: "Title"
locale: "ja" → value: "タイトル"
※ 모든 모델이 같은 테이블 공유
수동 구현
# app23_article2s
id: 1 (컬럼 없음)
# app23_article2_translations
article2_id: 1
locale: "ko" → title: "제목"
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"
locale: "ko" → title: "제목"
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"
※ 모든 언어가 translations에 저장
Globalize 스타일
# app23_article3s (메인)
id: 1
title: "제목" ← 기본 언어(ko)
title: "제목" ← 기본 언어(ko)
# app23_article3_translations
article3_id: 1
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"
※ ko는 메인 테이블, 나머지만 translations
JSON 컬럼
# app23_article4s
id: 1
title_translations: {
"ko": "제목",
"en": "Title",
"ja": "タイトル"
}
title_translations: {
"ko": "제목",
"en": "Title",
"ja": "タイトル"
}
※ 추가 테이블 없이 JSON으로 모든 언어 저장
Lingo.dev API
# app23_article5s
id: 1
source_locale: "ko"
title: "제목" ← 원본
translations_cache: {
"en": {"title": "Title"},
"ja": {"title": "タイトル"}
} ← API 번역 캐시
source_locale: "ko"
title: "제목" ← 원본
translations_cache: {
"en": {"title": "Title"},
"ja": {"title": "タイトル"}
} ← API 번역 캐시
※ 원본만 저장, 번역은 API → 캐시
💡 패턴 선택 가이드
신규 프로젝트라면?
→ Mobility 추천. 코드가 간결하고 유지보수가 쉬움. gem 의존성이 부담되면 수동 구현 선택.
기존 앱에 다국어 추가라면?
→ Globalize 스타일 추천. 기존 데이터를 기본 언어로 유지하면서 점진적으로 다른 언어 추가 가능.
특수한 요구사항이 있다면?
→ 수동 구현 추천. 완전한 제어권으로 어떤 요구사항도 구현 가능.
성능이 중요하다면?
→ 기본 언어 조회가 많으면 Globalize 스타일, 다양한 언어 조회가 많으면 Mobility (eager loading 지원).
🎯 app1~app50 독립 앱이라면?
→ JSON 컬럼 강력 추천! 추가 테이블 없이 앱별 완전 독립. locale 검색이 필요 없다면 가장 심플한 선택.
🤖 번역 리소스가 없다면?
→ Lingo.dev API 추천! 원본만 입력하면 AI가 자동 번역. 빠르게 다국어 지원이 필요할 때 최적.