[Next.js] 포트폴리오 웹 페이지 제작기 - 14. react-slot-machine-text

olwooz·2023년 4월 17일
0

슬롯 머신 텍스트 이펙트를 react-slot-machine-text라는 라이브러리로 만들어서 배포했었다.
하지만 아이러니하게도 정작 내 포트폴리오 웹 페이지에서는 SlotMachine 내부에서 i18n을 사용하고 있던 터라 곧바로 컴포넌트를 라이브러리로 대체해서 사용할 수가 없는 상태였다.

현재 방식은 표시하고자 하는 text의 key string을 전달해 SlotMachine 내부에서 번역하는 방식이었는데, 이를 텍스트 배열로 바꿔서 전달해주는 방식으로 변경해야 한다.

기존 코드

Main/data.ts

// 번역되는 자기소개 문장에 대한 키워드
export const textKeys = ['concise', 'detail', 'sympathy', 'global'];

위 키워드들은 번역되는 문장들이 위치하는 common.json의 key이다.

public/locales/en/common.json

// public/locales/en/common.json
...
"main": {
  "greetings": "Hi. I'm",
  "textData": {
    "concise": "a concise and clean coder",
    "detail": "a detail-oriented programmer",
    "sympathy": "an empathetic developer",
    "global": "a global-minded engineer"
  },
  "introduction": "Dongun Yi."
},

따라서 원래는 MainSlotMachine에게 prop으로 textKeys를 넘겨주고, SlotMachine 내부에서 아래와 같이 번역된 문장들을 받아오고 있었다.

t(`main.textData.${textKeys[0]}`)

해결 과정

먼저 배열을 통째로 받아오는 방법을 모색했다.

t('main.textData', { returnObjects: true })

이렇게 하면 main.textData object 전체를 받아오게 되는데, 내가 관심 있는 부분은 오직 values들이기 때문에 Object.values()를 사용해 감싸주면 문장들만을 배열로 받아올 수 있게 된다.

Object.values(t('main.textData', { returnObjects: true }))

react-slot-machine-text을 설치하고 SlotMachine을 import해서 textData prop에 위 값을 넘겨줬다.

<SlotMachine textData={Object.values(t('main.textData', { returnObjects: true }))}></SlotMachine>

그러나 React에서는 prop이 변경된다고 해서 child를 re-render하지 않기 때문에 언어 버튼을 클릭해도 SlotMachine의 텍스트는 바뀌지 않았다.
child에 key를 설정해 key가 변경될 때마다 child가 re-render되도록 해서 해결하면 된다.

<SlotMachine 
  textData={Object.values(t('main.textData', { returnObjects: true }))} 
  key={i18n.language}
>
</SlotMachine>

이번엔 언어 버튼을 클릭하면 SlotMachine의 텍스트가 한 틱씩 느리게 바뀐다.
(e.g. 초기 언어는 영어고 한국어, 중국어 순서로 바꾼다고 가정, 처음 한국어 버튼을 클릭했을 땐 표시 언어가 바뀌지 않고 영어로 유지되고, 그 다음 중국어 버튼을 클릭했을 때 비로소 한국어로 번역되어 표시됨)
이는 useTranslation을 사용했을 때 언어가 바뀌게 되면 i18n.language는 곧바로 바뀌지만 translation은 비동기로 로드되기 때문이다. 이 문제를 해결하려면 useTranslation에서 추가로 ready라는 값을 사용해주면 된다.

const [language, setLanguage] = useState(i18n.language);

useEffect(() => {
  if (!ready) return;
  setLanguage(i18n.language);
}, [ready, i18n.language]);

/*...*/

<SlotMachine 
  textData={Object.values(t('main.textData', { returnObjects: true }))} 
  key={language}
>
</SlotMachine>

마지막으로 Array를 얻는 코드가 너무 복잡해보여서 변수를 선언해 코드를 깔끔하게 만들고 마무리한다.

// type assertion을 하지 않으면 unknown[]이 되기 때문에 as string[]을 붙여줌
const textDataArray = Object.values(t('main.textData', { returnObjects: true })) as string[];

/*...*/

<SlotMachine textData={textDataArray} key={language}></SlotMachine>

내가 직접 만든 라이브러리를 내 웹 포트폴리오에 적용시킬 수 있게 되니까 굉장히 뿌듯하다.

0개의 댓글