RelativeDateTimeFormatter: 개요, 상대 날짜 및 Swift

저자: IT Sectr 게시일: 2026-07-13 읽는 시간: 10 분

RelativeDateTimeFormatter는 iOS 및 macOS의 Foundation 클래스로, 절대 날짜를 사람이 읽을 수 있는 상대적 표현으로 변환합니다: "5분 전", "어제", "3일 후". Apple Developer Documentation, 2024에 따르면, RelativeDateTimeFormatter는 적절한 단위(초, 분, 시간, 일)를 자동으로 선택하고 장치의 현재 로케일 언어로 출력을 지역화합니다. Calendar를 통해 날짜 간의 차이를 수동으로 계산하는 것과 달리, 이 클래스는 각 언어의 언어학적 특징을 고려합니다: 일부 언어에서는 숫자가 활용되고, 다른 언어에서는 "어제"라는 단어에 특별한 형태가 사용됩니다. 이 클래스는 iOS 13 및 macOS 10.15부터 사용할 수 있습니다.

핵심 요점

  • RelativeDateTimeFormatter — iOS 및 macOS에서 상대 날짜를 표시하는 클래스 (iOS 13+)
  • 지역화된 출력 — 현재 로케일의 언어로 자동으로 표현 선택
  • 세 가지 컨텍스트 유형 — 과거(전), 미래(후), 현재(지금) 다른 표현 사용
  • 자동 단위 선택 — 초, 분, 시간, 일, 주, 월, 년
  • 스타일 사용자 정의 — numeric (3일 후) 또는 abbreviated (3일 후)

RelativeDateTimeFormatter란?

RelativeDateTimeFormatter는 Foundation의 Formatter 서브클래스로, Date(또는 초 단위 차이)를 받아 상대 시간이 포함된 지역화된 문자열을 반환합니다. 예를 들어, 현재보다 5분 전인 날짜의 경우 "5분 전"을 반환합니다. 이 클래스는 세 가지 시간 컨텍스트(과거, 미래, 현재)를 지원합니다.

RelativeDateTimeFormatter의 내부 로직은 Calendar와 Locale을 사용하여 날짜 간의 차이를 계산하고 올바른 문법 형태를 선택합니다. 한국어의 경우 "1분 전"과 "5분 전"을 적절히 선택합니다. 이 기능은 ICU(International Components for Unicode) 데이터를 기반으로 하며 개발자의 추가 구성이 필요하지 않습니다.

Apple WWDC 2019에 따르면, RelativeDateTimeFormatter는 지역화를 간소화하기 위해 프레임워크의 일부가 되었습니다. 도입 전에는 개발자가 수동으로 날짜 차이를 계산하고 String.localizedStringWithFormat을 통해 지역화된 문자열을 대체해야 했습니다. 이로 인해 활용 오류(특히 슬라브어 및 아랍어)와 측정 단위의 잘못된 선택이 발생했습니다.

RelativeDateTimeFormatter는 "5분 전"을 어떻게 표시하나요?

알고리즘은 세 단계로 구성됩니다: 전달된 날짜와 현재 시점 간의 차이 계산, 적절한 단위 선택(0이 되지 않는 가장 큰 것), 로케일에 따른 형식 지정. 예를 들어, 3720초(1시간 2분) 차이의 경우 "시간" 단위가 선택되고 결과는 "1시간 전"이 되며 "62분 전"이 아닙니다.

단위는 "최대 비제로" 원칙에 따라 선택됩니다: 차이가 86400초(1일)를 초과하면 일이 사용되고, 604800(1주)을 초과하면 주가 사용됩니다. 이 알고리즘은 결과가 항상 자연스럽게 읽히도록 보장합니다: "518400초 전" 대신 사용자는 "6일 전"을 봅니다. 단위의 정확한 경계는 현재 로케일의 달력에 의해 결정됩니다.

차이 범위단위ko_KR 예시
0–59초Seconds30초 전
1–59분Minutes5분 전
1–23시간Hours3시간 전
1–6일Days2일 전
7–27일Weeks1주 전
28일–11개월Months3개월 전
12개월 이상Years1년 전

형식 지정 컨텍스트는 구문의 끝을 결정합니다. 과거: "전" (한국어). 미래: "3일 후" (한국어). 현재: "지금" (한국어). 컨텍스트는 localizeString(fromTimeInterval:) 메서드 또는 string(from: Date)를 통해 직접 설정됩니다.

단위 및 스타일 설정

RelativeDateTimeFormatter는 출력을 제어하기 위한 여러 설정을 제공합니다: unitsStyle 속성은 형식 지정 스타일(numeric, abbreviated, full, spellOut)을 결정하고, maximumUnitCount는 표시되는 단위 수를 제한합니다. 예를 들어, maximumUnitCount = 1인 경우 1시간 30분의 차이는 "1시간 30분 전" 대신 "1시간 전"으로 표시됩니다.

형식 지정 스타일

  • .numeric — 전체 숫자 값: "3일 전", "2주 후". 기본 UI에 권장
  • .abbreviated — 축약형: "3일 전", "2주 후". 테이블 및 목록에서 간결한 표시용
  • .full — 숫자 없는 구어체: "삼일 전". 접근성 및 음성 인터페이스용
  • .spellOut — 대체 표기의 문자 형식: "three days ago". 거의 사용되지 않으며 주로 특수 용도로 사용

단위 제한: 기본적으로 RelativeDateTimeFormatter는 하나의(가장 큰) 단위만 표시합니다. maximumUnitCount = 2로 설정하면 더 정확한 설명을 위해 다음 단위가 포함됩니다: "1시간 30분 전". 그러나 이로 인해 짧은 메시지(푸시 알림, 경고)에서 문자열이 지나치게 길어질 수 있습니다. UI의 경우 maximumUnitCount = 1을 유지하는 것이 좋습니다.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()

// Configure styles
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

// Examples with different dates
let fiveMinAgo = Date().addingTimeInterval(-300)
print("5 min ago: \(formatter.localizedString(for: fiveMinAgo, relativeTo: Date()))")

let twoDaysLater = Date().addingTimeInterval(172800)
print("2 days later: \(formatter.localizedString(for: twoDaysLater, relativeTo: Date()))")

// Abbreviated style
formatter.unitsStyle = .abbreviated
let oneWeekAgo = Date().addingTimeInterval(-604800)
print("Abbreviated: \(formatter.localizedString(for: oneWeekAgo, relativeTo: Date()))")

// Full style (spelled out)
formatter.unitsStyle = .full
let threeHours = Date().addingTimeInterval(10800)
print("Full: \(formatter.localizedString(for: threeHours, relativeTo: Date()))")

다른 컨텍스트에 따른 스타일 선택: 뉴스 피드에는 maximumUnitCount = 1과 함께 .numeric을 사용하세요 — 이는 Twitter, Instagram 및 Facebook의 표준입니다. 접근성(VoiceOver)에는 .full을 사용하세요 — 철자가 표시된 숫자가 더 자연스럽게 읽힙니다. 간결한 요소(알림 배지, 상태 표시줄)에는 공간을 절약하기 위해 .abbreviated를 사용하세요.

Swift의 RelativeDateTimeFormatter: 예제

기본 사용법은 인스턴스 생성, 속성 구성 및 형식 지정 메서드 중 하나 호출로 요약됩니다. 주요 메서드는: localizedString(for:relativeTo:) — 날짜 쌍용, localizedString(fromTimeInterval:) — 초 단위 차이용, string(for:) — 자동 컨텍스트(과거/미래)가 있는 Date용입니다.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

// Social network UI examples
let postDates: [(title: String, date: Date)] = [
    ("Just now", Date().addingTimeInterval(-30)),
    ("5 min ago", Date().addingTimeInterval(-300)),
    ("Yesterday", Date().addingTimeInterval(-90000)),
    ("Last week", Date().addingTimeInterval(-700000)),
    ("Last year", Date().addingTimeInterval(-32000000))
]

for (title, postDate) in postDates {
    let relative = formatter.localizedString(
        for: postDate,
        relativeTo: Date()
    )
    print("\(title): \(relative)")
}

// Future dates
let reminderFormatter = RelativeDateTimeFormatter()
reminderFormatter.unitsStyle = .abbreviated
let inOneHour = Date().addingTimeInterval(3600)
let reminderText = reminderFormatter.localizedString(
    for: inOneHour,
    relativeTo: Date()
)
print("Reminder: \(reminderText)")

"방금" 시나리오 처리 — RelativeDateTimeFormatter에는 매우 짧은 간격에 대한 "방금" 표현의 기본 지원이 없습니다. 5초 미만 차이의 경우 "0초 전"을 반환하여 UI에서 보기에 좋지 않습니다. 포맷터 호출을 조건부 로직으로 감싸는 것이 좋습니다: 차이가 설정된 임계값(예: 5초) 미만이면 수동으로 "방금"을 표시하고, 그렇지 않으면 날짜를 포맷터에 전달합니다.

swift
import Foundation

func relativeTimeString(from date: Date) -> String {
    let interval = Date().timeIntervalSince(date)

    // "Just now" threshold
    if interval < 5 {
        return "just now"
    }

    // "Today" threshold
    if interval < 60 {
        return "just now"
    }

    let formatter = RelativeDateTimeFormatter()
    formatter.unitsStyle = .numeric
    formatter.maximumUnitCount = 1

    // Display without "ago" suffix
    return formatter.localizedString(
        for: date,
        relativeTo: Date()
    )
}

print(relativeTimeString(from: Date().addingTimeInterval(-3)))
print(relativeTimeString(from: Date().addingTimeInterval(-120)))
print(relativeTimeString(from: Date().addingTimeInterval(-3600)))

string(fromTimeInterval:) 메서드는 초 단위 차이를 받아 자동으로 컨텍스트를 결정합니다(양수 값 — 미래, 음수 값 — 과거). 이는 차이를 이미 알고 있는 경우(예: 서버에서 Unix 타임스탬프로 수신)에 편리합니다. 이 경우 Date를 생성할 필요 없이 차이가 직접 전달됩니다.

상대 날짜의 지역화

RelativeDateTimeFormatter는 Locale.current를 기반으로 자동으로 출력을 지역화합니다. 형식 지정 언어를 변경하려면 locale 속성을 설정하세요 — DateFormatter와 달리 RelativeDateTimeFormatter의 로케일은 고정되어 있지 않으며 호출마다 변경할 수 있습니다. 이를 통해 인터페이스 언어와 다른 언어로 상대 날짜를 표시할 수 있습니다(예: 원래 언어의 콘텐츠).

상대 날짜 지역화의 복잡성은 다양한 언어의 문법적 특징에 있습니다. 한국어는 "1분 전", "5분 전"과 같이 숫자 뒤에 적절한 조사가 붙습니다. 아랍어는 3~10의 숫자에 복수형을, 11 이상에는 특수 형태를 사용합니다. 중국어는 활용이 없어 작업이 간단합니다. RelativeDateTimeFormatter는 ICU 규칙을 통해 이러한 모든 경우를 처리하며 추가 코드가 필요하지 않습니다.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

let targetDate = Date().addingTimeInterval(-7200) // 2 hours ago

// Different locales
let locales: [String] = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "ar_SA"]

for identifier in locales {
    formatter.locale = Locale(identifier: identifier)
    let result = formatter.localizedString(
        for: targetDate,
        relativeTo: Date()
    )
    print("\(identifier): \(result)")
}

// Check Korean pluralization
formatter.locale = Locale(identifier: "ru_RU")
let intervals: [TimeInterval] = [-60, -120, -180, -300]
for interval in intervals {
    let date = Date().addingTimeInterval(interval)
    print("\(-Int(interval / 60)) min: \(formatter.localizedString(for: date, relativeTo: Date()))")
}

중요한 차이점: RelativeDateTimeFormatter는 .numeric 설정의 차이 계산 시 TimeZone을 무시합니다 — 초 단위의 절대 차이를 사용합니다. 그러나 .full 스타일(철자 표시 숫자) 및 특수 경우(어제, 오늘)에는 TimeZone이 고려됩니다. 일관성을 위해 항상 TimeZone을 명시적으로 설정하세요, 특히 애플리케이션이 UTC의 서버 날짜로 작업하는 경우 중요합니다.

일반적인 형식 지정 오류

상대 날짜 계산 시 TimeZone 무시 — 서버 날짜로 작업할 때 흔한 실수입니다. 서버가 UTC로 Date를 보내고 RelativeDateTimeFormatter가 TimeZone.current를 사용하는 경우 현재 시간에 가까운 날짜의 차이가 잘못 계산될 수 있습니다. 서버 데이터에는 항상 formatter.timeZone = TimeZone(secondsFromGMT: 0)을 설정하는 것이 좋습니다.

짧은 간격에 대한 잘못된 단위 선택 — RelativeDateTimeFormatter는 차이를 가장 큰 단위로 반올림합니다. 25시간의 경우 결과는 "1일 전"이 되어 사용자를 오도할 수 있습니다. 높은 정밀도가 필요한 경우(예: 카운트다운 타이머), RelativeDateTimeFormatter 대신 DateComponentsFormatter를 사용하세요 — 여러 단위를 동시에 표시할 수 있습니다.

음수 TimeInterval 확인 부족 — 미래 날짜가 과거로 전달된 경우(string(fromTimeInterval:)에서 음수 값), 포맷터가 잘못된 문자열을 반환할 수 있습니다. 포맷터에 전달하기 전에 항상 간격의 부호를 확인하세요, 특히 시간대가 계산을 왜곡할 수 있는 서버 데이터로 작업할 때 중요합니다.

Hacker News(2024)에 따르면, RelativeDateTimeFormatter의 가장 논의된 문제 중 하나는 영어에서 "어제"와 "오늘"의 기본 지원 부족입니다. "어제" 대신 90000초 차이의 포맷터는 "1일 전"을 반환합니다. 한국어에는 이러한 문제가 없으며 "1일 전"은 자연스럽게 들리지만 영어 UI에서는 "yesterday"가 더 바람직합니다. 이 기능은 지원되지 않으며 Calendar.isDateInToday/Yesterday를 통한 수동 확인이 필요합니다.

자주 묻는 질문

RelativeDateTimeFormatter란?

RelativeDateTimeFormatter는 날짜를 상대 형식으로 표시하는 Foundation 클래스입니다: "5분 전", "2일 후". iOS 13 및 macOS 10.15부터 사용 가능합니다.

RelativeDateTimeFormatter는 단위를 어떻게 선택하나요?

최대 비제로 단위 원칙에 따라 — 초, 분, 시간, 일, 주, 월, 년. 예를 들어, 3720초(1시간 2분) 차이의 경우 "시간" 단위가 선택되며 "분"이 아닙니다.

출력 언어를 변경하려면?

locale 속성을 설정하세요원하는 Locale 인스턴스로. 기본적으로 Locale.current가 사용됩니다. 예: 독일어의 경우 formatter.locale = Locale(identifier: "de_DE").

.numeric과 .abbreviated의 차이는?

.numeric — 전체 형식("3일 전"), .abbreviated — 축약 형식("3일 전"). 선택은 컨텍스트에 따라 다릅니다: 기본 UI에는 numeric, 간결한 요소에는 abbreviated.

"0초 전" 대신 "방금"을 표시하려면?

수동 확인을 추가하세요5-10초 미만 간격에 대해. RelativeDateTimeFormatter는 "방금"을 지원하지 않습니다 — 작은 간격에 대해 "0초 전"을 반환합니다. 임계값이 있는 조건부 로직을 사용하세요.

요약

  • RelativeDateTimeFormatter — iOS 13+에서 상대 날짜를 표시하는 편리한 클래스
  • 자동 지역화 — ICU를 통한 모든 지원 언어의 올바른 활용
  • 세 가지 스타일 — .numeric(표준), .abbreviated(간결), .full(문자 표기)
  • 단위 선택 — 최대 비제로 값 기반 자동 선택
  • TimeZone 설정 — 서버 날짜 작업 시 일관성을 위해 필수
  • "방금" 임계값 — 기본 지원 안 됨; 수동 간격 확인 필요
  • "어제" 지원 없음 — 포맷터가 영어의 yesterday 형식을 사용하지 않음

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기