블로그 목록
frontend

Intl.DurationFormat으로 "1시간 30분" 표시하기 — 초를 직접 나눠 붙이던 코드 그만

영상 재생 길이나 "예상 소요 시간" 같은 걸 화면에 뿌릴 때마다 비슷한 유틸 함수를 또 짜고 있었다. 초를 받아서 60으로 나누고, 나머지 구하고, 1시간 넘으면 "시간"도 붙이고... 매번 조금씩 다르게.

function formatDuration(totalSeconds: number) {
  const h = Math.floor(totalSeconds / 3600);
  const m = Math.floor((totalSeconds % 3600) / 60);
  const s = totalSeconds % 60;
  const parts = [];
  if (h) parts.push(`${h}시간`);
  if (m) parts.push(`${m}분`);
  if (s) parts.push(`${s}초`);
  return parts.join(' ');
}

동작은 한다. 근데 "시간/분/초" 단어가 코드에 박혀 있으니 다국어 대응이 안 되고, 0인 단위를 숨기는 규칙도 손으로 관리해야 한다. 이런 건 이미 브라우저가 해준다.

Intl.DurationFormat

Intl.DurationFormat은 { hours, minutes, seconds } 같은 객체를 받아서 로케일에 맞는 문자열로 만들어준다. 2025년에 Baseline에 들어왔고, 최신 크롬·사파리·파이어폭스에서 다 돈다.

const fmt = new Intl.DurationFormat('ko', { style: 'narrow' });

fmt.format({ hours: 1, minutes: 30 });
// "1시간 30분"

new Intl.DurationFormat('en', { style: 'narrow' }).format({ hours: 1, minutes: 30 });
// "1h 30m"

style은 long("1시간 30분 ..."은 "1 hour, 30 minutes"), short("1 hr, 30 min"), narrow("1h 30m") 중에 고르면 된다. 로케일 문자열만 바꾸면 단어가 알아서 따라오는 게 핵심이다.

초 하나만 들어올 때

보통 API나 <video>에서 넘어오는 건 초 하나다. 큰 단위로 쪼개는 건 여전히 직접 해야 하지만, 단어 붙이기와 로케일은 포매터에 넘기면 된다.

function format(totalSeconds: number, locale = 'ko') {
  const duration = {
    hours: Math.floor(totalSeconds / 3600),
    minutes: Math.floor((totalSeconds % 3600) / 60),
    seconds: totalSeconds % 60,
  };
  return new Intl.DurationFormat(locale, { style: 'narrow' }).format(duration);
}

format(5415); // "1시간 30분 15초"

0인 단위를 자동으로 빼고 싶으면 값이 있는 것만 객체에 담으면 된다. 단위별로 hoursDisplay: 'always' 같은 옵션으로 강제 표시도 조절할 수 있다.

마무리

재생 길이, 남은 시간, 소요 시간처럼 "기간"을 보여주는 자리라면 이제 유틸 함수를 새로 짤 필요가 없다. 나누는 로직만 남기고 포맷은 Intl에 맡기면, 다국어도 공짜로 따라온다. 상대 시간은 Intl.RelativeTimeFormat, 기간은 Intl.DurationFormat — 이렇게 역할을 나눠 쓰면 깔끔하다.