소소한 노력으로 i18n 다국어 개발 생산성 높이기

unhyif·2024년 10월 13일
1

생산성

목록 보기
3/3

배경

최근에 새로운 팀에 합류하게 되었다! 우리 팀은 react-i18next를 이용하여 다국어 프로덕트를 개발하고 있다.
처음 들어왔을 때 신기했던 것은 컴포넌트 내의 내용을 모두 키값으로 나타내고 있다는 점이었다.

    <span>
      {t('manage:charge.complete.productName')}
    </span>

먼저 i18next 시스템을 간단히 알아 보자. 전체적인 시스템을 이 글에서 모두 담긴 어렵기 때문에, 기회가 된다면 나중에 다른 글로 작성해 보겠다.

t() 함수(useTranslation)에 key를 전달하면, 그에 대응하는 번역 값을 불러올 수 있다. 그리고 그러한 값들은 json 파일에 정의되어 있다. (json 파일 생성 방법은 회사마다 다르다.)


이제 다시 처음으로 돌아가 보자.

    <span>
      {t('manage:charge.complete.productName')}
    </span>

이 상태에서는 이게 어떤 내용인지 알 수 없다. 따라서 내용을 알기 위해서는 아래처럼 json 파일을 검색해 봐야 하는 수고가 필요했다.

charge.complete.productName을 그대로 검색할 수도 없었다. json이 중첩 구조일 때 key가 .로 연결되는 것이기 때문에, 코드 베이스에서 productName을 검색한 후 번역 값을 힘들게 찾아내야 했다.

처음 며칠 간은 이렇게 눈물을 흘리며 개발하였다.


문제 정의

이러한 상황에서 2가지 문제를 발견했고, 난 2번 문제를 우선 해결해 보기로 했다.


1. 내용으로 코드를 검색하기 어렵다.

내게만 해당될 수도 있지만, 난 컴포넌트 코드를 찾아야 할 때 컴포넌트 내의 내용을 검색하여 찾곤 했다. (동료들도 보통 그런 듯 했다.)

velog 화면을 예로 들면, 태그 목록 작동이 잘 안 된다는 보고를 받았을 때 태그 목록을 검색하여 관련 파일을 찾곤 했다는 뜻이다.
그런데 지금은 컴포넌트 내의 내용이 모두 key로만 이루어져 있으니 그럴 수 없었다. 이 문제는 훗날 LocatorJS 라는 크롬 익스텐션을 발견해서 꽤 보완할 수 있었다.

2. 컴포넌트를 파악하기 어렵다.

한 컴포넌트를 유지보수할 때, key의 향연이었기 때문에 이 부분이 어떤 부분인지 알기가 어려웠다. 내용을 확인하기 위해 컴포넌트 파일 <-> json 파일 검색을 반복적으로 거쳐야 했다.


우선은 2번 문제를 해결해 보기로 했다. 최근에 유지보수 프로젝트를 진행했을 때 반복적인 컴포넌트 파일 <-> json 파일 검색 과정에서 고통을 느꼈기 때문이다. 😇 그리고 1번 문제는 해결에 시간이 좀 들 것 같은데, 크롬 익스텐션으로 꽤 보완할 수 있는 상태라 우선 순위를 낮추었다.


Step 1

TypeScript를 활용해 보자

i18next는 declaration 파일을 통한 타입 확장이 가능한데, 위 사진의 resources 필드 부분에 타입을 명시하면 된다.
우리 팀의 declaration 파일은 위와 동일하게 설정되어 있었다. 그런데 결과적으로 아래처럼 key의 추론은 되었지만 그에 대응하는 번역 값의 추론이 정확히 되지 않았다.

즉, t('manage:charge.complete.productName')이 string으로만 추론이 되었다.
왜인가 보아하니, 아직 TypeScript에서 json의 value 추론을 할 수 없기 때문이었다. 예를 들어,

// data.json

{
  "key": "value"
}
import data from './data.json';

type Data = typeof data;

이 상황에서 Data는 아래와 같이 추론된다.

type Data = {
    key: string;
}

찾아 보니 json을 as const 처럼 타입 추론 가능하게 해달라는 https://github.com/microsoft/TypeScript/issues/32063 이슈도 있었다.


이 상황을 해결하기 위해, 다음과 같은 아이디어를 생각했다.

json 파일 ns1 대신, json을 const assertion 변수로 변환해 사용하는 것이다.

// data.json

{
  "key": "value"
}

const data = { key: 'value' } as const;

로 변환하여, ns1 자리에 data 변수를 대입한다면 타입 추론이 정확해질 것으로 생각했다.

왜냐하면 const assertion(as const) 사용 시, 값을 상수처럼 취급하기에 더 구체적인 타입 추론이 가능해지기 때문이다. 아래의 z 변수 예시를 통해 이해할 수 있다.


이에 따라, 다국어 json 파일을 생성하는 프로세스가 기존 코드에 있었기 때문에, 그 프로세스 완료 직후에 해당 json 파일을 ts 파일로 변환하는 함수(createTsFromJson)을 개발하여 실행해 주었다.

async function createTsFromJson(jsonFilePath: string) {
  // ex: 'common.json'에서 'common' 추출
  const namespace = path.basename(jsonFilePath, path.extname(jsonFilePath));
  // json data
  const data = await readFile(jsonFilePath, 'utf8');
  // const assertion 변수를 default export
  const tsContent = `const ${namespace} = ${data} as const;\n\nexport default ${namespace};`;

  const prettierOptions = await prettier.resolveConfig(jsonFilePath);
  const formattedTsContent = prettier.format(tsContent, {
    ...prettierOptions,
    parser: 'typescript',
  });

  await writeFile(
    jsonFilePath.replace('.json', '.ts'),
    formattedTsContent,
    'utf8',
  );
}

그리고 아까의 ns1을 const assertion 변수로 대체해 주니, 아래처럼 타입 추론이 정확히 되기 시작했다. 🥳
이로써 컴포넌트 파일 <-> json 파일을 왔다갔다 해야 하는 비효율을 줄일 수 있었다.


Step 2

익스텐션을 이용해 보자

이건 약간 허무한 방법이다. 나중에 알게 되었는데, VSCode에서 i18n Ally 익스텐션을 사용하면 코드에서 번역 값을 쉽게 확인 가능하다.

이런 식으로 기능이 더 엄청났다 🤣 사용법은 Docs를 참고하면 된다.

그래도 우리 팀은 IDE를 다양하게 쓰고 있기 때문에, 익스텐션이 없는 팀원에게는 Step 1 방법이 도움이 되어서 의미 있는 작업이었다고 생각한다.


배운 점

1. 일하고 있는 형태를 낯선 시선으로 관찰해 보기

일을 하다 보면 익숙함에 쉽게 물들기 쉬운 것 같다. 이것이 문제인지조차 인지하지 못 하는 경우도 있고, 인지했더라도 그것을 개선하는 리소스가 현행 유지보다 더 클 것 같은 막연한 두려움이 들 때도 있다.

하지만 일하고 있는 형태를 때때로 낯선 시선으로 관찰해 보면 새로운 문제를 인지하게 될 수도 있다. 그리고 고민해보면 생각보다 적은 리소스로 해결이 가능할지도 모른다. 만일 전부 해결하진 못 한다고 해도, 점진적인 개선을 이루어 나가면 궁극적으로 더 행복하게 일하게 될 것이라고 생각한다. 😇 개인적으로는 작은 시도를 자주 해서 임팩트를 만들어 나가는 것을 좋아하는 편이다.

2. Best Practice 찾아 보기

내가 느꼈던 문제를 좀 더 검색해 봤으면 IDE 익스텐션을 더 일찍 발견할 수 있었을 것 같다. 그런데 성격이 약간 급한 탓에 떠오른 아이디어를 바로 실행해 버리는 바람에 그러지 못 했다 🥲

개발을 해내는 것도 중요하지만, '잘' 하는 것도 중요하기 때문에, 무언가를 실행하기 전에 Best Practice를 찾아 봐야겠음을 배웠다. 요즘엔 리서치 또한 개발 실력에 포함되는 것 같다는 생각이 든다. (당연한가?)


다국어 개발과 관련하여 또 다른 문제를 만나게 된다면, 그에 대한 해결 스토리로 다시 찾아 오겠다. 👋

1개의 댓글

comment-user-thumbnail
2024년 10월 23일

i18n Ally를 통해서 찾을 수 있군요 좋은 꿀팁 감사합니다!ㅎㅎ

답글 달기