Next.js(Pages Router) v16에서 다양한 로케일에 맞게 숫자 형식을 지정하는 방법

로케일별 구분 기호를 사용하여 숫자 표시하기

문제

숫자는 전 세계적으로 다르게 표기됩니다. 미국에서 10,000.5로 표시되는 것이 독일에서는 10.000,5로 표시됩니다—쉼표와 마침표가 완전히 역할을 바꿉니다. 이것은 선호도나 스타일의 문제가 아니라 가독성의 문제입니다. 독일 사용자가 10,000.5를 보면 그룹 구분 기호를 무시하고 10으로 읽을 수 있습니다. 미국 사용자가 10.000,5를 보면 소수점 구분 기호를 무시하고 만 단위로 읽을 수 있습니다. 같은 숫자지만 정반대의 의미를 가집니다.

애플리케이션이 로케일을 고려하지 않은 형식으로 숫자를 표시할 때, 사용자를 혼란스럽게 하거나 잘못된 정보를 전달할 위험이 있습니다. 통화 금액, 백분율, 측정값 및 통계는 모두 사용자가 어린 시절부터 배워온 지역 관습에 따라 달라집니다.

해결책

사용자의 로케일에 기반하여 숫자를 형식화하고, 소수점과 그룹 구분 기호에 대한 지역 규칙을 사용합니다. 이는 숫자 값을 해당 지역의 사용자에게 익숙한 형식 규칙을 따르는 문자열로 변환합니다.

react-intl은 두 가지 접근 방식을 제공합니다: JSX에서 선언적 형식화를 위한 <FormattedNumber> 컴포넌트와 명령적 형식화를 위한 useIntl 훅의 formatNumber 메서드입니다. 둘 다 브라우저의 Intl.NumberFormat API에 의존하며 IntlProvider에 구성된 로케일을 자동으로 적용합니다. 형식화된 출력은 천 단위 구분 기호, 소수점 및 숫자 그룹화에 대한 지역 관습을 준수합니다.

단계

1. 선언적으로 숫자를 형식화하는 컴포넌트 생성하기

'<FormattedNumber>' 컴포넌트는 'formatNumber'와 'Intl.NumberFormat' API를 사용하며 'Intl.NumberFormatOptions'에 해당하는 옵션과 함께 'value' prop을 받습니다.

import { FormattedNumber } from "react-intl";

export default function ProductPrice({ price }: { price: number }) {
  return (
    <div>
      <FormattedNumber value={price} />
    </div>
  );
}

기본적으로 <FormattedNumber>는 형식화된 숫자를 React.Fragment로 렌더링합니다. 이 컴포넌트는 가장 가까운 IntlProvider에서 로케일을 읽고 적절한 형식화 규칙을 적용합니다.

2. 특정 옵션으로 숫자 형식 지정하기

숫자가 표시되는 방식을 제어하기 위해 형식 지정 옵션을 전달하세요. 일반적인 옵션으로는 minimumFractionDigits, maximumFractionDigits, style 등이 있습니다.

import { FormattedNumber } from "react-intl";

export default function Statistics({ value }: { value: number }) {
  return (
    <dl>
      <dt>총 사용자</dt>
      <dd>
        <FormattedNumber
          value={value}
          minimumFractionDigits={0}
          maximumFractionDigits={0}
        />
      </dd>
      <dt>전환율</dt>
      <dd>
        <FormattedNumber
          value={value / 100}
          style="percent"
          minimumFractionDigits={2}
        />
      </dd>
    </dl>
  );
}

이러한 속성들은 Intl.NumberFormatOptions에 해당하며, 로케일별 구분 기호를 유지하면서 정밀도와 표현 방식을 제어할 수 있습니다.

3. useIntl 훅을 사용하여 명령적으로 숫자 형식 지정하기

컴포넌트가 함수 컴포넌트로 표현될 수 있을 때, useIntl 훅은 formatNumber 메서드를 포함하는 intl 객체에 접근할 수 있게 해줍니다.

import { useIntl } from "react-intl";

export default function DataTable({ rows }: { rows: number[] }) {
  const intl = useIntl();

  return (
    <table>
      <tbody>
        {rows.map((value, index) => (
          <tr key={index}>
            <td>{intl.formatNumber(value)}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

formatNumber 함수는 형식이 지정된 숫자 문자열을 반환하며, 숫자로 파싱할 수 있는 값과 NumberFormatOptions를 준수하는 옵션을 받습니다.

4. JSX가 아닌 컨텍스트에서 숫자 형식 지정하기

속성, 변수 또는 컴포넌트를 사용할 수 없는 다른 컨텍스트에서 형식이 지정된 숫자가 필요할 때 formatNumber를 사용하세요.

import { useIntl } from "react-intl";

export default function Chart({ dataPoints }: { dataPoints: number[] }) {
  const intl = useIntl();

  const formattedLabel = intl.formatNumber(dataPoints[0], {
    notation: "compact",
    maximumFractionDigits: 1,
  });

  return (
    <div>
      <img
        src="/chart.png"
        alt={`${formattedLabel} 항목을 보여주는 차트`}
        title={formattedLabel}
      />
    </div>
  );
}

명령적 API는 React 컴포넌트를 렌더링할 수 없는 title, aria-label, alt와 같은 텍스트 속성을 설정하는 데 필수적입니다.