최신 프런트엔드 프레임워크에서의 타입 안전한 국제화 구축
Olivia Novak
Dev Intern · Leapcell

소개
오늘날 상호 연결된 디지털 세계에서 웹 애플리케이션의 글로벌 도달 범위는 더 이상 사치가 아니라 필수입니다. 다양한 사용자 기반에 효과적으로 서비스를 제공하려면 애플리케이션은 사용자의 언어로 소통해야 합니다. 국제화(i18n)는 소프트웨어를 다양한 언어 및 지역에 맞게 조정하는 프로세스이며, 현대 프런트엔드 개발의 기본 측면이 되었습니다. 텍스트 문자열을 번역하는 것은 간단해 보일 수 있지만, 복잡한 프런트엔드 프레임워크에서 이 프로세스 전반에 걸쳐 타입 안전성을 보장하는 것은 고유한 문제를 제시합니다. 잘못된 키, 잘못된 매개변수 수 또는 일치하지 않는 반환 유형은 런타임 오류, 열악한 사용자 경험 및 유지 관리 오버헤드 증가로 이어질 수 있습니다. 이 글에서는 현대 프런트엔드 프레임워크와 TypeScript를 활용하여 강력하고 타입 안전한 i18n 솔루션을 구축하는 방법을 자세히 살펴보고, 애플리케이션을 더 안정적이고 유지 관리 가능하며 진정으로 글로벌하게 만들 것입니다.
핵심 개념
구현 세부 정보에 들어가기 전에 타입 안전한 국제화를 달성하는 데 관련된 핵심 개념을 명확하게 이해해 봅시다.
국제화(i18n): 엔지니어링 변경 없이 다양한 언어 및 지역에 맞게 조정할 수 있도록 소프트웨어를 설계 및 개발하는 프로세스입니다. 여기에는 다양한 통화, 날짜 형식, 숫자 형식 및 물론 텍스트 번역 처리가 포함됩니다.
지역화(l10n): 지역별 구성 요소를 추가하고 텍스트를 번역하여 국제화된 소프트웨어를 특정 지역 또는 언어에 맞게 조정하는 프로세스입니다.
메시지 키: 특정 번역된 텍스트 문자열을 참조하는 데 사용되는 고유 식별자입니다. 코드에 원시 텍스트를 포함하는 대신 이러한 키를 사용하며, 이를 통해 활성 로캘에 따라 번역된 값에 매핑됩니다. 예를 들어, home.welcomeMessage
는 영어에서는 "Welcome!"로, 프랑스어에서는 "Bienvenue !"로 매핑될 수 있습니다.
플레이스홀더/보간: 번역된 문자열에 삽입되는 동적 값입니다. 예를 들어, 영어 메시지 "Hello, {{userName}}!"는 프랑스어에서 "Bonjour, {{userName}} !"가 될 수 있습니다. 여기서 타입 안전성은 각 플레이스홀더에 대해 올바른 유형의 데이터가 전달되는지 확인하는 것을 의미합니다.
복수형 처리: 수량에 따라 다른 단어 형태를 처리합니다. 예를 들어, "1 item" 대 "2 items"입니다. 이는 언어마다 상당히 다르며, 강력한 i18n 솔루션은 이를 올바르게 처리하기 위한 메커니즘을 제공해야 합니다.
TypeScript: 정적 타이핑을 추가하는 JavaScript의 상위 집합입니다. i18n 메시지 및 함수에 대한 유형을 정의함으로써 TypeScript는 런타임이 아닌 컴파일 시간에 잠재적인 오류를 감지하여 코드 품질과 개발자 경험을 크게 향상시킬 수 있습니다.
타입 안전한 국제화의 원칙
타입 안전한 i18n의 핵심 원칙은 애플리케이션 코드와 번역 리소스 간의 계약이 유형 시스템에 의해 시행되도록 보장하는 것입니다. 이는 다음을 의미합니다:
- 유효한 메시지 키: 애플리케이션 코드에서 사용되는 모든 키가 번역 파일에 실제로 존재하도록 보장합니다.
- 올바른 플레이스홀더 유형: 플레이스홀더에 제공된 값이 번역 문자열에 정의된 예상 유형과 일치하는지 확인합니다.
- 정확한 복수형 인수: 복수형 함수가 올바른 수와 유형의 인수를 받는지 확인합니다.
React 및 TypeScript를 사용한 구현
일반적인 최신 프런트엔드 스택인 React 및 TypeScript를 사용하여 인기 있는 i18n 라이브러리인 react-i18next
를 활용하여 이러한 원칙을 설명해 보겠습니다.
먼저 번역 리소스를 정의해 보겠습니다. JSON 파일을 사용하지만 원칙은 다른 형식에도 적용됩니다.
public/locales/en/translation.json
:
{ "home": { "welcome": "Welcome, {{name}}!", "itemCount": { "one": "You have {{count}} item.", "other": "You have {{count}} items." }, "helloButton": "Say Hello" }, "common": { "cancel": "Cancel", "save": "Save" } }
public/locales/fr/translation.json
:
{ "home": { "welcome": "Bienvenue, {{name}} !", "itemCount": { "one": "Vous avez {{count}} article.", "other": "Vous avez {{count}} articles." }, "helloButton": "Dire bonjour" }, "common": { "cancel": "Annuler", "save": "Sauvegarder" } }
단계 1: react-i18next
초기화
i18next
및 react-i18next
를 사용하여 i18n
을 설정합니다.
i18n.ts
:
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-http-backend'; // 타입 추론을 위해 번역 리소스를 가져옵니다 import enTranslation from '../public/locales/en/translation.json'; import frTranslation from '../public/locales/fr/translation.json'; // 번역 리소스에 대한 유형을 정의합니다 // 이는 타입 추론에 중요합니다 export type AppResource = { translation: typeof enTranslation; // 'en'이 기본 언어라고 가정 }; i18n .use(Backend) .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: 'en', debug: true, interpolation: { escapeValue: false, // react는 기본적으로 이미 이스케이프합니다 }, resources: { en: { translation: enTranslation, }, fr: { translation: frTranslation, }, } as AppResource, // AppResource 유형으로 형변환 }); export default i18n;
단계 2: react-i18next
에 대한 사용자 정의 유형 정의
기본적으로 react-i18next
의 useTranslation
후크는 특정 번역 구조에 대한 타입 인식을 완전히 하지는 못합니다. 유형을 추가해야 합니다.
react-i18next.d.ts
: (이 파일은 프로젝트의 루트 또는 src
디렉토리에 배치해야 합니다)
import 'react-i18next'; import { AppResource } from './i18n'; // AppResource 유형을 가져옵니다 declare module 'react-i18next' { interface CustomTypeOptions { defaultNS: 'translation'; // 기본 네임스페이스를 지정합니다 resources: AppResource; // 사용자 정의 AppResource 유형을 사용합니다 } }
이제 useTranslation
은 훨씬 더 나은 타입 인식을 갖게 됩니다.
단계 3: 타입 안전하게 useTranslation
사용
번역된 문자열을 사용하는 React 구성 요소를 만들어 보겠습니다.
HomePage.tsx
:
import React from 'react'; import { useTranslation } from 'react-i18next'; interface HomePageProps { userName: string; itemCount: number; } const HomePage: React.FC<HomePageProps> = ({ userName, itemCount }) => { const { t } = useTranslation(); const handleSayHello = () => { alert(t('home.welcome', { name: userName })); // 이제 타입 안전합니다! // TypeScript는 'name'이 누락되었거나 유형이 틀렸으면 경고합니다 }; return ( <div> <h1>{t('home.welcome', { name: userName })}</h1> {/* 타입 안전한 플레이스홀더 */} <p> {t('home.itemCount', { count: itemCount, defaultValue: 'You have no items.' })} {/* 타입 안전한 복수형 */} </p> <button onClick={handleSayHello}>{t('home.helloButton')}</button> <div> <button>{t('common.cancel')}</button> <button>{t('common.save')}</button> </div> </div> ); }; export default HomePage;
어떤 타입 안전성을 얻었습니까?
- 키 존재:
t('home.nonExistentKey')
를 사용하려고 하면 TypeScript가 오류를 발생시켜 잘못된 키로 인한 런타임 실패를 방지합니다. - 플레이스홀더 매개변수:
name
매개변수 없이t('home.welcome')
를 호출하거나t('home.welcome', { name: 123 })
(여기서name
은 문자열이어야 함)을 호출하면 TypeScript가 이를 표시합니다. - 복수형 매개변수:
t('home.itemCount', { count: itemCount })
의 경우 TypeScript는 올바른 복수형 논리를 위해 필요한count
가 제공되고 숫자임을 보장합니다.
고급 시나리오: 동적 키 및 네임스페이스
때로는 키가 동적으로 생성되거나 API에서 검색될 수 있습니다. 진정한 동적 키의 직접적인 타입 추론은 어려울 수 있지만, 여전히 일부 수준의 안전성을 제공하는 기술을 사용할 수 있습니다.
// API 기반 애플리케이션에서 키는 백엔드에서 제공될 수 있습니다 interface ApiMessage { key: keyof AppResource['translation']; // 알려진 최상위 키로 제한 params?: Record<string, string | number>; } const renderApiMessage = (message: ApiMessage) => { const { t } = useTranslation(); // 동적 키에 대한 params를 완전히 타입 검사할 수는 없지만, `keyof AppResource['translation']`은 // 기본 키가 유효함을 보장합니다. 더 고급 솔루션에는 코드 생성이 필요합니다. return <p>{t(message.key as any, message.params)}</p>; };
많은 번역 파일("네임스페이스")이 있는 애플리케이션의 경우 react-i18next
는 여러 네임스페이스도 지원합니다. AppResource
및 CustomTypeOptions
를 확장하여 이를 포함하면 됩니다. 이렇게 하면 useTranslation('myNamespace')
를 호출할 때 t
함수가 해당 특정 네임스페이스에 대해 올바르게 형식화되는지 확인할 수 있습니다.
궁극적인 타입 안전성을 위한 코드 생성
특히 대규모 프로젝트에서 최고의 타입 안전성을 확보하려면 코드 생성 접근 방식을 고려하십시오. 도구는 번역 JSON 파일을 검색하고 TypeScript 유형 또는 완전한 t
함수 래퍼를 자동으로 생성할 수 있습니다. 이렇게 하면 번역 파일과 코드의 유형 정의 간의 완벽한 동기화가 보장됩니다. 예를 들어, 스크립트는 다음과 같은 파일을 생성할 수 있습니다.
generated-i18n-keys.ts
:
interface I18nKeys { 'home.welcome': { name: string }; 'home.itemCount': { count: number }; 'home.helloButton': undefined; 'common.cancel': undefined; 'common.save': undefined; }
그런 다음 t
함수를 이러한 인터페이스를 직접 사용하도록 확장하여 각 키에 대한 정확한 매개변수를 포함하여 더 정확한 유형 검사를 수행할 수 있습니다.
결론
최신 프런트엔드 프레임워크에서 타입 안전한 국제화를 구현하는 것은 글로벌 애플리케이션의 품질과 유지 관리성을 크게 향상시킵니다. react-i18next
와 같은 라이브러리와 함께 TypeScript의 강력한 유형 시스템을 활용하면 누락된 키, 잘못된 매개변수 및 잘못 관리된 복수형 처리와 관련된 일반적인 런타임 오류를 제거할 수 있습니다. 이 접근 방식은 버그를 방지할 뿐만 아니라 지능적인 자동 완성 및 조기 오류 감지를 제공하여 개발자 경험을 크게 향상시킵니다. 타입 안전한 i18n을 채택하는 것은 전 세계 사용자를 진정으로 충족하는 복원력 있고 사용자 친화적인 애플리케이션을 구축하기 위한 중요한 단계입니다.