Pagenation

주방·2023년 5월 1일
0

MovieRank

목록 보기
4/8

배경

  • MVVM 작업을 진행하며, GitHub을 활용함으로써 코드 관리의 완성성을 높이고자 하였음.
  • Issue 탭을 활용해 작업에 대한 주변 동료들의 피드백을 받았음.
  • 현재 보여주고 있는 작업의 경우 주간 영화 트렌드 순위인데, 더 많은 인기 순위정보를 궁금하다고 함.(궁금하다면 알려드리는 것이 인지상정)
  • MainView에서 보여줄 수 있는 페이지를 늘리기로 결정함.
  • Pagenation 기능을 구현 하게 되었음.(약 38,000개의 인기 영화 정보 리스트 맘껏 보여드리겠음. 원하는거 뭐든지 말만해 다해드리겠음.)

구현 GIF

이미지 설명

작업

  1. endPoint 변경

    • 기존: 주간 영화 트렌딩(apikey)
    • 변경: 인기 영화 순위(apikey + page)
  2. endPoint 변경을 통해 page값으로 int를 사용하고 있음. 이를 파라미터로 받아야 함.

  3. 기존의 코드 내 파라미터에서 page를 받도록 함.

  4. func fetchMovies(page: Int, completion: @escaping (Result<MovieResponse, Error>) -> Void){
            let requestURL = URL(string: "https://api.themoviedb.org/3/movie/popular?api_key=\(APIKey.apiKey)&page=\(page)")
     // 중략
    }
  5. api변경하였음. 그런데 스크롤을 내릴 때마다 page를 변경해줘야하는데 어떻게 변경해줄 수 있을가?

  6. scroll을 완전히 다 내렸을 때, page의 int 값을 변경해줌

  7. 어떻게 모든 scroll을 내렸음을 알 수 있을까?

  8. 처음 scrollViewDidScroll을 사용하여 구현하고자 하였다. 그러나 스크롤 하단을 데이터가 기준이 아니라, view.frame이 되니 기계마다 표시의 오류가 발생할 수 있음.

  9. 그래서 데이터를 기준으로 다음 페이지를 넘기는 방법을 선택함. collectionview의 indexpath.row의 값을 model.count와 비교하기로함.

  10. func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
            if indexPath.row == viewModel.movie.count - 1{
                loadData()
            }
      }
    
    func loadData(){
            currentPage += 1
            viewModel.fetchMovies(page: currentPage) {
                self.mainView.collectionView.reloadData()
            }
        }
  11. indexpath.row 하단에 내려갔을 경우, loadData() 를 통해 fetchMovie를 실행하면 정상적으로 다음 데이터가 표시될줄 알았음.

  12. 그러나 fetchMovie의 성공할 경우, 기존 model 데이터에 append 되는 것이 아니라, movie 자체의 데이터 덮어씀. 계속해서 스크롤을 내리니 해당 뷰의 이미지가 계속해서 내려가지 않고, 바뀌는 기현상을 경험했음.

  13. 결국 loadData()가 호출될 경우는 기존 movie model에 append 하는 것을 선택함.

  14. //viewModel.swift
    func appendMovies(page: Int, completion: @escaping () -> Void) {
          apiManager.fetchMovies(page: page) { [weak self] result in
              DispatchQueue.main.async {
                  switch result {
                  case .success(let response):
                      self?.movie.append(contentsOf: response.results)
                      completion()
                  case .failure(let error):
                      print("Error: \(error)")
                  }
              }
          }
      }
  15. 데이터가 성공적으로 덮어졌으나, page 3까지만 보여줌.

  16. Error: keyNotFound(CodingKeys(stringValue: "release_date", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "results", intValue: nil), _JSONKey(stringValue: "Index 7", intValue: 7)], debugDescription: "No value associated with key CodingKeys(stringValue: \"release_date\", intValue: nil) (\"release_date\").", underlyingError: nil))
  17. release_date 키 값을 찾지 못했음. 보유하고 있는 항목의 데이터가 없을 줄 생각도 못함.

  18. 옵셔널로 처리하니 문제 없이 표시됨.

  19. struct Movie: Codable {
        let title: String
        let posterPath: String?
        let releaseDate: String?
        let voteAverage: Double
        let overview: String
        
        enum CodingKeys: String, CodingKey{
            case title
            case posterPath = "poster_path"
            case releaseDate = "release_date"
            case voteAverage = "vote_average"
            case overview
        }
    }

정리

  1. 사람들은 더 많은 것을 보고 싶어함.
  2. 한번만 서버로부터 데이터를 가져오는 것이 아니라, 지속적인 통신이 요구됨.
  3. frame을 기준으로 데이터를 표시하는 방법보다, model을 기준으로 서버 통신 여부를 결정하는 것이 기기에 상관없이 적절하다고 판단할 수 있었음.
  4. 데이터를 가져왔을 때, 기존 model의 데이터와 어떻게 연결되는지를 확인해야함. 그렇지 않으면, 계속 초기화시켜 데이터 핸들링에 혼선을 줄 수 있음.
  5. 서버에서 모든 데이터를 가지고 있지 않을 수 있음.
  6. 다양한 대응을 해볼 수 있어 즐거웠음.

0개의 댓글