📍 현재 장르 정보가 늦게 나옴... (좌측) / 이제는 잘 나옴 (우측)
✅ 검색 결과에서 장르 정보를 가져오는 코드 검토 & 개선점
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에 정확한 장르 정보가 표시됨! 🚀
'Project > MovieClip' 카테고리의 다른 글
🌟 회원 가입 화면 뷰 관리? 순서?는 어떻게 해야하나? (0) | 2025.02.26 |
---|---|
❌ 문제 해결 - 검색 결과의 상세페이지 이동이 안됨.. (0) | 2025.02.24 |
🔥 MVVM + Combine을 통한 검색기능 구현 2편 구현 순서? (0) | 2025.02.24 |
🤔 createDataSource() 메서드에서 "검색 결과 전체보기" 버튼 동작 (0) | 2025.02.24 |
🤔 reloadData() 함수의 역할 및 동작 방식 (0) | 2025.02.24 |