React Router v7에서 시간대를 처리하는 방법
사용자의 현지 시간대로 시간 표시하기
문제
애플리케이션이 사용자의 위치를 고려하지 않고 시간을 표시하면 혼란과 오류가 발생합니다. 서버가 이벤트 시간을 "2024-03-15T20:00:00Z"로 저장하고 해당 UTC 값에서 직접 "8:00 PM"을 렌더링할 수 있습니다. 다른 시간대에 있는 사용자들은 동일한 시계 시간을 보지만 이를 자신의 현지 시간으로 해석하여 약속을 놓치고 일정 충돌이 발생합니다. 근본적인 문제는 시간상의 단일 순간이 지리적 위치에 따라 다른 표현을 가지며, 잘못된 시간대로 시간을 표시하면 의미가 없거나 오해를 불러일으킨다는 것입니다.
이 문제는 사용자들이 지역 간에 협업할 때 더욱 심화됩니다. 서버에서 오후 3시로 예약된 이벤트는 UI 어디에서나 오후 3시로 표시되지만, 실제로는 동일한 순간에 대해 도쿄에서는 오전 6시, 런던에서는 오후 2시, 뉴욕에서는 오전 9시로 표시되어야 합니다.
해결 방법
모든 타임스탬프를 UTC 표시가 있는 ISO 8601 문자열이나 Unix 타임스탬프와 같은 범용 형식으로 저장합니다. 이러한 시간을 사용자에게 표시할 때는 JavaScript Date 객체로 변환하고 브라우저의 시간대를 존중하는 국제화 API를 사용하여 형식을 지정합니다. 최신 브라우저는 사용자의 현지 시간대를 자동으로 감지하며, 형식 지정 라이브러리는 이를 활용하여 수동 오프셋 계산 없이 UTC 타임스탬프를 올바른 현지 표현으로 변환합니다.
이 접근 방식은 저장과 표시를 분리합니다. 서버는 하나의 표준 타임스탬프를 전송하고, 각 클라이언트는 자신의 시간대에 따라 렌더링하여 모든 사용자가 동일한 전역 순간에 대해 올바른 현지 시간을 볼 수 있도록 보장합니다.
단계
1. 데이터 소스에서 ISO 8601 형식으로 타임스탬프 전송
API 또는 데이터 로더가 UTC 표시자가 포함된 ISO 8601 문자열 또는 밀리초 단위의 Unix 타임스탬프로 타임스탬프를 반환하도록 합니다.
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>
);
}
FormattedDate 및 FormattedTime 컴포넌트는 브라우저에서 감지된 시간대를 자동으로 사용하여 올바른 현지 시간을 표시합니다. 뉴욕의 사용자는 "2024년 3월 15일 오후 3:00"으로 표시되고, 도쿄의 사용자는 동일한 UTC 타임스탬프에 대해 "2024년 3월 16일 오전 5:00"으로 표시됩니다.
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>
);
}
formatDate 및 formatTime 메서드는 사용자의 로케일 및 시간대에 따라 형식화된 문자열을 반환합니다. dateTime 속성은 기계 가독성을 위해 원본 ISO 문자열을 유지하는 동시에 표시되는 텍스트는 사용자 친화적인 현지 시간을 보여줍니다.