블로그 목록
frontend

Intl.RelativeTimeFormat으로 '3분 전' 표시하기 — 날짜 라이브러리 없이

댓글이나 알림 목록에 "3분 전", "2시간 전" 같은 상대 시간을 붙이는 건 흔한 요구사항이다. 예전엔 이거 하나 때문에 moment나 dayjs + relativeTime 플러그인을 습관처럼 설치했다. 근데 상대 시간 표시 하나 쓰려고 번들에 라이브러리를 통째로 넣는 게 늘 좀 아깝다는 생각이 들었다.

이제는 브라우저에 Intl.RelativeTimeFormat이 들어있다. 별도 설치 없이 되고, 다국어까지 알아서 처리해준다.

기본 사용법

포맷터에 숫자와 단위를 넘기면 그 나라 표현으로 문자열을 만들어준다. 음수는 과거, 양수는 미래다.

const rtf = new Intl.RelativeTimeFormat("ko", { numeric: "auto" });

rtf.format(-3, "minute"); // "3분 전"
rtf.format(-2, "hour");   // "2시간 전"
rtf.format(1, "day");     // "내일"
rtf.format(-1, "day");    // "어제"

numeric: "auto"가 포인트다. 이걸 켜두면 -1, "day"가 "1일 전"이 아니라 "어제"로 나온다. 딱딱한 숫자 대신 자연스러운 표현이 필요할 때 유용하다. 반대로 항상 숫자를 원하면 numeric: "always"를 쓴다.

실전: 타임스탬프를 상대 시간으로

실제로는 서버에서 받은 ISO 날짜 문자열을 넘겨서 알아서 적절한 단위를 골라주는 함수가 필요하다. 초/분/시간/일 중에 어떤 걸 쓸지 직접 계산해주면 된다.

const DIVISIONS: [number, Intl.RelativeTimeFormatUnit][] = [
  [60, "second"],
  [60, "minute"],
  [24, "hour"],
  [7, "day"],
  [4.34524, "week"],
  [12, "month"],
  [Infinity, "year"],
];

export function timeAgo(date: Date, locale = "ko") {
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
  let duration = (date.getTime() - Date.now()) / 1000;

  for (const [amount, unit] of DIVISIONS) {
    if (Math.abs(duration) < amount) {
      return rtf.format(Math.round(duration), unit);
    }
    duration /= amount;
  }
}

timeAgo(new Date(Date.now() - 90_000)); // "2분 전"

단위를 하나씩 나눠가며 Math.abs가 임계값보다 작아지는 순간의 단위로 포맷한다. 로케일만 바꾸면 "en"에서는 "2 minutes ago", "ja"에서는 "2 分前"으로 그대로 나온다. 번역 테이블을 따로 관리할 필요가 없다.

마무리

Intl.RelativeTimeFormat은 모든 최신 브라우저에서 몇 년째 지원된다. 상대 시간 하나 때문에 날짜 라이브러리를 통째로 들고 오던 습관이 있다면, 이제는 내장 API로 충분하다. 포맷터 생성이 살짝 비싼 편이라 컴포넌트 바깥에서 한 번 만들어 재사용하는 것 정도만 신경 쓰면 된다.