번역은 이미 끝나 있었습니다
한국어 운세 데일리 포스트를 매일 자동으로 발행하고 있었습니다. 목표는 단순했습니다. 같은 포스트를 영어는 영어 계정으로, 일본어는 일본어 계정으로 동시에 내보내는 것. 계정은 새로 만들지 않고 이미 있는 계정 3개와 기존 토큰을 그대로 쓰기로 했습니다.
처음엔 번역 작업이라고 생각했습니다. 그런데 운세 데이터(운세 구간 문구, 행운색)는 이미 ko/en/ja 세 언어로 들어 있었습니다. 문구는 로케일만 넘기면 자동으로 바뀌는 상태였습니다.
그러니 번역이 병목이 아니었습니다. 병목은 두 가지였습니다. 하나는 '한국어'라는 가정이 생성 코드 곳곳에 숨어 있었다는 것, 다른 하나는 어떤 콘텐츠는 번역해도 그 언어권 독자에게 의미가 없다는 것이었습니다.
당신의 프로젝트에서 언어 하나를 추가한다면, 문자열 파일 말고 '언어'를 전제하고 있는 곳이 몇 군데인지 바로 말할 수 있나요?
한국어가 숨어 있던 곳
생성 스크립트에 로케일을 끝까지 관통시키면서 손댄 곳은 이랬습니다. 페이지의 lang 속성과 폰트, 템플릿 라벨, 스펙 함수들, 앱 목록 로딩, 나레이션 문구와 TTS 음성. 전부 locale 파라미터를 받도록 바꿨습니다.
폰트는 그냥 넘어갈 수 없는 부분이었습니다. 일본어 렌더에는 Hiragino를 지정했습니다. 한국어 폰트로 가나를 그리면 두부(빈 네모)가 됩니다.
데이터에 없던 것도 있었습니다. 별자리·띠·계절 이름입니다. 운세 문구는 세 언어로 있었는데 이 이름들은 한국어뿐이어서, en/ja 매핑 테이블을 따로 만들었습니다.
가장 조용한 함정은 파일명이었습니다. 세 로케일이 같은 날 같은 종류의 자산을 만들면 같은 이름으로 서로 덮어씁니다. 로케일별 접미사를 붙여 충돌을 막았습니다.
번역하면 안 되는 콘텐츠
여기서 진짜 결정이 나왔습니다. 한국어 포스트는 별자리, 띠, 일주(사주) 같은 여러 축을 돌아가며 다룹니다. 이걸 전부 영어로 옮기면 될까요?
당신이라면 기계적으로 전부 번역해서 세 계정에 똑같이 올리겠습니까, 아니면 언어권마다 다루는 축 자체를 줄이겠습니까?
저는 줄이는 쪽을 택했습니다. 한국어는 전체 축을 다 씁니다. 일본어는 별자리와 띠만 쓰고 일주는 뺐습니다. 영어는 별자리만 씁니다. 띠와 사주는 서구권 독자에게 맞지 않는다고 판단했습니다.
해시태그도 같은 맥락입니다. 캡션과 해시태그를 로케일별로 만들고, 메가태그(지나치게 큰 범용 태그)는 넣지 않았습니다.
발행 루프와 한 줄짜리 반전
계정 3개를 한 루프에서 돌리려면 계정마다 다른 자격증명을 써야 합니다. 이미 다른 자동화에서 쓰던 방식대로, 발행 직전에 클라이언트가 읽는 환경 파일 경로(ENV_PATH)를 계정별로 바꿔 끼웠습니다. 계정 사이에는 간격을 뒀습니다.
그리고 성과를 보고 스스로 조정하는 장치를 붙이려다 막힌 지점이 있었습니다. 인사이트 데이터는 게시물 ID 기준으로 나오는데, 제 발행 함수는 그 ID를 반환하지 않고 버리고 있었습니다. 어떤 게시물이 어떤 종류(kind)의 콘텐츠였는지 이을 고리가 없었던 겁니다. 수정은 publish()가 media_id를 반환하게 하는 한 줄이었습니다.
그 위에 학습 단계를 얹었습니다. learn()이 인사이트 성과를 게시 로그의 media_id→kind와 조인해 kind_weights.json을 만들고, 그날의 계획을 짜는 함수가 이 파일을 읽어 앱 로테이션에 가중치를 줍니다. 파일이 없거나 깨져 있으면 균등 분배로 돌아갑니다. 발행 경로에는 동기 API 호출을 넣지 않고, 학습은 별도 스케줄로 돌립니다. 학습이 실패해도 발행이 멈추지 않게 하려는 것입니다.
검증은 유닛 테스트 8개를 추가해 전체 19개 통과, 세 로케일 렌더를 눈으로 확인, 그리고 계정 3개에 대한 DRY 실행으로 자산 6개까지 끝에서 끝까지 통과시킨 것입니다.
자가진단 체크리스트
- 문자열 파일 말고 폰트,
lang속성, TTS 음성, 고유명사 매핑, 산출물 파일명 중 언어를 전제하는 곳을 목록으로 뽑아본 적이 있나요? - 번역할 수 있는 콘텐츠와 그 언어권에서 의미가 있는 콘텐츠를 따로 구분하고 있나요?
- 발행 함수가 플랫폼이 돌려주는 게시물 ID를 반환하고 로그에 남겨서, 나중에 성과와 조인할 수 있나요?
솔직한 부분
이 기록 시점에 남은 건 테스트 통과와 DRY 실행 결과까지입니다. 실제 세 계정에서 어떤 성과가 나왔는지는 아직 모르고, 가중치 파일이 의미 있는 값을 갖게 될 만큼 데이터가 쌓였는지도 모릅니다. 띠·사주를 영어 계정에서 뺀 것은 측정이 아니라 판단이고, 일본어에서 일주를 뺀 것도 마찬가지입니다. 그 판단이 맞았는지는 학습 루프가 돌아야 확인할 수 있는데, 그 루프는 이번에 지금 올린 종류만 비교할 수 있습니다. 빼버린 축이 해외에서 먹혔을지는 이 구조로는 알 수 없습니다.