부지런한 바보보다는 게으른 천재가 되는 것이 나의 업무 태도이지만, 효율적으로 업무를 처리하는 방법을 발견해내지 못하면 게으른 바보가 되기도 한다. (나의 컨디션은 게으른 바보와 게으른 천재 사이다 : 부지런한 천재같은 건 유니콘이지 않을까?)
다행히도 이번 홈페이지 번역기능 구현 할 때에는 게으른 천재가 될 수 있었다. next-intl 라이브러리와 googlspreedsheet, appscript(웹 스크래핑 + json convert)의 조합으로 오래 걸릴 번역작업을 빨리 끝낼 수 있었다. 이 글을 통해 홈페이지 다국어지원 기능을 효율적으로 구현하는 방법을 공유하고자 한다.
Next-intl
Next-intl은 Next.js 기반 다국어 제공 라이브러리이다. Next.js 공식문서 에서도 링크걸려되어 있는 라이브러리로 가이드 문서가 잘 작성되어 있어서, 초보자라도 쉽게 따라 할 수 있는 개발가이드이다.
message 폴더는 홈페이지에 제공될 번역본을 제작한다. 번역본은 이 글 웹 스크래핑 부분을 통해 빠르게 번역하는 방법을 공유한다.
i18n 폴더는 다국어 기능지원 관련 핵심 기능을 담당한다 request.ts 파일에서 locale을 정의하고 message폴더에 적절한 값을 파싱하여 불러오는 역할을 한다. i18n폴더를 다른 경로에 두고 싶다면, Next.config 설정에서 지정한 경로를 입력해주어야 한다.
2. Next.config.ts
next.js의 옵션들을 설정하는 config 파일에 next-intl/plugin 설정을 추가로 적용해주어야 한다. createNextIntlPlugin 함수에 역할은 i18n의 환경설정을 적용해준다.
본인은 output: “exprot” 설정을 통해 정적 환경에서 Nex.js를 운영하고 있다. 즉 Next.js의 서버기능을 부분적으로 활용하고 정적 HTML 파일 기반으로 정적 호스팅에 배포하고 있다. next-intl 은 정적환경에서도 정상적으로 동작한다!
3. src/i18n/routing.ts
기본적으로 접속가능한 locale 정보를 입력해주어야 하며, 본인은 한국어, 일어, 영어해석을 기본적으로 지원한다.
static 환경에서 export하는게 아니라면 middleware 설정을 통해서 API나 정적 파일 요청에는 불필요하게 언어 감지나 리다이렉션이 일어나지 않도록 필터링해주어야 하는데, 본인은 정적배포를 진행하기 때문에 생략하였다.
navigation.ts를 통해서 next.js의 기본 라우터 이동 함수들을 next-intl용으로 변경해준다. 이를 통해 기존 router이동 함수들을 locale 인식 버전으로 제공합니다. 이 래퍼를 쓰면 페이지 이동 시 현재 언어가 유지되고, locale을 지정하면 언어 전환도 자연스럽게 제공된다.
5. src/i18n/request.ts
getRequestConfig 함수는 브라우저에 locale(언어설정) 요청을 인식하는 역할을 하며, 요청한 locale 정보를 routing.ts 에서 설정한 locales 폴더에서 요청한 언어정보가 있는 지 확인 한뒤 없으면 defaultLocale로 언어정보를 제공하고, 있는경우 요청한 locale의 언어정보를 제공한다.
6. src/app/[locale]/layout.tsx
locale layout에서 locale 정보를 요청해서 최종적인 설정 마무리 작업을 해주는 역할을 한다.
<NextIntlClientProvider> 를 통해 받은 메시지를 하위 자식들에게 전달해준다.
generateStaticParams 을 통해 static 환경에서도 동작할 수 있도록, locale에 따라 모든 html 페이지를 생성해주는 역할을 한다.
7. src/app/[locale]/page.tsx
번역이 실제로 적용되기 위해서는 각 page.tsx에서 useTranslations 훅을 통해 json의 키를 불러와 적용시켜주어야 한다. json 웹스크래핑을 통해 간단하게 생성 할 예정이다.
8. 언어변경
i18n/navigation.ts 에서 네비게이션을 next-intl 네비게이션을 변경해주었다. locale 정보를 쭉 유지시켜주기 위함으로, 모든 next/link의 참조를 i18n/navigation 참조로 변경해 줄 필요가 있다. locale 정보가 없어 아예 route 이동이 되지 않는 경우가 있다.
9. Static 환경에서 주의사항 | page.tsx
root page.tsx에서 url redirection작업을 해주어야 한다. 동적환경이라면 자동으로 되지만, 정적 페이지라면 root 경로에 접속했을 때 아무런 데이터가 나타나지 않아서, 초심자라면 무조건 헤맬 수 있다. 경로 뒤에 locale 정보를 입력해야만 들어갈 수 있다.
웹 크롤링
json 언어번역 파일을 만드는 것은 엄청난 수작업을 필요로 한다. 기존에 모국어 텍스트를 제공하고자 하는 언어로 번역을 하고 하나하나 각각의 언어.json 파일을 만들어서 동일한 json key값을 작성해주고 번역한 내용을 각각 입력해주어야 한다. 양이 적은 페이지라면 할만하지만, 페이지 내용이 많다면, 지루하고 고통스러운 작업이 될 것이다.
다행히도 나보다 먼저 이러한 고통을 겪었던 사람들이 웹크롤링 기술과, google translate 기술의 발전을통해, 간단하게 해결 할 수 있었다. 개발자 선배님들 만만세!
app script 사이트와 가이드 사이트를 첨부한다. 간단하게 App Script를 설명하면 Google Spreedsheet의 단순반복 노가다 작업을 빠르게 해결해줄 수 있도록 만들어주는 좋은 사이트다. App Script를 통해 데이터를 자신이 운영하는 도메인의 데이터를 수집하였다. (남의 데이터를 크롤링하면 벌금을 받을 수 있다. 주의하길 바란다.)
앱 스크립트 페이지에 접속하면 파일을 추가한 뒤 코드를 작성하고, 함수를 선택하여 실행을 통해 스크립트를 실행해 줄 수 있다. 앱스크립트 실행 시에는 google sheet에 확장프로그램 > App script를 통해 접속해서 실행해주어야 한다.
크롤링 코드
CheerioGS 라이브러리는 웹 요청(request)으로 가져온 HTML을 손쉽게 분석하기 위해 사용된다. CheerioGS는 jQuery 선택자 문법을 이용해 HTML 내용을 파싱할 수 있는 라이브러리로, Apps Script의 ‘라이브러리 +’ 버튼을 클릭한 뒤 Script ID를 입력하여 추가 해야 한다.
중복 데이터를 구글 시트에 데이터 > 데이터 정리 > 중복 항목 삭제를 통해 제거해준 후 googletranslate 함수를 통해 사이트에서 추출한 텍스트 데이터의 일괄적인 번역을 실행한다. google 만만세
json convert
최종적으로 next-intl에서 필요한 ko.json, en.json, jp.json 파일을 제작해주기 위한 기준 구분 키를 최우측 항목에 입력해준뒤 아래의 코드를 App script에서 실행시켜주어야 한다. 간단한 함수라 별도의 주석을 작성하지 않았다.
결론
설명이 길어졌지만, 누군가는 이보다 더 효율적인 방법으로 최적화를 이룰 수도 있을 것이다.
그럼에도 지금은 주어진 환경 속에서 선택할 수 있는 최선의 방식으로 업무 효율을 높이려 하고 있다.
특히 코드를 잘 모르는 사람이라도 웹 스크래핑을 통해 반복적인 업무를 조금 더 편하게 처리할 수 있기를 바란다.
결국 ‘노력하는 바보’와 ‘게으른 천재’의 이야기는 업무 효율성을 직관적으로 이해하기 위한 비유에 가깝다. 진짜 생산성은 타고난 재능보다 꾸준히 배우고 익히려는 태도에서 비롯된다. 부지런히 공부하고 성장하려는 사람이 결국 더 나은 결과를 만들어낼 수 있을것이다.