블로그

LunariaJS 고급 설정: 기업급 현지화 관리 솔루션 구축하기

LunariaJS의 고급 설정 옵션과 사용자 정의 현지화 전략을 탐구하고, 다중 저장소 지원, 사용자 정의 상태 규칙, 복잡한 파일 구조 처리 등 기업급 시나리오의 솔루션을 배웁니다.

LibDoc Team 2026년 3월 6일 LunariaJS 칼럼 69 분 읽기
#LunariaJS #고급 설정 #기업급 #사용자 정의 전략 #Monorepo

LunariaJS 고급 설정: 기업급 현지화 관리 솔루션 구축하기

이전 글에서는 @lunariajs/core의 소스 코드 아키텍처를 깊이 있게 분석했습니다. 오늘은 이 지식을 실천에 적용하여 LunariaJS의 고급 설정 옵션을 탐구하고, 기업급 시나리오의 복잡한 현지화 관리 요구를 해결해 보겠습니다.

💡 공식 문서: LunariaJS 한국어 문서 - 고급 설정

기업급 시나리오 도전

전형적인 기업급 요구

기업급 프로젝트는 보통 다음 현지화 도전에 직면합니다:

도전설명영향
다중 저장소 관리코드가 여러 Git 저장소에 분산번역 상태를 통합 추적하기 어려움
복잡한 파일 구조모듈마다 다른 디렉토리 구조설정이 복잡하고 오류 발생 쉬움
사용자 정의 워크플로우기존 개발 프로세스에 맞춤 필요표준 설정으로 요구 충족 불가
권한 제어팀마다 다른 접근 권한세밀한 권한 관리 필요
대규모 파일수백에서 수천 개의 번역 파일성능이 병목
다중 원본 언어모듈마다 다른 원본 언어상태 계산 로직 복잡

학습 목표

이 글을 통해 다음을 배웁니다:

  • ✅ 복잡한 다중 저장소 프로젝트 설정
  • ✅ 사용자 정의 상태 판정 규칙 설계
  • ✅ 대규모 프로젝트 성능 최적화
  • ✅ 기업 도구 및 시스템 통합
  • ✅ 고급 보안 및 권한 제어 구현

고급 설정 옵션

사용자 정의 상태 규칙

LunariaJS는 번역 상태 판정 로직을 사용자 정의할 수 있습니다:

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 사용자 정의 상태 판정
  statusRules: {
    // "오래됨" 판정 조건 사용자 정의
    outdated: {
      // 일수 임계값: 원본 파일이 번역보다 며칠 새로워야 오래된 것으로 판정
      dayThreshold: 7,  // 기본값 0, 여기서는 7일로 설정

      // 또는 사용자 정의 함수 사용
      customCheck: async (source, translation) => {
        // 원본 파일에 중대한 변경이 있는지 확인
        const changes = await getSignificantChanges(source.path);
        return changes.length > 0;
      },
    },

    // "누락" 판정 조건 사용자 정의
    missing: {
      // 특정 파일 패턴 무시
      ignorePatterns: ['**/internal/**', '**/draft/**'],

      // 또는 사용자 정의 함수 사용
      customCheck: (sourcePath, translationPath) => {
        // 번역 필요 여부 확인 (일부 파일은 번역 불필요)
        const content = fs.readFileSync(sourcePath, 'utf-8');
        return !content.includes('<!-- no-translate -->');
      },
    },
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

복잡한 경로 매핑

복잡한 프로젝트 구조 처리:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  files: [
    // 문서 파일
    {
      sourcePath: 'docs/guides/{slug}.md',
      localizationPath: 'docs/{lang}/guides/{slug}.md',
      include: ['**/*.md'],
      exclude: ['**/draft/**'],
    },

    // API 참조 문서
    {
      sourcePath: 'docs/api/{category}/{slug}.md',
      localizationPath: 'docs/{lang}/api/{category}/{slug}.md',
    },

    // UI 번역 파일 (JSON)
    {
      sourcePath: 'src/locales/en/{module}.json',
      localizationPath: 'src/locales/{lang}/{module}.json',
    },

    // 설정 파일
    {
      sourcePath: 'config/en/{slug}.yaml',
      localizationPath: 'config/{lang}/{slug}.yaml',
    },
  ],
});

조건화 처리

조건에 따라 동적으로 설정 조정:

// lunaria.config.ts
const isProduction = process.env.NODE_ENV === 'production';
const isCI = process.env.CI === 'true';

export default defineConfig({
  sourceLanguage: 'en',

  // 환경에 따라 언어 목록 조정
  languages: isProduction
    ? ['en', 'zh-cn', 'ja', 'ko', 'es', 'fr', 'de']
    : ['en', 'zh-cn'],  // 개발 환경에서는 두 언어만 추적

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',

      // CI 환경에서 초안 파일 제외
      exclude: isCI ? ['**/draft/**', '**/wip/**'] : [],
    },
  ],

  dashboard: {
    outputDir: isProduction ? 'dist/i18n-status' : 'public/i18n-status',
    title: isProduction ? 'Production i18n Status' : 'Development i18n Status',
  },
});

다중 저장소 현지화 관리

Monorepo 시나리오 설정

Monorepo 프로젝트의 경우 (pnpm workspaces 또는 Turborepo 사용):

my-monorepo/
├── packages/
│   ├── core/
│   │   ├── docs/
│   │   │   └── en/
│   │   └── i18n/
│   │       ├── zh-cn/
│   │       └── ja/
│   ├── cli/
│   │   ├── docs/
│   │   │   └── en/
│   │   └── i18n/
│   │       ├── zh-cn/
│   │       └── ja/
│   └── ui/
│       ├── docs/
│       │   └── en/
│       └── i18n/
│           ├── zh-cn/
│           └── ja/
├── lunaria.config.ts
└── package.json

설정 예시:

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';
import { glob } from 'glob';

// 모든 패키지 자동 발견
const packages = await glob('packages/*');

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 각 패키지에 독립적인 파일 설정 생성
  files: packages.flatMap(pkg => [
    {
      sourcePath: `${pkg}/docs/{slug}.md`,
      localizationPath: `${pkg}/i18n/{lang}/{slug}.md`,
      // 패키지 이름을 그룹으로 사용
      group: pkg.replace('packages/', ''),
    },
  ]),

  dashboard: {
    outputDir: 'docs/i18n-status',
    title: 'My Monorepo - Translation Status',

    // 패키지별 그룹화 표시
    groupBy: 'group',
  },
});

사용자 정의 현지화 전략

전략 인터페이스 설계

사용자 정의 현지화 전략의 인터페이스 정의:

// src/strategies/types.ts
import { TranslationStatus, FileInfo } from '@lunariajs/core';

/**
 * 현지화 전략 인터페이스
 */
export interface LocalizationStrategy {
  /** 전략 이름 */
  name: string;

  /** 전략 설명 */
  description: string;

  /**
   * 번역 상태 계산
   */
  calculateStatus(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ): Promise<TranslationStatus>;

  /**
   * 번역 필요 여부
   */
  needsTranslation?(
    source: FileInfo,
    context: StrategyContext
  ): Promise<boolean>;

  /**
   * 번역 우선순위 획득
   */
  getPriority?(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ): Promise<'high' | 'medium' | 'low'>;
}

사용자 정의 전략 구현

콘텐츠 복잡도 기반 전략 생성:

// src/strategies/content-complexity.ts
import { LocalizationStrategy, StrategyContext, FileInfo } from './types';

/**
 * 콘텐츠 복잡도 기반 전략
 * 원본 파일의 복잡도에 따라 "오래됨" 판정 기준을 동적으로 조정
 */
export const ContentComplexityStrategy: LocalizationStrategy = {
  name: 'content-complexity',
  description: '콘텐츠 복잡도에 따라 번역 상태 판정 기준을 조정',

  async calculateStatus(
    source: FileInfo,
    translation: FileInfo | null,
    context: StrategyContext
  ) {
    // 번역 파일이 존재하지 않으면 누락 반환
    if (!translation) {
      return { status: 'missing', reason: 'Translation file not found' };
    }

    // 콘텐츠 복잡도 계산
    const complexity = calculateComplexity(source.content);

    // 복잡도에 따라 일수 임계값 결정
    const dayThreshold = getDayThreshold(complexity);

    // 시간 차이 확인
    const sourceTime = source.lastModified.getTime();
    const translationTime = translation.lastModified.getTime();
    const daysDiff = (sourceTime - translationTime) / (1000 * 60 * 60 * 24);

    if (daysDiff > dayThreshold) {
      return {
        status: 'outdated',
        reason: `Source is ${daysDiff.toFixed(0)} days newer (threshold: ${dayThreshold})`,
        complexity,
      };
    }

    return {
      status: 'done',
      complexity,
    };
  },

  async getPriority(source: FileInfo, translation: FileInfo | null) {
    const complexity = calculateComplexity(source.content);

    if (complexity > 0.8) return 'high';
    if (complexity > 0.5) return 'medium';
    return 'low';
  },
};

/**
 * 콘텐츠 복잡도 계산 (0-1)
 */
function calculateComplexity(content: string): number {
  const factors = {
    // 코드 블록 수
    codeBlocks: (content.match(/```/g) || []).length / 10,

    // 링크 수
    links: (content.match(/\[.*?\]\(.*?\)/g) || []).length / 20,

    // 특수 구문
    specialSyntax: (content.match(/\{.*?\}/g) || []).length / 30,

    // 파일 길이
    length: content.length / 10000,
  };

  // 가중 평균
  return (
    factors.codeBlocks * 0.3 +
    factors.links * 0.2 +
    factors.specialSyntax * 0.3 +
    factors.length * 0.2
  );
}

/**
 * 복잡도에 따른 일수 임계값 획득
 */
function getDayThreshold(complexity: number): number {
  // 복잡도가 높을수록 임계값이 큼 (번역자에게 더 많은 시간 부여)
  if (complexity > 0.8) return 30;
  if (complexity > 0.5) return 14;
  return 7;
}

사용자 정의 전략 등록

// lunaria.config.ts
import { defineConfig } from '@lunariajs/core';
import { ContentComplexityStrategy } from './src/strategies/content-complexity';

export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 사용자 정의 전략 등록
  strategies: {
    // 기본 전략
    default: 'git-timestamp',

    // 파일 패턴별로 다른 전략 적용
    rules: [
      {
        pattern: 'docs/guides/**/*.md',
        strategy: 'content-complexity',
      },
      {
        pattern: 'docs/api/**/*.md',
        strategy: 'strict',  // API 문서는 엄격 모드 사용
      },
      {
        pattern: '**/changelog.md',
        strategy: 'lenient',  // 변경 로그는 관대한 모드 사용
      },
    ],

    // 사용자 정의 전략 등록
    custom: [ContentComplexityStrategy],
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

대규모 프로젝트 최적화

성능 병목 분석

대규모 프로젝트의 일반적인 성능 병목:

병목원인해결책
Git 작업 과다각 파일마다 Git 히스토리 조회Git 정보 일괄 조회
파일 I/O 과다파일을 하나씩 읽음병렬 읽기
설정 파싱 느림복잡한 glob 패턴glob 결과 캐시
대시보드 생성 느림대량 템플릿 렌더링점진적 렌더링

점진적 빌드 전략

변경된 파일만 처리:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 점진적 빌드 활성화
  incremental: {
    enabled: true,

    // 캐시 디렉토리
    cacheDir: '.lunaria/cache',

    // 캐시 유효 기간 (초)
    cacheTTL: 3600,

    // 전체 빌드 강제 조건
    forceRebuild: {
      // 설정 파일 변경 시 전체 빌드 강제
      configChanged: true,
      // 새 언어 추가 시 전체 빌드 강제
      languageAdded: true,
    },
  },

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',
    },
  ],
});

병렬 처리

병렬 처리로 빌드 속도 향상:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 병렬 처리 설정
  parallel: {
    enabled: true,

    // 워커 스레드 수 (기본값은 CPU 코어 수)
    workers: 4,

    // 배치당 처리 파일 수
    batchSize: 100,

    // 타임아웃 (밀리초)
    timeout: 60000,
  },

  files: [
    // ...
  ],
});

캐시 메커니즘

반복 빌드 속도 향상을 위한 캐시 설정:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  // 캐시 설정
  cache: {
    // Git 정보 캐시
    git: {
      enabled: true,
      ttl: 86400,  // 24시간
    },

    // 파일 콘텐츠 캐시
    content: {
      enabled: true,
      ttl: 3600,  // 1시간
    },

    // 상태 계산 캐시
    status: {
      enabled: true,
      ttl: 3600,  // 1시간
    },
  },

  files: [
    // ...
  ],
});

보안 및 권한

민감 파일 처리

추적에서 민감 파일 제외:

// lunaria.config.ts
export default defineConfig({
  sourceLanguage: 'en',
  languages: ['en', 'zh-cn', 'ja', 'ko'],

  files: [
    {
      sourcePath: 'docs/{slug}.md',
      localizationPath: 'i18n/{lang}/{slug}.md',

      // 민감 파일 제외
      exclude: [
        '**/internal/**',
        '**/.env*',
        '**/secrets/**',
        '**/admin/**',
      ],
    },
  ],

  // 전역 제외 규칙
  globalExclude: [
    '**/node_modules/**',
    '**/.git/**',
    '**/dist/**',
    '**/.env*',
    '**/credentials*',
  ],
});

접근 제어

대시보드에 접근 제어 추가:

// lunaria.config.ts
export default defineConfig({
  // ...기타 설정

  dashboard: {
    outputDir: 'public/i18n-status',
    title: 'Translation Status',

    // 접근 제어
    accessControl: {
      enabled: true,

      // 기본 인증
      basicAuth: {
        enabled: process.env.NODE_ENV === 'production',
        username: process.env.I18N_DASHBOARD_USER,
        password: process.env.I18N_DASHBOARD_PASS,
      },

      // IP 화이트리스트
      ipWhitelist: [
        '192.168.1.0/24',
        '10.0.0.0/8',
      ],

      // 또는 사용자 정의 인증 함수 사용
      customAuth: async (request) => {
        const token = request.headers.get('Authorization');
        return validateToken(token);
      },
    },
  },
});

기업 도구 통합

번역 관리 시스템 통합

전문 번역 관리 플랫폼과 통합:

// lunaria.config.ts
export default defineConfig({
  // ...기타 설정

  // 통합 설정
  integrations: {
    // Crowdin 통합
    crowdin: {
      enabled: true,
      projectId: process.env.CROWDIN_PROJECT_ID,
      apiToken: process.env.CROWDIN_API_TOKEN,

      // 동기화 설정
      sync: {
        direction: 'bidirectional',  // 'push' | 'pull' | 'bidirectional'
        interval: 3600,  // 동기화 간격 (초)
      },
    },

    // 또는 Transifex 사용
    transifex: {
      enabled: false,
      organization: 'your-org',
      project: 'your-project',
      apiToken: process.env.TRANSIFEX_API_TOKEN,
    },
  },
});

CMS 통합

콘텐츠 관리 시스템과 통합:

// lunaria.config.ts
export default defineConfig({
  // ...기타 설정

  integrations: {
    // Contentful CMS
    contentful: {
      enabled: true,
      spaceId: process.env.CONTENTFUL_SPACE_ID,
      accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,

      // 콘텐츠 매핑
      contentMapping: {
        'blogPost': 'posts/{slug}.md',
        'documentation': 'docs/{slug}.md',
      },
    },

    // 또는 Strapi 사용
    strapi: {
      enabled: false,
      apiUrl: process.env.STRAPI_API_URL,
      apiToken: process.env.STRAPI_API_TOKEN,
    },
  },
});

요약

이 글에서는 LunariaJS의 고급 설정과 기업급 응용을 탐구했습니다:

주제핵심 내용
사용자 정의 상태 규칙일수 임계값, 사용자 정의 검사 함수
복잡한 경로 매핑다중 파일 패턴, 다중 원본 언어
다중 저장소 관리Monorepo, 교차 저장소 의존성
사용자 정의 전략전략 인터페이스, 구현, 등록
성능 최적화점진적 빌드, 병렬 처리, 캐시
보안 권한민감 파일, 접근 제어, 감사
기업 통합TMS, CMS, 내부 도구

핵심 포인트:

  • 조건 설정을 유연하게 활용해 다양한 환경에 적응
  • 사용자 정의 전략으로 특정 비즈니스 요구 충족
  • 성능 최적화로 대규모 파일 처리
  • 보안 조치로 민감 정보 보호

다음 단계

다음 글에서는 완전한 기업급 실전을 진행하고, 처음부터 완전한 다국어 문서 플랫폼을 구축하여 본 시리즈의 모든 지식을 종합적으로 활용합니다.

기대해 주세요!


💡 추천 읽기: