본문 바로가기

Project/MovieClip

테이블 섹션 별 데이터 모델 사용

📌 테이블 뷰 안에 컬렉션 뷰를 넣고, 섹션별로 다른 데이터 모델을 사용하는 경우

테이블 뷰의 각 섹션에서 서로 다른 API 데이터를 받아와 컬렉션 뷰 셀에 전달해야 한다면, 각 섹션에 맞는 데이터 모델을 관리할 방법이 필요

 

 

🚀 해결 방법: Enum + Associated Value 사용

🔹 각 섹션에서 다른 데이터 모델을 사용할 때, Enum을 활용하면 쉽게 구분 가능
🔹 각 섹션별 데이터를 Enum의 case에 저장하여 switch 문으로 분기 처리

// 1. 각 섹션별 데이터 모델 정의
struct TrendingMovie {
    let title: String
    let posterURL: String
}

struct PopularTVShow {
    let name: String
    let thumbnailURL: String
}

struct UpcomingMovie {
    let title: String
    let releaseDate: String
}

// 2. 섹션 타입을 Enum으로 정의
enum HomeSection {
    case trendingMovies([TrendingMovie])
    case popularTVShows([PopularTVShow])
    case upcomingMovies([UpcomingMovie])
}

 

 

📌 테이블 뷰에서 Enum을 사용하여 데이터 관리

class HomeViewController: UIViewController {
    
    private let homeFeedTableView: UITableView = {
        let tableView = UITableView(frame: .zero, style: .grouped)
        tableView.separatorStyle = .none
        return tableView
    }()

    // 3. 테이블의 섹션별 데이터 저장
    private var sections: [HomeSection] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(homeFeedTableView)
        setupTableView()
        fetchData() // ✅ API 데이터 가져오기
    }

    private func setupTableView() {
        homeFeedTableView.delegate = self
        homeFeedTableView.dataSource = self
        homeFeedTableView.register(HomeFeedTableViewCell.self, forCellReuseIdentifier: "HomeFeedTableViewCell")
    }
    
    // 4. API에서 데이터를 받아와 sections에 저장
    private func fetchData() {
        Task {
            do {
                let trendingMovies = try await NetworkManager.shared.getTrendingMovies()
                let popularTVShows = try await NetworkManager.shared.getPopularTVShows()
                let upcomingMovies = try await NetworkManager.shared.getUpcomingMovies()

                self.sections = [
                    .trendingMovies(trendingMovies),
                    .popularTVShows(popularTVShows),
                    .upcomingMovies(upcomingMovies)
                ]

                DispatchQueue.main.async {
                    self.homeFeedTableView.reloadData()
                }
            } catch {
                print("Failed to fetch data: \(error)")
            }
        }
    }
}

 

📌 테이블 뷰에서 Enum을 사용하여 섹션별 데이터 제공

extension HomeViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1 // ✅ 각 섹션에 컬렉션뷰 1개만 존재
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "HomeFeedTableViewCell", for: indexPath) as? HomeFeedTableViewCell else {
            return UITableViewCell()
        }
        
        let sectionData = sections[indexPath.section]
        cell.configure(with: sectionData) // ✅ 섹션별 데이터를 전달
        return cell
    }
}

 

📌 컬렉션 뷰에서 Enum을 사용하여 데이터 바인딩 

class HomeFeedTableViewCell: UITableViewCell {
    
    private let homeCollectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 10
        layout.itemSize = CGSize(width: 180, height: 250)
        
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.showsHorizontalScrollIndicator = false
        return collectionView
    }()
    
    private var items: [Any] = [] // ✅ 섹션별로 다른 데이터 저장
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        contentView.addSubview(homeCollectionView)
        homeCollectionView.delegate = self
        homeCollectionView.dataSource = self
        homeCollectionView.register(MovieCollectionViewCell.self, forCellWithReuseIdentifier: "MovieCollectionViewCell")
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // ✅ 데이터를 설정하는 함수
    func configure(with section: HomeSection) {
        switch section {
        case .trendingMovies(let movies):
            self.items = movies
        case .popularTVShows(let shows):
            self.items = shows
        case .upcomingMovies(let movies):
            self.items = movies
        }
        homeCollectionView.reloadData()
    }
}

 

📌 컬렉션 뷰 데이터 제공

extension HomeFeedTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MovieCollectionViewCell", for: indexPath) as? MovieCollectionViewCell else {
            return UICollectionViewCell()
        }
        
        if let movie = items[indexPath.item] as? TrendingMovie {
            cell.configure(with: movie.title, imageURL: movie.posterURL)
        } else if let tvShow = items[indexPath.item] as? PopularTVShow {
            cell.configure(with: tvShow.name, imageURL: tvShow.thumbnailURL)
        } else if let upcoming = items[indexPath.item] as? UpcomingMovie {
            cell.configure(with: upcoming.title, imageURL: "placeholder_url")
        }
        
        return cell
    }
}