MovieCollectionView, PosterCell, MovieDetailView) MovieCollectionViewController, MovieDetailViewController) MovieCollectionViewModel, MovieDetailViewModel) override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
configure()
bindViewModel()
Task { await viewModel.fetchMovieData() }
}
MovieCollectionViewController.viewDidLoad() 에서configure()로 View 붙이고 collectionView의 datasource/delegate 연결 bindViewModel()로 VM의 콜백을 VC가 받도록 연결 Task { await viewModel.fetchMovieData() } 로 UI 구성에 필요한 데이터 요청 시작 MovieCollectionViewModel.fetchMovieData()에서 async let으로 3개 목록 동시 요청 NetworkManager.fetchMovies(type:)가 담당 private func bindViewModel() {
viewModel.onUpdate = { [weak self] in
self?.movieCollectionView.collectionView.reloadData()
}
viewModel.onErrorMessage = { [weak self] message in
guard let self else { return }
self.showAlert(with: message)
}
}
nowPlaying/upcoming/popular 배열(영화 정보)에 저장하고 화면 업데이트를 위해onUpdate?() 호출 onErrorMessage?(...) 호출 onUpdate에서 collectionView.reloadData() 호출 VC는 데이터를 직접 들고 있지 않고 VM한테 물어보고, VM이 업데이트 됐다는 신호를 주면 그때 View를 갱신
상세 화면은 네트워크가 아니고 목록에서 선택한 Movie(모델)를 넘겨서 ViewModel을 만들고, 그 ViewModel이 “표시용 데이터”를 제공하는 구조
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let movie = viewModel.getMovieInfo(at: indexPath) else { return }
let detailVM = MovieDetailViewModel(movie: movie)
let movieDetailVC = MovieDetailViewController(viewModel: detailVM)
navigationController?.pushViewController(movieDetailVC, animated: true)
}
indexPath를 받음 (collectionView(_:didSelectItemAt:) 이용)getMovieInfo(at:)로 선택한 Movie(영화정보)를 얻음 MovieDetailViewModel(movie:)해당 영화정보를 가진 뷰모델 생성 MovieDetailViewController(viewModel:) 생성해서 해당 VC를 push
- 데이터 전달은 VC가 ‘모델(Movie)’을 VM으로 감싸서 다음 VC로 넘기는 방식
DetailVC는Movie를 직접 만지지 않고, Detail VM이 만들어준 ‘표시용 값’만 사용하도록 의도함.
MovieDetailViewController.viewDidLoad()에서 네비게이션 타이틀에viewModel.title를 이용해서 영화제목을 넣어줌private func configureDetailViewLabels() {
let url = URL(string: viewModel.imageURL)
detailView.posterImageView.kf.setImage(with: url)
detailView.titleLabel.text = viewModel.title
detailView.genreLabel.text = viewModel.genre
detailView.overviewLabel.text = viewModel.overview
detailView.voteAverageLabel.text = viewModel.stars
detailView.releaseDateLabel.text = viewModel.releaseDate
}
configureDetailViewLabels()에서
viewModel.imageURL title/genre/overview/stars/releaseDate 모두 VM에서 연산프로퍼티로 가공한 뒤 View(Label)에 세팅함.Model(Movie) → ViewModel(Movie를 바탕으로 가공된 영화정보) → View(UILabel/UIImageView)
VM이 데이터를 가져오고(onUpdate), VC가 그 신호를 받아 View를 reload, 셀 구성은 VC가 VM에 질문해서 만듦VC는 Movie를 VM으로 감싸서 넘기고, DetailVC는 VM이 만든 표시용 값으로 View를 채움