[포켓몬 도감 앱 만들기] 리팩토링 3

황석범·2025년 1월 6일
0

내일배움캠프_iOS_5기

목록 보기
59/76

수정하고 싶은 부분

MainViewController가 DetailViewModel과 DetailViewController의 초기화를 담당하고 있는 방식이 마음에 들지 않는다..

   func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let selectedPokemon = pokemon[indexPath.row]
        let pokemonURL = selectedPokemon.url
        let detailViewModel = DetailViewModel(useCase: PokemonUseCase())
        detailViewModel.fetchPokemonDetail(for: pokemonURL)
        let detailVC = DetailViewController(viewModel: detailViewModel)
        navigationController?.pushViewController(detailVC, animated: true)
    }

왜 마음에 들지 않는가

  • MainViewController가 하위 계층의 세부 구현에 너무 의존적이게 만들어, 계층 간의 의존성이 강하게 결합되는 것 같다.

  • MVVM에서는 뷰 컨트롤러가 뷰 모델에 의존하지만, 뷰 모델이 뷰 컨트롤러나 다른 뷰 모델의 세부 사항을 알지 못하는 것이 이상적인 것 같다.


개선 방향

네비게이션과 화면 전환 책임을 담당하는 별도의 객체를 만들자

final class AppCoordinator {
    private let navigationController: UINavigationController
    private let disposeBag = DisposeBag()
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        let useCase = PokemonUseCase()
        let mainViewModel = MainViewModel(useCase: useCase)
        let mainVC = MainViewController(viewModel: mainViewModel, coordinator: self)
        
        mainViewModel.pokemonSelected
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { [weak self] selectedPokemon in
                self?.navigateToDetail(with: selectedPokemon)
            })
            .disposed(by: disposeBag)
        
        navigationController.setViewControllers([mainVC], animated: false)
    }
    
    func navigateToDetail(with pokemon: Pokemon) {
        let useCase = PokemonUseCase()
        let detailViewModel = DetailViewModel(useCase: useCase)
        detailViewModel.fetchPokemonDetail(for: pokemon.url) // 데이터 초기화
        
        let detailVC = DetailViewController(viewModel: detailViewModel)
        navigationController.pushViewController(detailVC, animated: true)
    }
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let selectedPokemon = pokemon[indexPath.row]
        viewModel.pokemonSelected.onNext(selectedPokemon)
    }

개선 효과

1. 뷰와 뷰 모델 간 결합도 감소

  • MainViewController는 이제 네비게이션 로직에 대해 알지 못하고, 그 책임은 AppCoordinator로 분리.

2. 코드 재사용성 향상

  • 만약 네비게이션 로직을 다른 곳에서 재사용해야 한다면, 코디네이터를 사용하면 쉽게 확장 가능.

3. 테스트 가능성 향상

  • 뷰 컨트롤러와 뷰 모델은 각각 독립적으로 테스트할 수 있습니다. 네비게이션 로직도 별도로 테스트 가능.
profile
iOS 공부중...

0개의 댓글

관련 채용 정보