본문 바로가기
UIKIT

카테고리별 검색 결과를 5개 보여주는 방법

by 밤새는 탐험가89 2024. 10. 29.
728x90
SMALL

구현 목적 

  • 서치바에 검색어를 입력합니다. 
  • 검색어를 기반으로 외부 API에 데이터를 요청합니다. 
  • 요청해서 받아온 데이터를 contentTypeId 별로 구분해서 최소 5개 씩 테이블 형식으로 보여줍니다. 
  • 이때, 외부에서 받아온 데이터를 1페이지 당 10개의 데이터를 가져옵니다.
  • contentpyeId 별로 보여주는 최소 데이터 개수 5개를 채우지 못하면, 그 다음 페이지로 넘어갑니다. 
  • 이를 반복해서 최소 데이터 개수를 맞춥니다. 

 

 

구현 코드 

1. searchForKeyword(with:page:completion:)

이 함수는 네트워크 요청을 보내어 특정 페이지의 검색 결과를 가져오고, 데이터를 contentTypeId별로 그룹화한 뒤 completion 클로저를 호출하여 **총 결과 수(totalCount)**를 전달합니다.

  • 네트워크 요청: NetworkManager.shared.searchKeywordList를 사용해 주어진 page와 keyword로 데이터를 요청합니다.
  • 결과 처리:
    • categorizeByContentTypeId(searchList)를 호출하여 searchList의 데이터를 contentTypeId별로 분류하여 누적합니다.
    • completion(totalCount)을 호출하여 총 결과 수(totalCount)를 상위 호출자에게 전달합니다.
  • UI 업데이트: DispatchQueue.main.async를 통해 UI를 갱신하고, 결과가 없으면 테이블 뷰를 숨깁니다.
func searchForKeyword(with keyword: String, page: Int, completion: @escaping (Int) -> Void) {
        NetworkManager.shared.searchKeywordList(pageNo: String(page) ,keyword: keyword) { [weak self] results in
            switch results {
            case .success(let items):
                let searchList = items.response.body.items.item
                let totalCount = items.response.body.totalCount
                self?.categorizeByContentTypeId(searchList)
                completion(totalCount)
                
                DispatchQueue.main.async {
                    // 검색 결과가 있으면 테이블 뷰를 표시하고 없으면 숨깁니다.
                    self?.spotResultsTableView.isHidden = searchList.isEmpty
                    self?.spotResultsTableView.reloadData()
                    
                }
                
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }

 

 

2. fetchForKeyword(with:)

이 함수는 초기 keyword 검색을 실행하며, 각 contentTypeId별로 최소 5개 이상의 데이터를 확보할 때까지 데이터를 요청합니다.

  • 전체 페이지 계산:
    • totalCount와 itemsPerPage를 사용하여 전체 페이지 수(totalPages)를 계산합니다.
  • 각 contentTypeId별 최소 5개 확보:
    • ensureMinimumItems 함수를 호출하여, 각 contentTypeId가 최소 5개 이상의 데이터를 확보하도록 추가 페이지를 요청합니다.
  • UI 업데이트: 데이터가 비어 있으면 테이블 뷰를 숨기고, 아니라면 reloadData()로 데이터를 다시 로드합니다.
func fetchForKeyword(with keyword: String) {
        searchForKeyword(with: keyword, page: 1) { [weak self] totalCount in
            guard let self = self else { return }
            
            // 전체 페이지 수 계산
            let itemsPersPage = 10
            let totalPages = (totalCount / itemsPersPage) + (totalCount % itemsPersPage > 0 ? 1 : 0)
            
            // 각 contentTypId별 최소 5개 확보
            for contentTypeId in self.spotResultsByContentTypeId.keys {
                self.ensureMinimumItems(for: contentTypeId, keyword: keyword, currentPage: 1, totalPages: totalPages)
            }
            
            DispatchQueue.main.async {
                self.spotResultsTableView.isHidden = self.spotResultsByContentTypeId.isEmpty
                self.spotResultsTableView.reloadData()
            }
        }
    }

 

 

3. categorizeByContentTypeId(_:)

이 함수는 가져온 데이터(items)를 contentTypeId별로 그룹화하여 spotResultsByContentTypeId 딕셔너리에 누적하는 역할을 합니다.

  • 데이터 누적:
    • spotResultsByContentTypeId[contentTypeId, default: []].append(item)을 통해, 각 contentTypeId에 해당하는 배열이 없다면 빈 배열을 초기화한 후 item을 추가합니다.
    • 이 방법으로 removeAll() 없이 데이터를 누적하여, 이후 요청한 페이지의 데이터도 spotResultsByContentTypeId에 추가됩니다.
    func categorizeByContentTypeId(_ items: [AttractionItem]) {
        //spotResultsByContentTypeId.removeAll()
        for item in items {
            let contenttypeid = item.contenttypeid
            // default: [] 구문을 사용하면, contentTypeId 키가 spotResults 딕셔너리에 없을 경우 빈 배열 []이 자동으로 초기화
            //
            spotResultsByContentTypeId[contenttypeid, default: []].append(item)
        }
    }

 

 

4. ensureMinimumItems(for:keyword:currentPage:totalPages:)

이 함수는 각 contentTypeId가 최소 5개 이상의 데이터를 확보할 수 있도록 반복적으로 추가 페이지를 요청합니다.

  • 데이터 수 확인:
    • spotResultsByContentTypeId[contentTypeId]?.count를 통해 현재 확보된 데이터 수(itemCount)를 확인합니다.
  • 추가 페이지 요청 조건:
    • itemCount가 5개 미만이고 currentPage가 totalPages보다 작을 때만 추가 페이지를 요청합니다.
    • searchForKeyword를 호출하여 nextPage의 데이터를 가져오고, 이를 categorizeByContentTypeId로 추가한 뒤 ensureMinimumItems를 재귀 호출하여 조건을 충족할 때까지 반복합니다.
    func ensureMinimumItems(for contentTypeId: String, keyword: String, currentPage: Int, totalPages: Int) {
        
        // 현재 확보된 데이터 수 확인
        let itemCount = spotResultsByContentTypeId[contentTypeId]?.count ?? 0
        
        
        // 최소 5개 확보되지 않았고, 다음 페이지가 없는 경우 추가 설정
        if itemCount < 5, currentPage < totalPages {
            let nextPage = currentPage + 1
            searchForKeyword(with: keyword, page: nextPage) { [weak self] _ in
                self?.ensureMinimumItems(for: contentTypeId, keyword: keyword, currentPage: nextPage, totalPages: totalPages)
            }
        }
    }

 

 

전체 동작 흐름 요약

  1. fetchForKeyword 함수가 searchForKeyword로 첫 번째 페이지의 데이터를 요청합니다.
  2. searchForKeyword가 데이터를 가져와 categorizeByContentTypeId로 누적하고, completion을 통해 총 결과 수를 fetchForKeyword로 전달합니다.
  3. fetchForKeyword는 totalCount를 기반으로 전체 페이지 수를 계산한 후, ensureMinimumItems를 통해 contentTypeId별로 최소 5개 이상의 데이터가 확보될 때까지 추가 요청을 보냅니다.
  4. 모든 요청이 완료되면 spotResultsTableView.reloadData()로 데이터를 갱신합니다.

728x90
LIST