다국어 패턴 비교 테스트

새 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: "タイトル"

※ 모든 모델이 같은 테이블 공유

수동 구현

# app23_article2s
id: 1 (컬럼 없음)
# app23_article2_translations
article2_id: 1
locale: "ko" → title: "제목"
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"

※ 모든 언어가 translations에 저장

Globalize 스타일

# app23_article3s (메인)
id: 1
title: "제목" ← 기본 언어(ko)
# app23_article3_translations
article3_id: 1
locale: "en" → title: "Title"
locale: "ja" → title: "タイトル"

※ ko는 메인 테이블, 나머지만 translations

JSON 컬럼

# app23_article4s
id: 1
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 번역 캐시

※ 원본만 저장, 번역은 API → 캐시

💡 패턴 선택 가이드

신규 프로젝트라면?

→ Mobility 추천. 코드가 간결하고 유지보수가 쉬움. gem 의존성이 부담되면 수동 구현 선택.

기존 앱에 다국어 추가라면?

→ Globalize 스타일 추천. 기존 데이터를 기본 언어로 유지하면서 점진적으로 다른 언어 추가 가능.

특수한 요구사항이 있다면?

→ 수동 구현 추천. 완전한 제어권으로 어떤 요구사항도 구현 가능.

성능이 중요하다면?

→ 기본 언어 조회가 많으면 Globalize 스타일, 다양한 언어 조회가 많으면 Mobility (eager loading 지원).

🎯 app1~app50 독립 앱이라면?

→ JSON 컬럼 강력 추천! 추가 테이블 없이 앱별 완전 독립. locale 검색이 필요 없다면 가장 심플한 선택.

🤖 번역 리소스가 없다면?

→ Lingo.dev API 추천! 원본만 입력하면 AI가 자동 번역. 빠르게 다국어 지원이 필요할 때 최적.