JavaScript의 함정: i18n-js v4에서 구조 분해 할당이 위험한 이유

Author Kini
·

인디 해커로서 다수의 앱을 동시에 찍어내는 SaaS Factory 시스템을 구축하다 보면, 기술적 로직보다 더 중요한 것이 바로 시스템의 안정성입니다. 최근 글로벌 대응을 위해 다국어 라이브러리인 i18n-js를 v4로 마이그레이션하던 중, 5년 차 개발자에게도 꽤나 흥미로운(?) 에러를 만났습니다.

혹시 여러분도 const { t } = i18n이라는 깔끔한 코드를 작성했다가 TypeError: Cannot read property ‘locale’ of undefined라는 차가운 메시지를 마주하셨나요? 오늘은 이 에러의 본질인 this 바인딩 이슈와 시스템 빌더로서 이를 어떻게 설계해야 하는지 정리해 보겠습니다.


사건의 발단: 가독성을 챙기려다 런타임을 잃다

개발자에게 가독성은 포기할 수 없는 가치입니다. 매번 i18n.t('key')라고 인스턴스를 직접 참조하는 것보다, 상단에서 함수만 쏙 뽑아서 사용하는 것이 훨씬 세련되어 보이죠.

TypeScript

// 문제가 발생한 전형적인 코드 패턴
const { t } = i18n; // 구조 분해 할당으로 함수 추출
return <Text>{t('welcome_message')}</Text>; // 여기서 런타임 에러 발생

객체 내부에는 분명히 데이터가 존재하는데, 왜 함수는 자기가 누구인지 잊어버린 채 에러를 뿜어냈을까요?


원인 분석: 자바스크립트 함수의 ‘고향’ 유실

결론부터 말씀드리면, 자바스크립트에서 객체의 메서드를 추출하는 순간 함수와 인스턴스 사이의 연결 고리(this)가 끊어지기 때문입니다.

자바스크립트의 this는 함수가 어디서 정의되었느냐가 아니라, 어떻게 호출되었느냐에 따라 결정되는 아주 독특한 성질을 가집니다.

  • 메서드 호출 (i18n.t()): 점(.) 연산자가 “너의 주인은 바로 앞에 있는 i18n이야”라고 알려줍니다. 내부의 this.locale이 정상적으로 작동합니다.
  • 독립 호출 (t()): 구조 분해 할당으로 뽑아낸 함수를 그냥 실행하면 주인이 없는 상태가 됩니다. 이때 thisundefined가 되어버리고, 내부에서 상태값을 참조하려 할 때 에러가 터지는 것입니다.

특히 i18n-js v4는 클래스 인스턴스 기반으로 동작하므로, 내부 상태(locale, translations)에 의존하는 t 함수에게 이 this 유실은 치명적입니다.


해결 전략: 시스템 빌더를 위한 두 가지 해법

SaaS Factory처럼 수많은 컴포넌트에서 번역 함수를 호출해야 하는 구조라면, 사용하는 쪽에서 이 this 문제를 고민하게 해서는 안 됩니다. 시스템 초기화 단계인 index.ts에서 이미 안전하게 가공된 결과물을 내보내야 합니다.

1. 명시적 바인딩 (bind)

가장 정석적인 방법입니다. 어떤 환경에서 호출되든 thisi18n 인스턴스로 영구 고정하는 것입니다.

TypeScript

// locales/index.ts
export const t = i18n.t.bind(i18n);

2. 화살표 함수 래핑 (Arrow Function Wrapping)

바인딩 대신 화살표 함수로 감싸서 내보낼 수도 있습니다. 호출될 때마다 i18n.t를 직접 실행하므로 점 연산자의 마법이 유지됩니다.

TypeScript

// locales/index.ts
export const t = (scope: string, options?: any) => i18n.t(scope, options);

SaaS Factory 운영자로서 얻은 교훈

이번 경험은 시스템 구축에 있어 아주 중요한 원칙을 다시 한번 일깨워주었습니다.

  • 인프라는 예측 가능해야 한다: 외부 컴포넌트에서는 내부의 this 문제를 몰라도 t 함수 하나로 모든 것이 해결되도록 추상화되어야 합니다.
  • 도구의 버전 변화에 민감할 것: v3에서 잘 되던 방식이 v4에서 안 되는 이유는 라이브러리의 설계 철학(Class vs Global Object)이 바뀌었기 때문입니다. 이를 파악하는 것이 전문성입니다.
  • 기본기가 곧 생산성이다: 자바스크립트의 기초인 thisScope를 이해하고 있으면, 수 시간의 디버깅 삽질을 예방할 수 있습니다.

마치며: 다시 가동되는 앱 공장

이제 저의 SaaS Factory는 다시 평화를 찾았습니다. 멈췄던 글로벌 서비스들의 UI가 다시 기깔나게 그려지고 있습니다. 여러분도 “분명히 맞는 코드인데 왜 안 되지?” 싶은 순간이 있다면, 내가 함수의 탯줄을 끊어버린 건 아닌지 확인해 보시기 바랍니다.

새해에는 더 견고한 시스템으로 더 많은 가치를 만들어내시길 응원합니다!

Share this post