본문 바로가기

Project/MovieClip

🔨 검색결과 내의 영화, 티비의 장르 가져오는 부분 개선

 

📍 현재 장르 정보가 늦게 나옴... (좌측)  / 이제는 잘 나옴 (우측)

✅ 검색 결과에서 장르 정보를 가져오는 코드 검토 & 개선점

class SearchViewModel: ObservableObject {
    
    ...
    @Published var fetchedGenres: [Int: [String]] = [:]    // 장르 저장 변수
    
    
    
    // MARK: - 검색 실행
    func search(query: String) {
        currentQuery = query
        resetState()
        
        Task {
            do {
                let results = try await searchService.searchAll(with: query, page: 1)
                DispatchQueue.main.async {
                    self.movies = results.movies.results
                    self.tvShows = results.tvShows.results
                    self.people = results.people.results
                    
                    self.totalMoviesCount = results.movies.totalResults
                    self.totalTVShowsCount = results.tvShows.totalResults
                    self.totalPeopleCount = results.people.totalResults
                    
                    self.translateOverviews(for: .movie(self.movies))
                    self.translateOverviews(for: .tv(self.tvShows))
                    
                    // ✅ 장르 정보 변환
                    self.fetchGenre(for: .movie(self.movies))
                    self.fetchGenre(for: .tv(self.tvShows))
                    
                    self.updateLoadMoreStatus()
                }
            } catch {
                print("❌ 검색 요청 실패: \(error.localizedDescription)")
            }
        }
    }
    
    // ✅ 각 섹션별로 장르명을 받아서 변수에 저장 
    private func fetchGenre(for media: SearchTranslatedItem) {
        switch media {
        case .movie(let movies):
            for movie in movies {
                Task {
                    let genreNames = getGenresFromHomeSection(for: movie.genreIDS, contentType: .movie)
                    DispatchQueue.main.async {
                        self.fetchedGenres[movie.id] = genreNames
                    }
                }
            }
        case.tv(let tvs):
            for tv in tvs {
                Task {
                    let genreName = getGenresFromHomeSection(for: tv.genreIDS, contentType: .tv)
                    DispatchQueue.main.async {
                        self.fetchedGenres[tv.id] = genreName
                    }
                }
            }
        }
    }
    
    // ✅ 장르id를 통해 장르 name을 반환하는 메서드
    private func getGenresFromHomeSection(for genreIDs: [Int], contentType: ContentType) -> [String] {
        switch contentType {
        case .movie:
            return genreIDs.compactMap { genreID in
                HomeViewController.movieGenres.first { $0.id == genreID }?.name
            }
        case .tv:
            return genreIDs.compactMap { genreID in
                HomeViewController.tvGenres.first {  $0.id == genreID }?.name
            }
        case .people:
            return []
        }
    }

 

class SearchResultCell: UICollectionViewCell, SelfConfiguringSearchCell {
    
    
    // MARK: - Variable
    static var reuseIdentifier: String = "SearchResultCell"
    private var viewModel: SearchViewModel?
    private var cancellable = Set<AnyCancellable>()
    
   ...
    
    func configure(with data: SearchItem) {
        switch data {
        case .movie(let movie):
           
            // ✅ 장르 변환
            let genreNames = viewModel?.fetchedGenres[movie.id]
            genreLabel.text = genreNames?.joined(separator: " / ")
            
            
        case .tv(let tv):
            
            // ✅ 장르 변환
            let genreNames = viewModel?.fetchedGenres[tv.id]
            genreLabel.text = genreNames?.joined(separator: " / ")

...

 

🔹 1. 현재 코드에서의 문제점

장르를 가져오는 fetchGenre() 로직이 Task로 비동기 처리되어 있음

  • 개별 Task가 여러 개 실행되면, 장르 정보가 비동기적으로 업데이트
  • 이전에 설정된 데이터가 바뀌는 순간 UI가 여러 번 갱신될 수 있음

장르 데이터를 configure()에서 매번 조회

  • configure() 메서드가 호출될 때마다 viewModel?.fetchedGenres[movie.id]를 조회
  • 이미 viewModel에서 fetchGenre()를 통해 변환했으므로 configure()에서 추가적인 처리 없이 바로 사용 가능

장르 정보를 movie.genreNames에서 가져옴 (이 값이 이미 nil일 가능성이 높음)

  • movie.genreNames는 API에서 직접 내려오는 값이 아니라서 항상 올바른 값을 가지지 않을 수 있음
  • 우리가 fetchGenre()에서 fetchedGenres에 저장한 값을 사용해야 함

 

🔹 2. 개선된 코드 (성능 최적화 & 데이터 일관성 유지)

🔹 ✅ fetchGenre() 메서드 개선 (한 번만 실행되도록 최적화)

  • Task 내에서 반복문을 돌릴 필요 없이 forEach로 일괄 처리
  • DispatchQueue.main.async 내에서 한 번에 fetchedGenres를 업데이트
  • UI가 여러 번 갱신되는 문제 해결!
private func fetchGenre(for media: SearchTranslatedItem) {
    var newGenres: [Int: [String]] = [:] // 🔹 한 번에 저장할 임시 딕셔너리

    switch media {
    case .movie(let movies):
        movies.forEach { movie in
            let genreNames = getGenresFromHomeSection(for: movie.genreIDS, contentType: .movie)
            newGenres[movie.id] = genreNames
        }
    case .tv(let tvs):
        tvs.forEach { tv in
            let genreNames = getGenresFromHomeSection(for: tv.genreIDS, contentType: .tv)
            newGenres[tv.id] = genreNames
        }
    }

    DispatchQueue.main.async {
        self.fetchedGenres.merge(newGenres) { _, new in new }
    }
}

 

UI가 여러 번 갱신되지 않고, 한 번에 모든 장르 데이터가 fetchedGenres에 저장됨!
이제 configure()에서 viewModel.fetchedGenres[movie.id]를 바로 사용 가능!

 

 

🔹 ✅ SearchResultCell에서 configure() 개선 (불필요한 데이터 조회 최소화)

func configure(with data: SearchItem) {
    switch data {
    case .movie(let movie):
        ...
        // ✅ 장르 정보를 `viewModel?.fetchedGenres`에서 가져옴
        if let genreNames = viewModel?.fetchedGenres[movie.id] {
            genreLabel.text = genreNames.joined(separator: " / ")
        } else {
            genreLabel.text = "장르 없음😅"
        }


    case .tv(let tv):
        ...

        // ✅ 장르 정보를 `viewModel?.fetchedGenres`에서 가져옴
        if let genreNames = viewModel?.fetchedGenres[tv.id] {
            genreLabel.text = genreNames.joined(separator: " / ")
        } else {
            genreLabel.text = "장르 없음😅"
        }

}

 

🔹 최종 개선된 흐름

1️⃣ search(query:) 실행 시
→ fetchGenre()를 호출하여 모든 장르 정보를 fetchedGenres에 저장

2️⃣ reloadData()에서 createDataSource()를 실행하여 UI 업데이트

3️⃣ configure(with:)가 실행될 때
viewModel.fetchedGenres[movie.id]를 바로 사용하여 장르 표시

4️⃣ 검색 결과 UI에 정확한 장르 정보가 표시됨! 🚀