다국어 패턴 비교 테스트

새 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가 자동 번역. 빠르게 다국어 지원이 필요할 때 최적.