pile·
백엔드·티몬티몬·

소소한 자바 이야기 - Number와 Locale

인도네시아 유저 대상 프로젝트에서 국제화를 다루다 마주친 두 가지 함정을 정리한 글이다. 표준 ISO 코드를 넣었는데 다른 값이 나가는 문제와, 숫자 포맷의 쉼표와 점이 뒤바뀌어 보이는 문제를 각각 JDK 구현까지 따라 들어가 확인한다. 결론보다 원인을 추적하는 과정이 본문의 대부분이다.

핵심 포인트
  • 언어는 ISO 639-1, 국가는 ISO 3166-1의 두 자리 표기를 기준으로 삼기로 팀 간에 정했다.
  • 인도네시아어 코드로 id를 넣었는데 계속 in이 나가 결제 게이트웨이 쪽에서 거부됐다.
  • 원인은 Locale 생성자가 하위 호환을 위해 새 코드를 구 코드로 되돌려 저장하기 때문이었다.
  • he는 iw로, yi는 ji로, id는 in으로 바뀌며 이 동작은 코드에 그대로 박혀 있다.
  • 인도네시아 숫자 포맷은 소수점이 쉼표이고 자리 구분이 점이라 한국 감각과 반대로 보인다.
  • 그 규칙은 localedata.jar 안의 언어·국가별 리소스 파일에 정의돼 있다.
상세 정리
  • 상황: 회원팀과 빌링팀이 어려운 부분을 맡고 필자는 그 팀들과 통신하고 UI를 표시하는 역할이었다.
  • 용어 유래: i18n은 첫 글자와 끝 글자 사이 글자 수가 18개라서 붙은 표기이며 localization도 같은 방식으로 l10n이 된다.
  • 표준 합의: 애매한 것은 모두 ISO 두 자리 표기를 따르기로 도메인 담당자들끼리 정했다.
  • 대상 코드: 인도네시아는 id와 ID, 대만은 zh와 TW, 태국은 th와 TH를 쓴다.
  • 구현: Locale 객체를 언어와 국가로 만들어 두고 결제 게이트웨이 호출에 언어 코드를 넘겼다.
  • 증상: 표준에 맞게 넣었는데 계속 표준에 맞지 않는 값을 보낸다는 답을 받았다.
  • 원인 추적: Locale 생성자를 열어보니 내부에서 옛 ISO 코드로 변환하는 함수를 거치고 있었다.
  • 변환 규칙: 새 코드와 옛 코드를 모두 받아들이되 하위 호환을 위해 항상 옛 코드로 저장한다는 주석이 달려 있다.
  • 회피 검토: Locale을 커스터마이징하면 그것을 중요하게 쓰는 SpringMVC 쪽 코드도 크게 손봐야 해서 부담이 컸다.
  • 실제 해결: 다행히 상대 팀이 어렵지 않다며 인도네시아어 코드를 in으로 받도록 바꿔줬다.
  • 두 번째 문제: 숫자를 보기 좋게 만들려고 NumberFormat에 Locale을 넘겼더니 10억과 소수점 값이 점과 쉼표가 뒤바뀐 형태로 나왔다.
  • 확인: 검색해보니 그 나라에서 실제로 그렇게 쓰는 표기였다.
  • QA 논의: 천 단위 구분에 점을 쓰면 한국 사람에게는 1원으로 읽힐 수 있어 바꾸자는 의견이 나왔지만, 오픈 시점에는 현지 표기를 그대로 쓰기로 했다.
  • 문화 차이: 현지 담당자들은 오히려 한국식 표기를 의아해했다는 후문을 덧붙이며 문화 차이로 정리한다.
  • 정의 위치 추적: 오라클 Java 기준으로 jre 아래 localedata.jar에 정의돼 있고 기본 패키지가 sun.text라 초창기부터 있던 것으로 보인다.
  • 리소스 구조: 언어와 국가별 FormatData 파일의 NumberElements 배열에서 0번째가 소수점, 1번째가 자리 구분자로 쓰인다.
  • 마무리 실험: 그 리소스 파일을 가진 Locale들이 같은 숫자를 어떻게 표현하는지 jar를 열어 순회하는 테스트 코드를 짜본다.
왜 읽나다국어 서비스에서 표준 코드를 넣었는데 다른 값이 나가거나 숫자 표기가 예상과 다르다면, 원인이 내 코드가 아니라 플랫폼 구현에 있을 수 있다는 실제 사례다.
티몬
티몬 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·twilio-engTwilio Engineering·

    Programmable Messaging에서 Verify API로 마이그레이션하기

    Twilio의 Programmable Messaging API로 자체 OTP 솔루션을 운영하던 서비스가 Verify API로 전환하는 방법을 코드 예시와 함께 설명한다. Verify는 OTP 전송·검증을 위한 전용 API로, 전화번호 구매, 토큰 생성, DB 저장·만료 관리를 내부에서 처리해 개발자가 직접 구현할 코드를 크게 줄인다.

    요약 이어보기
    #authentication#twilio#sms+2
  2. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 2부

    포스타입 백엔드 엔지니어가 벡터 기반 개인화 추천 시스템을 실제 운영하며 맞닥뜨린 성능 장애와 용량 문제를 해결한 과정을 담은 2부다. 수백만 개의 벡터 KNN 검색이 피크 시간대에 전체 Elasticsearch 검색 성능을 흔드는 문제부터 클러스터 OOM 사태까지, 쿼리 최적화와 인프라 분리 두 가지 경로로 근본 해결에 이른다.

    요약 이어보기
    #elasticsearch#vector-search#recommendation-system+2
  3. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 1부

    포스타입이 태그 기반 추천의 한계를 극복하고 벡터 임베딩 기반 개인화 추천 시스템을 구축한 과정을 담은 1부다. 유사한 콘텐츠가 다른 용어를 쓰거나 동일한 태그가 전혀 다른 톤의 콘텐츠를 가리키는 문제를 임베딩 벡터로 해결하고, OpenSearch의 HNSW ANN 검색으로 수백만 벡터를 실시간 검색하는 시스템을 구축해 구매율 15% 향상을 달성했다.

    요약 이어보기
    #opensearch#vector-search#recommendation-system+2