React Router v7에서 시간대를 처리하는 방법

사용자의 현지 시간대로 시간 표시하기

문제

애플리케이션이 사용자의 위치를 고려하지 않고 시간을 표시할 때 혼란과 오류가 발생합니다. 서버는 이벤트 시간을 "2024-03-15T20:00:00Z"와 같이 저장하고 해당 UTC 값에서 직접 "8:00 PM"으로 렌더링할 수 있습니다. 다른 시간대에 있는 사용자들은 동일한 시계 시간을 보지만 이를 자신의 지역 시간으로 해석하여 약속을 놓치거나 일정 충돌이 발생합니다. 근본적인 문제는 하나의 시점이 지리적 위치에 따라 다른 표현을 가지며, 잘못된 시간대로 시간을 표시하면 의미가 없거나 오해의 소지가 있다는 것입니다.

이 문제는 사용자가 여러 지역에서 협업할 때 더욱 복잡해집니다. 서버에서 오후 3시로 예약된 이벤트가 UI에서는 모든 곳에서 오후 3시로 표시되지만, 실제로는 동일한 순간에 도쿄에서는 오전 6시, 런던에서는 오후 2시, 뉴욕에서는 오전 9시로 표시되어야 합니다.

해결책

모든 타임스탬프를 UTC 표시기가 있는 ISO 8601 문자열이나 유닉스 타임스탬프와 같은 범용 형식으로 저장하세요. 이러한 시간을 사용자에게 표시할 때는 JavaScript Date 객체로 변환하고 브라우저의 시간대를 존중하는 국제화 API를 사용하여 형식을 지정하세요. 최신 브라우저는 사용자의 로컬 시간대를 자동으로 감지하며, 포맷팅 라이브러리는 이를 활용하여 수동 오프셋 계산 없이 UTC 타임스탬프를 올바른 로컬 표현으로 변환합니다.

이 접근 방식은 저장과 표현을 분리합니다. 서버는 하나의 표준 타임스탬프를 보내고, 각 클라이언트는 자신의 시간대에 따라 렌더링하여 모든 사람이 동일한 글로벌 순간에 대해 올바른 현지 시간을 볼 수 있도록 합니다.

단계

1. 데이터 소스에서 ISO 8601 형식으로 타임스탬프 전송

API 또는 데이터 로더가 타임스탬프를 UTC 표시기가 있는 ISO 8601 문자열 또는 밀리초 단위의 유닉스 타임스탬프로 반환하도록 하세요.

export async function loader() {
  const event = await fetchEvent();
  return {
    title: event.title,
    startTime: "2024-03-15T20:00:00Z",
    endTime: "2024-03-15T22:00:00Z",
  };
}

Z 접미사는 UTC 시간을 나타냅니다. 이 문자열이 JavaScript Date 객체로 파싱될 때, 브라우저는 내부적으로 UTC 타임스탬프로 저장합니다.

2. react-intl을 사용하여 시간을 포맷팅하는 컴포넌트 생성하기

react-intl에서 포맷팅 컴포넌트를 가져와서 ISO 문자열로부터 생성된 Date 객체를 전달합니다.

import { FormattedDate, FormattedTime } from "react-intl";
import type { Route } from "./+types/event";

export default function EventDetails({ loaderData }: Route.ComponentProps) {
  const startTime = new Date(loaderData.startTime);
  const endTime = new Date(loaderData.endTime);

  return (
    <div>
      <h1>{loaderData.title}</h1>
      <p>
        <FormattedDate
          value={startTime}
          year="numeric"
          month="long"
          day="numeric"
        />
        {" at "}
        <FormattedTime value={startTime} />
        {" to "}
        <FormattedTime value={endTime} />
      </p>
    </div>
  );
}

FormattedDateFormattedTime 컴포넌트는 브라우저에서 감지된 시간대를 자동으로 사용하여 올바른 현지 시간을 표시합니다. 뉴욕의 사용자는 동일한 UTC 타임스탬프에 대해 "March 15, 2024 at 3:00 PM"을 보는 반면, 도쿄의 사용자는 "March 16, 2024 at 5:00 AM"을 봅니다.

3. 동적 포맷팅 요구 사항에 명령형 API 사용하기

속성, 계산된 값 또는 JSX가 아닌 컨텍스트에서 포맷팅된 시간이 필요할 때는 useIntl 훅을 사용하세요.

import { useIntl } from "react-intl";
import type { Route } from "./+types/event";

export default function EventCard({ loaderData }: Route.ComponentProps) {
  const intl = useIntl();
  const startTime = new Date(loaderData.startTime);

  const formattedDate = intl.formatDate(startTime, {
    year: "numeric",
    month: "short",
    day: "numeric",
  });

  const formattedTime = intl.formatTime(startTime, {
    hour: "numeric",
    minute: "2-digit",
  });

  return (
    <div>
      <h2>{loaderData.title}</h2>
      <time dateTime={loaderData.startTime}>
        {formattedDate} at {formattedTime}
      </time>
    </div>
  );
}

formatDateformatTime 메서드는 사용자의 로케일과 시간대에 따라 포맷팅된 문자열을 반환합니다. dateTime 속성은 기계가 읽을 수 있도록 원래 ISO 문자열을 보존하는 동시에 표시되는 텍스트는 사용자 친화적인 현지 시간을 보여줍니다.