'1998-07-02' 형태의 Json 데이터(String)를 변형하여 'June 26, 1997'(String) 형태로 표시하려고 한다
1. DateFormmater
- DateFormatter는 Foundation에서 제공하는 클래스로 날짜를 문자열로 변환하거나 반대로 변환하는 기능을 한다
- String 값을 dateFormmater를 이용하여 Date 타입으로 변환
- Date 타입의 dateStyle, locale을 설정하여 Date → String 타입으로 변환
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// '1998-07-02' 형태로 되어있는 Json 데이터를 변형하여 'June 26, 1997' 형태로 표시
let releaseString = try container.decode(String.self, forKey: .releaseDate)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
// String -> Date 타입으로 변환
guard let date = dateFormatter.date(from: releaseString) else {
self.releaseDate = "Formmating Error"
return
}
// '1998-07-02'-> 'June 26, 1997' 변환 ('en_US' Locale 사용)
dateFormatter.dateStyle = .medium
dateFormatter.locale = Locale(identifier: "en_US")
self.releaseDate = dateFormatter.string(from: date)
}
→ dateFormmater의 dateStyle을 .medium으로 설정했다. 이게 뭘까?
dateStyle: DateFormatter.Style
- https://developer.apple.com/documentation/foundation/dateformatter/style
- DateFormatter에서 사용하는 enum 타입으로 날짜와 시간을 설정하는 속성
- 날짜 및 시간 스타일의 형식은 Locale, 사용자 환경 설정 및 운영체제 버전에 따라 달라져서 정확하지 않음
- 정확한 형식을 원하는 경우 이러한 enum 타입을 사용하면 안 됨
- Case
- .none: 스타일을 지정하지 않음
- .short: 숫자로만 구성된 짧은 스타일(예: "11/23/37" 또는 "3:30 PM")
- .medium: "1937년 11월 23일"이나 "오후 3시 30분 32초"와 같이 약어가 포함된 중간 스타일을 지정
- .long: "1937년 11월 23일" 또는 "오후 3시 30분 32초 PST"와 같이 전체 텍스트가 포함된 긴 스타일
- .full: “Tuesday, April 12, 1952 AD” or “3:30:42 PM”와 같이 모든 세부 정보가 포함된 전체 스타일
→ 현재 코드에서는 dateStyle을 .medium으로 지정했다
왜냐? 'June 26, 1997' 형식으로 Date를 변경하기 위해 Locale을 변경하는데 Locale을 변경할 때 Time 정보가 존재 하지 않으므로 제대로 Formatting되지 않음
따라서 Time을 표현하지 않는 .medium 타입으로 변경한 후 Locale을 변경해주는 것!
DateFormmater의 단점
- 성능 이슈
- DateFormatter는 생성될 때마다 내부적으로 비용이 큰 초기화 작업을 수행함
- 특히, dateFormat을 자주 변경하면 매번 새로운 객체를 생성하는 것과 동일한 오버헤드 발생
- 로컬라이징 문제
- dateFormat을 집적 문자열로 설정하면 언어 및 지역에 따라 포맷이 깨질 가능성 있음
- 타입 안전성 부족
- dateFormat은 문자열이므로, 오타가 있어도 컴파일러가 체크해주지 않음
- “YYYY’ 와 ‘yyyy’의 차이처럼 잘못된 포맷을 사용하면 예상치 못한 오류 발생 가능
2. Date.FormatStyle
- Swift 5.5(iOS 15, macOS 12)부터 도입된 타입 안전한 날짜 포맷 방식
- DateFormmater의 단점을 해결하기 위해 등장함
https://developer.apple.com/documentation/foundation/date/formatstyle
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// '1998-07-02' 형태로 되어있는 Json 데이터를 변형하여 'June 26, 1997' 형태로 표시
let releaseString = try container.decode(String.self, forKey: .releaseDate)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
// String -> Date 타입으로 변환
guard let date = dateFormatter.date(from: releaseString) else {
self.releaseDate = "Formmating Error"
return
}
// '1998-07-02'-> 'June 26, 1997' 변환
self.releaseDate = date.formatted(date: .abbreviated, time: .omitted)
}
formatted(date: time:) 메서드 분석
1. date: 파라미터 (날짜 포맷 스타일)
- date 매개변수는 날짜(Date)를 어떻게 표현할지 지정함
- https://developer.apple.com/documentation/foundation/date/formatstyle/datestyle
옵션 | 설명 | 예제 출력 (2025년 3월 26일 기준) |
.omitted | 날짜 관련 구성 요소가 표현되지 않은 날짜 스타일입니다. | (날짜 생략) |
.numeric | 월, 일, 연도 구성 요소를 숫자 값으로 표현한 날짜 스타일입니다. | "3/26/2025" |
.abbreviated | 공간이 제한된 애플리케이션을 위해 일부 구성 요소가 축약된 날짜 스타일입니다. | "Mar 26, 2025" |
.long | 전체 월, 일, 연도 구성 요소를 표현한 확장된 날짜 스타일입니다. | "March 26, 2025" |
.complete | 모든 구성 요소가 표현된 날짜 스타일입니다. | "Wednesday, March 26, 2025" |
let now = Date()
print(now.formatted(date: .omitted, time: .standard)) // "2:30 PM"
print(now.formatted(date: .numeric, time: .omitted)) // "3/26/2025"
print(now.formatted(date: .abbreviated, time: .omitted)) // "Mar 26, 2025"
print(now.formatted(date: .long, time: .omitted)) // "March 26, 2025"
print(now.formatted(date: .complete, time: .omitted)) // "Wednesday, March 26, 2025"
2. time: 파라미터 (시간 포맷 스타일)
- time 매개변수는 시간을 어떻게 표현할지를 지정함
옵션 | 설명 | 예제 출력 (오후 2시 30분 기준) |
.omitted | 시간 관련 구성 요소가 표현되지 않은 시간 스타일입니다. | (시간 생략) |
.shortened | 시간, 분, 일 단위의 구성 요소만 표현된 단축된 시간 스타일입니다. | "2:30 PM" |
.standard | 시간대를 제외한 모든 구성 요소를 나타낸 시간 스타일입니다. | "2:30:45 PM" |
.complete | 모든 구성 요소가 표현된 시간 스타일입니다. | "2:30:45 PM Coordinated Universal Time" |
let now = Date()
print(now.formatted(date: .numeric, time: .omitted)) // "3/26/2025"
print(now.formatted(date: .numeric, time: .shortened)) // "3/26/2025, 2:30 PM"
print(now.formatted(date: .numeric, time: .standard)) // "3/26/2025, 2:30:45 PM"
print(now.formatted(date: .numeric, time: .complete)) // "3/26/2025, 2:30:45 PM Coordinated
Date.FormatStyle의 단점
- String → Date를 지원하지 않음
- Formatter가 아니라 타입 안전성을 강화한 구조체(struct) 기반 API
- 변환 방향(Date → String)을 명확하게 제한하여 실수로 인한 오류를 줄이고 성능을 최적화하려는 의도를 가짐
- String → Date로 변환하려면 DateFormatter를 사용해야 함
DateFormatter VS Date.FormatStyle 차이점 정리
특징 | DateFormatter | FormatStyle |
타입 | 클래스 (DateFormatter) | 구조체 (FormatStyle) |
성능 | 초기화 비용이 큼, 캐싱 필요 | 더 가볍고 빠름 |
타입 안전성 | 문자열 기반 (dateFormat)으로 오류 발생 가능 | 메서드 체이닝 방식으로 타입 안전함 |
로컬라이징 | Locale을 수동으로 설정해야 함 | 자동 로컬라이징 지원 |
사용성 | dateFormat을 직접 지정해야 함 | .year().month().day()처럼 직관적 |
날짜 변환 | formatter.string(from:), formatter.date(from:) | .formatted(), Date(_:strategy:) |
가독성 | 문자열 기반이라 가독성이 낮음 | 체이닝 방식으로 직관적 |
Date.FormatStyle은 DateFormatter보다 빠르고, 안전하고, 직관적인 API이다.
formatted(date:time:)를 활용하면 날짜와 시간을 간단하게 변환할 수 있다.
iOS 15 미만을 지원해야 한다면 DateFormatter를 사용해야 하지만, iOS 15 이상을 지원하는 프로젝트에서는 DateFormatter보다 Date.FormatStyle을 적극 활용하는 것이 좋다.
String -> Date 변환이 필요하면 DateFormatter를 사용해야 한다
'Today I Learn' 카테고리의 다른 글
Swift - NSCache + Actor 기반 이미지 캐시 매니저 구현기(1) (1) | 2025.04.11 |
---|---|
RxSwift에서 observe(on:)으로도 UI 업데이트가 즉시 반영되지 않는 이유와 해결법 (1) | 2025.04.01 |
Today I Learn - 배열의 짝수 번째 요소를 제거하는 다양한 방법 (0) | 2025.03.20 |
Today I Learn: @escaping 클로저 (0) | 2025.03.14 |
Today I Learn: Swift에서의 Copy On Write (COW) (0) | 2025.03.12 |