IT Study/iOS

[iOS] Swift에서 날짜 기반 채팅 목록 그룹화 개선하기 (Feat. 그룹화 우선순위의 오류, sortWeight)

three kim 2025. 1. 30. 17:01
728x90

"01월"은 2025년 1월이다...

 

ㅡAloneChat의 groupChatRoms() 정렬 문제 해결 과정에 대해 다루고 있습니다.

 

1. 문제 정의 : 연도별 정렬 오류

AloneChat에는 채팅 목록을 날짜별로 그룹화하는 기능이 있습니다. 이 기능은 2024년 연말까지는 문제없는 듯 보였지만, 2025년 새해가 되면서(ㅋㅋ ㅠㅠ 바보...) 2024년 그룹이 2025년 1월보다 위에 표시되는 문제가 발생하였습니다.

 

내가 기대하는 정렬

  • 오늘
  • 어제
  • 02월 (2025년 2월)
  • 01월 (2025년  1월)
  • 2024년 (이전 연도는 한 그룹으로 묶음)

 

현재 코드에서 발생하는 문제

2024년이 2025년 1월보다 위에 정렬되는 것이 문제가 됩니다.

 


 

2. 문제 원인 분석

이전에는 Dictionary<String, [ChatRoom]> 형태로 반환한 뒤, Dictionary의 Key인 문자열(오늘, 내일 등의 문자 표시)를 문자열 내림차순으로 정렬하고 있었습니다...

ㅡ정말 대충격입니다... AloneChat 개발을 시작한 10월부터 "오늘, 내일, 12월, 11월, 10월"의 정렬만 봤기에 지금까지는 문제가 없는 줄 알았습니다. 지금까지의 방식은 아래와 같은 문제가 생길 수 있습니다.

  1. "오늘", "어제" 등 문자표기가 들어오면 사전순과 의도한 순서가 엇갈릴 수 있다는 것.
  2. 한글, 숫자, 연도("2024년")를 단순 문자열로 정렬하면 예기치 못한 순서가 만들어진다는 것.
  3. 결국 2025년 1월과 2024년이 서로 정확히 어떤 순서로 비교되어야 하는지, 문자열 비교만으로는 해결할 수 없다는 것.

 


 

3. 해결 방법: 그룹 우선순위 + 정렬된 배열 의 반환

제가 원하는 건 "오늘 → 어제 → 올해(월 순서) → 이전 연도(연 순서)" 정렬입니다. 이를 위해 sortWeight(우선순위)를 부여해 "그룹별" 정렬 기준을 명확히 하고, 그 결과를 기존 Dictionary 대신, 이미 정렬된 배열의 형태로 만들어서 UI에 넘기면 됩니다. (그리고 UI에서는 배열 그대로 사용하면 되죠.)

  1. sortWeight 도입
    • 오늘 (3) > 어제 (2) > 현재 연도 월별 (1) > 이전 연도 (0)
    • 같은 우선순위 내에서는 날짜 내림차순으로 정렬 (예: 2월 > 1월)
  2. 최종 반환은 [(groupKey: String, rooms: [ChatRoom])] 형태의 정렬된 배열
    • Dictionary를 사용하면 키 정렬에 또다시 사전식 분류가 들어가 정렬 순서가 뒤죽박죽됩니다.
    • 배열로 준다면 ForEach로 배열을 그대로 순회해, 원하는 순서를 확실히 유지할 수 있습니다.

 


 

4. 해결 코드

private func groupChatRooms(_ chatRooms: [ChatRoom]) -> [(groupKey: String, rooms: [ChatRoom])] {
    let dateFormatter = DateFormatter()
    let calendar = Calendar.current
    let today = calendar.startOfDay(for: Date())
    let yesterday = calendar.date(byAdding: .day, value: -1, to: today)
    let currentYear = calendar.component(.year, from: today)

    // (키, 날짜, 우선순위, ChatRoom)을 임시 저장할 배열
    var groupedRooms: [(key: String, date: Date, sortWeight: Int, rooms: [ChatRoom])] = []

    for chatRoom in chatRooms {
        let chatDate = calendar.startOfDay(for: chatRoom.lastTimestamp)
        let chatYear = calendar.component(.year, from: chatRoom.lastTimestamp)

        let groupKey: String
        let sortWeight: Int

        if chatDate == today {
            groupKey = Constants.ChatList.today
            sortWeight = 3

        } else if chatDate == yesterday {
            groupKey = Constants.ChatList.yesterday
            sortWeight = 2

        } else if chatYear == currentYear {
            dateFormatter.dateFormat = Constants.ChatList.currentYearFormat
            groupKey = dateFormatter.string(from: chatRoom.lastTimestamp)
            sortWeight = 1

        } else {
            dateFormatter.dateFormat = Constants.ChatList.previousYearFormat
            groupKey = dateFormatter.string(from: chatRoom.lastTimestamp)
            sortWeight = 0
        }

        if let index = groupedRooms.firstIndex(where: { $0.key == groupKey }) {
            groupedRooms[index].rooms.append(chatRoom)
        } else {
            groupedRooms.append((key: groupKey, date: chatDate, sortWeight: sortWeight, rooms: [chatRoom]))
        }
    }

    // 1) 그룹 우선수누위 내림차순
    // 2) 우선순위가 같으면 날짜 내림차순
    groupedRooms.sort {
        if $0.sortWeight == $1.sortWeight {
            return $0.date > $1.date
        }
        return $0.sortWeight > $1.sortWeight
    }

    // 이미 정렬된 상태의 (groupKey, rooms) 배열 반환
    return groupedRooms.map {
        (groupKey: $0.key, rooms: $0.rooms)
    }
}

 


 

5. 결론

한동안 별 문제 없을 거라 생각했던 정렬 로직이, 새해가 되면서 갑자기 꼬이는 일이 났습니다.(ㅋㅋ ㅠㅠ) 원인은 Dictionary의 키 정렬 방식이 제가 기대한 "날짜 우선순위"와 맞지 않았기 떄문이었죠...

이를 해결하기 위해 우선순위(sortWeight)를 도입해 정렬 기준을 명확히 했을 뿐만 아니라, 순서를 보장하지 않는 Dictionary 대신, 이미 정렬된 배열을 반환하는 방식으로 변경했습니다.

  • Dictionary는 키 정렬이 필요하고, 사전식 정렬이 원하는 순서와 다를 수 있다는 한계를 직접 경험했고,
  • 배열로 반환하면 View에서 별도의 정렬 작업 없이 원하는 순서를 그대로 사용할 수 있다는 점을 깨달았습니다.

비슷한 정렬 이슈가 발생한다면, 우선순위를 정의하는 것뿐만 아니라, 최종적으로 정렬된 배열을 반환하는 구조인지도 함께 고민해봐야 한다는 교훈을 얻었습니다. 

(혹시 아직도 Dictionary로 정렬 후 .keys.sorted(by: >) 같은 코드 쓰고 계신다면… 한 번쯤 다시 생각해보시길!🔥)

 

후련하게 해결한 사진까지 투척...