본문 바로가기

Project/MovieClip

상세페이지로 넘어가기 (문제 해결, 데이터 전달 방법 비교)

 

📌 영화 및 TV 상세 정보 페이지를 구현하는 최적의 데이터 전달 방법

컬렉션뷰의 영화 셀을 눌렀을 때, 해당 id를 전달하여 상세 정보를 불러올 예정
영화(Movie)뿐만 아니라 TV(TVShow) 상세 페이지도 동일한 DetailViewController에서 사용해야 함
델리게이트 패턴, configure(), init()을 통한 데이터 전달 방식 중 어떤 것이 가장 적절한지 고민 중

 

 

🚀 해결 방법

🔹 최적의 선택: init(movieID:) 또는 init(contentID:contentType:) 사용

1️⃣ DetailViewController에서 movieID 또는 tvID를 받아야 하므로 초기화(init)에서 직접 전달
2️⃣ viewDidLoad()에서 API 요청을 실행하여 데이터를 로드하면 깔끔한 구조 유지 가능
3️⃣ configure() 메서드를 추가하면 데이터 요청 후 UI 업데이트 가능

 

 

🔹 Step 1: HomeFeedTableViewCell에서 UICollectionViewDelegate 구현

먼저, HomeFeedTableViewCell 내부에서 셀 선택 이벤트를 HomeViewController로 전달하는 델리게이트 패턴을 사용

// MARK: - Protocol
protocol HomeFeedTableViewCellDelegate: AnyObject {
    func homeFeedTableViewCellDidSelectItem(_ cell: HomeFeedTableViewCell, section: Int, index: Int)
}

class HomeFeedTableViewCell: UITableViewCell {
    
    // MARK: - Variable
    static let reuseIdentifier: String = "HomeFeedTableViewCell"
    weak var delegate: HomeFeedTableViewCellDelegate? // ✅ 델리게이트 선언

    /// 테이블뷰의 섹션 인덱스 저장
    var sectionIndex: Int = 0
    ...
}


// Extension
extension HomeFeedTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
    
    ...
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // ✅ 셀이 눌리는 확인하기 위한 print() 문
        print("✅ [컬렉션뷰] 선택된 셀 - section: \(sectionIndex), item: \(indexPath.item)")
        delegate?.homeFeedTableViewCellDidSelectItem(self, section: sectionIndex, index: indexPath.item)
    }
}

 

🔹 Step 2: HomeViewController에서 델리게이트 구현 및 cellForRowAt에서 델리게이트 설정 

이제 HomeViewController에서 HomeFeedTableViewCellDelegate를 채택하여 셀 선택 이벤트를 받아서 DetailViewController로 이동하도록 구현.

// MARK: - Extension: TableView Delegate
extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
    ...
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: HomeFeedTableViewCell.reuseIdentifier, for: indexPath) as? HomeFeedTableViewCell else { return UITableViewCell() }
        
        // 섹션 인덱스 전달
        cell.sectionIndex = indexPath.section
        cell.delegate = self // ✅ 델리게이트 설정
        
        return cell
    }
    ...
}

// MARK: - Extension: HomeFeedTableViewCellDelegate
extension HomeViewController: HomeFeedTableViewCellDelegate {
    
    func homeFeedTableViewCellDidSelectItem(_ cell: HomeFeedTableViewCell, section: Int, index: Int) {
        
        // ✅ 확인하기 위함
        print("✅ [홈 뷰 컨트롤러] 선택된 섹션: \(section), 아이템: \(index)")
        
        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)
            
        }
    }
}

 

🔹 Step 3: DetailViewController 에서 데이터 받을 준비 (생성자에서 ID와 콘텐츠 타입 전달)

전달받은 contentID와 contentType을 바탕으로 해당 콘텐츠의 상세 정보를 가져옴.

class DetailViewController: UIViewController {
    
    private let contentID: Int
    private let contentType: ContentType
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        fetchContentDetail()  // ✅ 상세 정보 불러오기
    }
    
    init(contentID: Int, contentType: ContentType) {
        self.contentID = contentID
        self.contentType = contentType
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func fetchContentDetail() {
        Task {
            do {
                switch contentType {
                case .movie:
                    let movieDetail = try await NetworkManager.shared.getMovieDetailInfo(movieID: contentID)
                    DispatchQueue.main.async {
                        self.configure(with: movieDetail) // ✅ UI 업데이트
                    }
                case .tv:
                    let tvDetail = try await NetworkManager.shared.getTVDetailInfo(tvID: contentID)
                    DispatchQueue.main.async {
                        self.configure(with: tvDetail)
                    }
                case .people:
                    let peopleDetail = try await NetworkManager.shared.getPeopleDetailInfo(peopleID: contentID)
                    DispatchQueue.main.async {
                        self.configure(with: peopleDetail)
                    }
                }
            } catch {
                print("❌ 데이터 로드 실패: \(error)")
            }
        }
    }
}

📌 과정

  1. HomeViewController에서 전달받은 contentID와 contentType을 저장.
  2. fetchContentDetail()을 호출하여 해당 콘텐츠의 상세 정보를 비동기적으로 요청.
  3. contentType에 따라 영화, TV, 사람의 정보를 구분하여 가져옴.
  4. 가져온 데이터를 기반으로 configure(with:)을 호출하여 UI 업데이트.

 

🔹 Step 4: DetailView 에서 UI 구성

DetailViewController에서 가져온 데이터를 DetailView에 전달하여 UI를 업데이트.

    ...
    // MARK: - Function
    // 🚗 movie 데이터를 받아 UI 전달 메서드
    func configure(_ movie: MovieDetailInfoWelcome) {
        if let backdropPath = movie.backdropPath {
            guard let url = URL(string: "https://image.tmdb.org/t/p/w500\(backdropPath)") else { return }
            backdropImage.sd_setImage(with: url, completed: nil)
        }
        
        if let posterPath = movie.posterPath {
            guard let url = URL(string: "https://image.tmdb.org/t/p/w500/\(posterPath)") else { return }
            posterImage.sd_setImage(with: url, completed: nil)
        }
    }
    ...

 

 

🔥  전체 흐름 정리

1️⃣ HomeViewController에서 셀을 클릭하면 homeFeedTableViewCellDidSelectItem()이 호출됨.

2️⃣ DetailViewController를 생성하고 contentID와 contentType을 전달하여 pushViewController로 화면 이동.

3️⃣ DetailViewController에서 fetchContentDetail()을 호출하여 해당 콘텐츠의 상세 정보 요청.

4️⃣ contentType에 따라 영화, TV, 사람 데이터를 가져오고 UI 업데이트.

5️⃣ 가져온 데이터를 DetailView에 전달하여 화면에 표시.

 

 

🚀 데이터 전달 방법 비교

결론: init(contentID:contentType:)을 사용하여 DetailViewController에서 직접 API 요청하는 것이 가장 깔끔한 해결 방법!

데이터 전달 방법 장점 단점
✅ init(contentID:contentType:) 사용
(추천)
- ID 전달이 직관적이며, viewDidLoad()에서 API 호출 가능
- Movie와 TV를 한 컨트롤러에서 처리 가능
- init()을 통해 매번 DetailViewController를 생성해야 함
❌ configure() 메서드를 사용하여 나중에 데이터 전달 - DetailViewController 생성 후 나중에 데이터 설정 가능 - 네트워크 요청을 HomeViewController에서 실행해야 하며, 구조가 복잡해질 수 있음
❌ 델리게이트 패턴 사용 - 데이터 전달이 유연함 - 단순 데이터 전달에는 불필요한 복잡성 증가