본문 바로가기

Project/MovieClip

헷갈리기 쉬운 주요 출연진 정보를 받아오는 로직

 

📌 주요 출연진 정보를 얻어오는 과정 정리

 

1️⃣ 개요

영화, TV, 배우에 대한 상세페이지에서 출연진 정보를 받아와 컬렉션뷰에서 보여주는 과정을 정리

 

목표:

  • movie, tv, people의 타입을 enum(ContentType)을 활용해 일관되게 전달.
  • DetailViewController → TopBilledCastTableViewCell →  CastCollectionViewCell 로 전달되는 흐름 정리.

흐름 개요:

  1. 사용자가 HomeViewController에서 특정 영화/TV/배우 선택 → DetailViewController로 이동.
  2. DetailViewController에서 fetchContentDetail()을 통해 해당 콘텐츠 상세 정보 요청.
  3. fetchContentDetail() 내 switch contentType을 사용해 출연진 정보 요청 후 저장
  4. tableView(cellForRowAt:)에서 TopBilledCastTableViewCell에 출연진 데이터 + 콘텐츠 타입 전달.
  5. TopBilledCastTableViewCell에서 CastingList enum을 통해 데이터를 컬렉션뷰에 넘김.
  6. collectionView(cellForItemAt:)에서 출연진 정보를 컬렉션뷰에 표시.

 

2️⃣ ContentType(enum) 사용해 타입 구분

이 enum을 사용해서 현재 선택된 콘텐츠의 타입을 구분한다.

enum ContentType {
    case movie
    case tv
    case people
}

 

3️⃣ DetailViewController에서 선택된 콘텐츠 정보를 전달하는 과정

✔ (1) HomeViewController → DetailViewController로 데이터 전달

설명: contentType: ContentType을 생성자에 전달하여 해당 콘텐츠가 영화인지, TV인지, 배우인지 구분.

// 사용자가 특정 영화를 선택했을 때 상세 페이지로 이동
func homeFeedTableViewCellDidSelectItem(_ cell: HomeFeedTableViewCell, section: Int, index: Int) {
    
    let sectionData = HomeViewController.homeSections[section]
    
    switch sectionData {
    case .trendingMovies(let movies):
        let selectedMovie = movies[index]
        let detailVC = DetailViewController(contentID: selectedMovie.id, contentType: .movie)
        navigationController?.pushViewController(detailVC, animated: true)
        
    case .trendingTVs(let tvShows):
        let selectedTV = tvShows[index]
        let detailVC = DetailViewController(contentID: selectedTV.id, contentType: .tv)
        navigationController?.pushViewController(detailVC, animated: true)
        
    case .trendingPeoples(let people):
        let selectedPeople = people[index]
        let detailVC = DetailViewController(contentID: selectedPeople.id, contentType: .people)
        navigationController?.pushViewController(detailVC, animated: true)
    }
}

 

✔ (2) DetailViewController에서 콘텐츠 정보 요청 및 저장

✅ 설명

  • fetchContentDetail() 실행 후 콘텐츠 타입(movie, tv, people)에 따라 적절한 데이터 요청 및 저장.
private func fetchContentDetail() {
    Task {
        do {
            var fetchedDetail: ContentDetail?
            var fetchedGenres: [String] = []
            var fetchedCastingList: TopBilledCastInfoWelcome?  

            switch contentType {
            case .movie:
                let movieDetail = try await NetworkManager.shared.getMovieDetailInfo(movieID: contentID)
                fetchedDetail = .movie(movieDetail)
                fetchedGenres = getGenresFromHomeSection(for: contentID)
                // ✅ casting 정보 받아오기
                fetchedCastingList = try await NetworkManager.shared.getMovieCastInfo(contentID: contentID)

            case .tv:
                let tvDetail = try await NetworkManager.shared.getTVDetailInfo(tvID: contentID)
                fetchedDetail = .tv(tvDetail)
                fetchedGenres = getGenresFromHomeSection(for: contentID)
                // ✅ casting 정보 받아오기
                fetchedCastingList = try await NetworkManager.shared.getTVCastInfo(contentID: contentID)

            case .people:
                let peopleDetail = try await NetworkManager.shared.getPeopleDetailInfo(peopleID: contentID)
                fetchedDetail = .people(peopleDetail)
                fetchedGenres = getGenresFromHomeSection(for: contentID)
            }

            DispatchQueue.main.async {
                self.detailTableHeaderView()

                guard let contentDetail = fetchedDetail else { return }
                self.contentDetail = contentDetail

                // ✅ 출연진 정보 저장
                switch contentDetail {
                case .movie:
                    if let castingList = fetchedCastingList {
                        self.movieTopBilledCastInfo = .movie(castingList)
                    }
                case .tv:
                    if let castingList = fetchedCastingList {
                        self.movieTopBilledCastInfo = .tv(castingList)
                    }
                case .people:
                    break
                }

                self.detailTableView.reloadData()
            }
        } catch {
            print("❌ 데이터 로드 실패: \(error)")
        }
    }
}

 

✔ (3) tableView(cellForRowAt:)에서 출연진 정보를 TopBilledCastTableViewCell로 전달 

설명

  • TopBilledCastTableViewCell에 출연진 정보(CastingList)를 configure()를 통해 전달.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let sectionType = DetailSection.allCases[indexPath.section]

    switch sectionType {
    case .overview:
        guard let cell = tableView.dequeueReusableCell(withIdentifier: OverviewTableViewCell.reuseIdentifier, for: indexPath) as? OverviewTableViewCell else { return UITableViewCell() }
        if let contentDetail = contentDetail {
            cell.configure(with: contentDetail)
        }
        return cell

    case .actor:
        guard let cell = tableView.dequeueReusableCell(withIdentifier: TopBilledCastTableViewCell.reuseIdentifier, for: indexPath) as? TopBilledCastTableViewCell else { return UITableViewCell() }

        // ✅ 출연진 데이터 전달
        switch contentType {
        case .movie:
            if let castingInfo = movieTopBilledCastInfo {
                cell.configure(with: castingInfo)
            }
        case .tv:
            if let castingInfo = movieTopBilledCastInfo {
                cell.configure(with: castingInfo)
            }
        case .people:
            break
        }
        
        return cell

    default:
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "Test"
        cell.backgroundColor = .white
        return cell
    }
}

 

 

4️⃣ TopBilledCastTableViewCell에서 컬렉션뷰에 콘텐츠 타입 전달

✔ (1) CastingList enum 정의

enum CastingList {
    case movie(TopBilledCastInfoWelcome)
    case tv(TopBilledCastInfoWelcome)
    case people
}

 

✔ (2) TopBilledCastTableViewCell에서 configure() 메서드 구현

✅ configure(with casting: CastingList)를 통해 출연진 정보를 저장하고 컬렉션뷰 갱신.

class TopBilledCastTableViewCell: UITableViewCell {
    
    static let reuseIdentifier: String = "TopBilledCastTableViewCell"
    private var castingList: CastingList?

    private let topBilledCastCollectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: 150, height: 200)
        layout.minimumLineSpacing = 10
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.showsHorizontalScrollIndicator = true
        collectionView.backgroundColor = .clear
        return collectionView
    }()
    
    func configure(with casting: CastingList) {
        self.castingList = casting
        topBilledCastCollectionView.reloadData()
    }
}

 

✔ (3) UICollectionView에서 출연진 정보를 사용

✅ castingList를 통해 현재 콘텐츠가 영화인지, TV인지 판별하여 컬렉션뷰 데이터 설정.

extension TopBilledCastTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        switch castingList {
        case .movie(let castInfo):
            return castInfo.cast.count
        case .tv(let castInfo):
            return castInfo.cast.count
        default:
            return 0
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CastCollectionViewCell.reuseIdentifier, for: indexPath) as? CastCollectionViewCell else { return UICollectionViewCell() }
        
        switch castingList {
        case .movie(let castInfo):
            let castMember = castInfo.cast[indexPath.item]
            cell.configure(with: castMember)
        case .tv(let castInfo):
            let castMember = castInfo.cast[indexPath.item]
            cell.configure(with: castMember)
        default:
            break
        }
        return cell 
    }
}

 

📌 정리

  1. HomeViewController → DetailViewController 이동 시 ContentType을 활용해 타입 전달
  2. fetchContentDetail()에서 CastingList 데이터를 요청 후 저장 ✅
  3. tableView(cellForRowAt:)에서 TopBilledCastTableViewCell에 적절한 데이터 전달 ✅
  4. TopBilledCastTableViewCell에서 CastingList를 configure()를 통해 컬렉션뷰에 반영 ✅

 

📌 switch contentDetail을 사용하는 코드 분석

출연진 정보를 저장할 때 switch contentDetail을 사용해야 하는 이유

  • 출연진 정보는 movie와 tv에만 존재하기 때문에, people일 경우를 필터링해야 함.
  • movie, tv 출연진 정보를 CastingList enum을 활용해서 적절히 저장.
guard let contentDetail = fetchedDetail else { return }
self.contentDetail = contentDetail

// ✅ 출연진 정보 저장
switch contentDetail {
case .movie:
    if let castingList = fetchedCastingList {
        self.movieTopBilledCastInfo = .movie(castingList) // 🎥 영화 출연진 정보 저장
    }
case .tv:
    if let castingList = fetchedCastingList {
        self.movieTopBilledCastInfo = .tv(castingList) // 📺 TV 출연진 정보 저장
    }
case .people:
    break // 🧍 배우는 출연진 정보가 필요 없음
}

 

📌 switch contentType을 사용하지 않은 이유

switch contentType {
case .movie:
    self.movieTopBilledCastInfo = .movie(castingList)
case .tv:
    self.movieTopBilledCastInfo = .tv(castingList)
case .people:
    break
}

 

✅ 이렇게 해도 동작은 하지만, contentType을 사용하면 출연진 데이터를 요청하기 전에 콘텐츠의 타입을 확인하는 방식.
✅ switch contentDetail을 사용하면 실제 데이터를 받아온 후(fetchedDetail) 타입을 확인해서 더 안전한 방식

 

📌 결론

  • switch contentDetail을 사용하면 실제 API 응답에서 콘텐츠 데이터를 확인한 후, 타입별로 출연진 정보를 저장할 수 있어서 안전
  • contentType을 사용하는 방식보다 더 데이터 중심적인 로직이 됨.
  • people은 출연진이 필요 없으므로 배제하는 작업이 필요하기 때문에 switch contentDetail이 적절