AdvanceApp

hyun·2025년 8월 4일

iOS

목록 보기
35/54

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가 증가해 무한 스크롤이 구현됨

0개의 댓글