[intl] 다국어 처리 :: 마이구미
이 글은 다국어를 처리하는 방법을 다룬다.
무겁지 않고, 가벼운 내용으로 사례를 기반으로 이어나가본다.
다국어 처리에 있어서, 라이브러리를 사용하는 경우, 직접 구현해보는 경우 2가지를 살펴본다.
라이브러리는 react-intl 를 사용한다.
서비스는 다국어 관리가 필요한 경우가 발생할 수 있다.
별도 API 가 존재한다면, 크게 고민할 것이 없다.
하지만 정적 데이터 성향은 결국 프론트에서도 관리해야하는 니즈가 나올 수 있다.
예를 들어보면, 서비스 단위가 아닌 모듈 기반으로 패키지들을 분리하는 경우이다.
그 중 하나의 예가 디자인 시스템을 들 수 있다.
이번 글에서는 디자인 시스템의 경우를 기반으로 이야기를 이어나간다.
서비스 단위의 규모가 작지 않은 단위라면, 대부분 라이브러리를 채택하여 사용할 수 있다.
디자인 시스템의 단위라면, 특정 서비스, 라이브러리 등에 종속되지 않고 독립적으로 시스템이 구현되기를 희망한다.
그러한 측면에서 다국어의 경우에는 비교적 가벼운 기능에다가 디자인 시스템의 특성상 다국어를 위한 데이터가 많지 않기에, 라이브러리를 사용하지 않고, 경량화를 위해 직접 구현하고 싶은 니즈가 생길 수 있다.
그렇다면, 간단한 버전의 다국어 시스템을 구현해보자.
관련 로직은 크게 3가지 파일로 구성된다.
- ConfigProvider.tsx
- useLocale.ts
- locales/{ko|en}.ts
ConfigProvider 의 경우에는 디자인 시스템의 모든 컴포넌트(Button,...) 들이 다국어 데이터를 바라보기 위한 전역 Context 라고 볼 수 있다. (실제 디자인 시스템의 경우에는 다국어뿐만 아니라 theme, className 등 디자인 컴포넌트의 설정들을 제어하기 위한 로직들이 포함되어있을 것이다.)
useLocale 은 원하는 다국어 데이터를 가져오기 위한 훅이다.
locales/{ko|en}.ts 의 경우는 다국어를 의미하는 텍스트들을 관리하고 있다.
예제처럼 간단한 코드를 구현하여 훅, 컴포넌트 방식으로 제공하여 각 디자인 컴포넌트에서는 쉽게 사용할 수 있다.
이러한 상황에서 다른 패키지, 서비스 등을 구현해야하는 경우...
"다른 곳에서도 라이브러리를 사용하지 않고, 이 방식으로 구현해도 충분할 것 같은데?" 다음과 같은 생각이 들 수 있다.
사실상 문제가 없다.
하지만 좀 더 고민을 해봐야한다.
무슨 고민이 필요한지, 직접 실제 라이브러리와 비교해보자.
위에서 직접 구현한 방식은 사실 react-intl 의 방식을 최소화한 버전이다.
react-intl 에서도 실제 제공해주는 방식은 흡사하다.
다른 점은 많은 기능이 포함되어있고, 성능 측면에서도 더 좋다.
기능 측면에서는 포맷팅 기능, 다국어 처리, 태그 변경, 동적 데이터를 포함한 텍스트 등까지 활용할 수 있다.
규모가 있는 프로젝트에서는 라이브러리의 많은 기능을 활용할 수 있다.
<FormattedMessage id="unit.count" value={count: 10} /> // 10개
<FormattedNumber value={1000} /> // 1,000
<FormattedMessage tagName='p' /> // p 태그 사용
// 배열 관리
{ 'textList': ['첫번째 텍스트', '두번째 텍스트', '세번째 텍스트'] }
const { messages } = useIntl()
messages('textList')
그리고 성능 측면에서도 기본적으로 메모이제이션을 기반으로 활용하고 있어서 성능이 더 좋다.
React.memo<Props>(
FormattedMessage,
areEqual
)
직접 구현한 방식에서 위와 같은 로직이 없는 경우에는 다국어 텍스트에 대한 id 를 매번 리스트에서 찾아야한다.
이건 리랜더링 시점마다 계속 동일한 행동으로 관리하는 데이터가 늘어날수록 비용은 커질 것이다.
그리고 DX 측면도 고민해볼 필요가 있다.
디자인 시스템처럼 종속되지 않는 성향이고, 대부분의 패키지화를 생각하면 이상적인 측면에서는 독립적이여야한다.
하지만 이상적인 것이지 모든 패키지가 종속성을 갖지 않도록 해야하는 것을 지향해야한다는 것은 아니다.
각자 환경이 다르고, 니즈가 다르고 이상적인 환경이 아닌 경우가 많다.
예를 들어, A 서비스와 B 서비스는 다른 서비스이지만, 많은 영역이 중복되어 사용되어야한다. (B2B 서비스의 흔한 사례)
즉, 서비스 단위의 공통 컴포넌트를 모듈화하는 경우에는 독립적인 성향이 아니라, 서비스에 종속된 구조가 된다.
서비스 단위의 공통 모듈 내에서는 직접 구현한 방식을 사용하고, 서비스 단위에서는 라이브러리 사용한다면 혼란을 줄 수 있다.
파라미터 전달 방식, 제공 기능 등도 일관성이 있게 제공해주어야 유지보수가 편해진다. (사실 서비스 단위의 공통 모듈은 서비스에 종속적이기에 때문에 서비스가 라이브러리를 사용하고 있다면, 여기서도 라이브러리를 사용하는 것이 바람직함)
전달하고자하는 말은 "라이브러리를 사용해라", "직접 구현해라", "일관성을 가져라" 의 의미는 아니다.
시작 단계에서 맞이할 수 있는 이러한 이슈와 고민이 있을 수 있으니, 조금이나마 참고하면 좋을 것 같다.