구현 목적
- 서치바에 검색어를 입력합니다.
- 검색어를 기반으로 외부 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)
}
}
}
전체 동작 흐름 요약
- fetchForKeyword 함수가 searchForKeyword로 첫 번째 페이지의 데이터를 요청합니다.
- searchForKeyword가 데이터를 가져와 categorizeByContentTypeId로 누적하고, completion을 통해 총 결과 수를 fetchForKeyword로 전달합니다.
- fetchForKeyword는 totalCount를 기반으로 전체 페이지 수를 계산한 후, ensureMinimumItems를 통해 contentTypeId별로 최소 5개 이상의 데이터가 확보될 때까지 추가 요청을 보냅니다.
- 모든 요청이 완료되면 spotResultsTableView.reloadData()로 데이터를 갱신합니다.
'UIKIT' 카테고리의 다른 글
UILabel 에서 패딩 효과를 주고 싶다면? (0) | 2024.10.30 |
---|---|
검색한 결과에서 중복된 데이터 통합하기 (1) | 2024.10.30 |
검색결과를 카테고리별로 구분하기 (5) | 2024.10.28 |
서치바로 데이터 검색하기 (0) | 2024.10.28 |
UISearchBar 사용하기 (0) | 2024.10.26 |