[내일배움캠프 9주차 (03/03)]

yeseul jang·2026년 3월 3일

내일배움캠프

목록 보기
15/32

📌 MVVM 한줄 정리

  • View (UIView / Cell): “그리기만” 하는 역할. 데이터 저장/가공/네트워크 안 함. (ex. MovieCollectionView, PosterCell, MovieDetailView)
  • ViewController: 화면의 중간 관리자, 중개자. View를 붙이고, ViewModel를 바인딩하고, 유저 이벤트를 받아 다음 화면으로 넘김. (ex. MovieCollectionViewController, MovieDetailViewController)
  • ViewModel: 화면에 필요한 데이터/표현 로직 담당. 네트워크 요청, 모델 가공, 에러 메시지 만들기, 섹션별 아이템 계산 등. (ex. MovieCollectionViewModel, MovieDetailViewModel)

1️⃣ 영화 목록 화면(MovieCollectionVC) 데이터 흐름

🔎 앱 시작 ~ 데이터 흐름

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        
        configure()
        bindViewModel()
        
        Task { await viewModel.fetchMovieData() }
    }
  1. VC가 로드됨
  • MovieCollectionViewController.viewDidLoad() 에서
    • configure()View 붙이고 collectionViewdatasource/delegate 연결
    • bindViewModel()로 VM의 콜백을 VC가 받도록 연결
    • Task { await viewModel.fetchMovieData() } 로 UI 구성에 필요한 데이터 요청 시작
  1. VM이 네트워크 요청
  • 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)
        }
    }
  1. VM이 상태 업데이트 + 신호 보냄
  • nowPlaying/upcoming/popular 배열(영화 정보)에 저장하고 화면 업데이트를 위해onUpdate?() 호출
  • 실패하면 onErrorMessage?(...) 호출
  1. VC가 신호를 받아 View 갱신
  • VC는 onUpdate에서 collectionView.reloadData() 호출
  • 에러면 alert 표시

VC는 데이터를 직접 들고 있지 않고 VM한테 물어보고, VM이 업데이트 됐다는 신호를 주면 그때 View를 갱신

2️⃣ 상세 화면(MovieDetailVC) 데이터 전달 흐름

상세 화면은 네트워크가 아니고 목록에서 선택한 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)
    }
  1. 사용자가 포스터 셀을 탭함
  2. VC가 indexPath를 받음 (collectionView(_:didSelectItemAt:) 이용)
  3. VC → VM: getMovieInfo(at:)로 선택한 Movie(영화정보)를 얻음
  4. VC가 MovieDetailViewModel(movie:)해당 영화정보를 가진 뷰모델 생성
  5. VC가 MovieDetailViewController(viewModel:) 생성해서 해당 VC를 push
  • 데이터 전달은 VC가 ‘모델(Movie)’을 VM으로 감싸서 다음 VC로 넘기는 방식
  • DetailVCMovie를 직접 만지지 않고, Detail VM이 만들어준 ‘표시용 값’만 사용하도록 의도함.

🔎 상세 화면에서 영화정보 데이터 흐름

  1. 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
    }
  1. configureDetailViewLabels()에서

    • 이미지 URL: viewModel.imageURL
    • 텍스트들: title/genre/overview/stars/releaseDate 모두 VM에서 연산프로퍼티로 가공한 뒤 View(Label)에 세팅함.

Model(Movie) → ViewModel(Movie를 바탕으로 가공된 영화정보) → View(UILabel/UIImageView)

3️⃣ 마지막 정리

  • CollectionVC: VM이 데이터를 가져오고(onUpdate), VC가 그 신호를 받아 Viewreload, 셀 구성은 VCVM에 질문해서 만듦
  • DetailVC: VCMovieVM으로 감싸서 넘기고, DetailVCVM이 만든 표시용 값으로 View를 채움
profile
iOS 개발

0개의 댓글