Kakao API에서 sale_price가 UI에 0이거나 -1원으로 뜨는 버그가 있었음..
Document.sale_price가 0 이하일 때도 그대로 반영돼서 포맷터가 0원이나 -1원으로 뜨고
BookCell.configure에서 priceText만 보고 바로 텍스트를 세팅해서 비정상 값도 필터링 없이 보여주는 거 같았음
BookItem.init(document:) 내부에서 sale_price > 0인 경우에만 사용하고 그렇지 않으면 원가(price)로 대체해서 모델 단계에서 정상화 시키려 했고
BookCell.configure에서도 item.priceText가 0원이나 -1원일 땐 item.price를 대신 표시 하는 걸로 해결할 수 있었음
// BookItem.swift
// 판매가 포맷팅: sale_price가 0 이하일 땐 원가 사용
let saleValue = document.sale_price > 0
? document.sale_price
: document.price
let formattedSale = BookItem.priceFormatter
.string(from: NSNumber(value: saleValue)) ?? "\(saleValue)"
self.priceText = "\(formattedSale)원"
모델 초기화 시점에 sale_price <= 0인 경우 원가를 사용하도록 예외 처리함
// BookCell.swift
func configure(with item: BookItem) {
let sale = item.priceText
// priceText가 0원 또는 -1원일 땐 원가(label: price)로 교체
if sale == "0원" || sale == "-1원" {
priceLabel.text = item.price
} else {
priceLabel.text = sale
}
}
모델에서 정상화해도 혹시 모르니까 데이터 오류 대비해서 뷰에서도 다시 필터링해 이중 방어를 적용함
이렇게 모델과 뷰 양쪽에서 각각 예외 처리를 걸어둬서 판매가가 없거나 잘못된 경우에 원가가 표시되도록 해서 제대로 된 값이 뜨게 함 ( 근데도 0원으로 뜨던 게 있긴 하던데 뭐지 진짜 ㄹㅇ 뭐지 )
tableView.rx.willDisplayCell을 이용해서 마지막 셀에 도달하면 loadNextPage가 발동하도록 했는데 스크롤이 끝까지 내려도 loadNextPage 스트림이 구독되지 않아서 다음 페이지 요청이 일어나지 않았음
사용자 입장에선 빈 화면만 유지되니까 내용이 없는 것 처럼 보였음
원인을
isEnd 플래그가 첫 페이지 요청 이후에만 false로 초기화되고 API 응답 직전까지 true 상태로 남아 있어서 스트림 진입 자체가 차단되는 거 같았음. -> filter { !isEnd } 위치 문제
currentPage += 1을 네트워크 호출 전에 실행하다 보니 호출이 아예 이루어지지 않는 경우 이후 로직이 올바르게 동작하지 않음 -> 동기화 시점
파악하고
lter { !isEnd }를 스트림 초기 단계로 이동,
Transform 함수 안에서 loadNextPage를 정의할 때 가장 먼저 마지막 페이지가 아니라면 체크하도록 배치,
currentPage 증가 시점 조정,
네트워크 응답이 성공했을 때만 currentPage를 증가시키도록 변경
해서 해결 할 수 있었음
let nextPage = input.loadNextPage
// 마지막 페이지가 아닐 때만 통과
.filter { [weak self] in !(self?.isEnd ?? true) }
// 빠른 스크롤 중복 방지
.debounce(.milliseconds(300), scheduler: MainScheduler.instance)
.flatMapLatest { [weak self] _ -> Observable<BookSearchResponse> in
guard let self = self else { return .empty() }
// 아직 페이지는 증가시키지 않음
return self.service.searchBooks(query: self.currentQuery, page: self.currentPage + 1)
}
nextPage
.observe(on: MainScheduler.instance)
.subscribe(onNext: { [weak self] response in
guard let self = self else { return }
let newItems = response.documents.map(BookItem.init)
// 기존 데이터에 합치기
self.itemsRelay.accept(self.itemsRelay.value + newItems)
// ④ 마지막 여부 업데이트
self.isEnd = response.meta.is_end
// ⑤ 응답 성공 시점에만 페이지 증가
if !self.isEnd {
self.currentPage += 1
}
})
.disposed(by: disposeBag)
filter를 스트림 맨 앞에 둬서 isEnd가 잘못 설정된 상태에서도 다음 페이지에 진입하고
페이지 증가는 응답 성공 후로 옮겨서 호출 실패를 방지함
스크롤이 끝까지 내려지면 -> filter를 통과해 -> API 호출이 이루어지고 -> 한 번씩만 currentPage가 증가해 무한 스크롤이 구현됨