Skip to content

일일 영화 순위, 주간/ 주말 영화 순위를 조회할 수 있는 BoxOffice 프로젝트 입니다.

Notifications You must be signed in to change notification settings

wongbingg/BoxOffice2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tags: README

BoxOffice2

프로젝트 소개

박스오피스를 일별, 주중/주말 순위 로 구분해서 볼 수 있는 앱 입니다.

📑 목차

🔑 핵심기술

  • MVVM + C
    • MVVM 패턴, Coordinator
  • UI 구현
    • 코드 베이스 UI
    • 오토레이아웃
    • CollectionView Compositional Layout
    • Custom Modal
  • 비동기처리
    • RxSwift
  • 이미지 캐싱
    • URLCache
    • Dictionary Cache

📱 실행화면

홈화면

일별 박스오피스 화면 주간/주말 박스오피스 화면

🔭 프로젝트 구조

[Presentation Layer]

- HomeScene

  • FlowCoordinator
    • HomeFlowCoordinator
  • HomeView
    • ViewModel
    • View
  • MovieDetailView
    • ViewModel
    • View

[Domain Layer]

- Entity

  • MovieCellData
  • MovieDetailData

- UseCase

  • SearchDailyBoxOfficeUseCase
  • SearchWeekDaysBoxOfficeUseCase
  • SearchWeekEndBoxOfficeUseCase
  • SearchMovieDetailBoxOfficeUseCase

[Data Layer]

- Network

  • DailyBoxOfficeListResponseDTO
  • WeeklyBoxOfficeListResponseDTO
  • MovieDetailInfoResponseDTO
  • MoviePosterResponseDTO

- Repository

  • MovieRepository
  • PosterImageRepository

⚙️ 적용한 기술

✅ CollectionView Compositional Layout

보기모드에 따라 Layout을 다르게 적용 해주었습니다.

일별 박스오피스 레이아웃 생성 메서드
func createDailyLayout() -> UICollectionViewLayout {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(1.0)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    item.contentInsets = NSDirectionalEdgeInsets(
        top: 0,
        leading: 10,
        bottom: 0,
        trailing: 10
    )

    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(0.25)
    )
    let group = NSCollectionLayoutGroup.vertical(
        layoutSize: groupSize,
        subitems: [item]
    )

    let section = NSCollectionLayoutSection(group: group)

    let headerFooterSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(60)
    )
    let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
        layoutSize: headerFooterSize,
        elementKind: "headerView",
        alignment: .top
    )
    section.boundarySupplementaryItems = [sectionHeader]

    let layout = UICollectionViewCompositionalLayout(section: section)
    return layout
}
주중/ 주말 박스오피스 레이아웃 생성 메서드
func createWeeklyLayout() -> UICollectionViewLayout {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(1.0)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    item.contentInsets = NSDirectionalEdgeInsets(
        top: 0,
        leading: 10,
        bottom: 0,
        trailing: 10
    )

    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(0.45),
        heightDimension: .fractionalHeight(0.45)
    )
    let group = NSCollectionLayoutGroup.horizontal(
        layoutSize: groupSize,
        subitems: [item]
    )

    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
    let headerFooterSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(50)
    )
    let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
        layoutSize: headerFooterSize,
        elementKind: "headerView",
        alignment: .top
    )
    section.boundarySupplementaryItems = [sectionHeader]

    let layout = UICollectionViewCompositionalLayout(section: section)
    return layout
}

✅ Custom Modal View

  • 보기모드 변경 버튼(상단 버튼), 캘린더 버튼, 출연진 더보기 버튼 을 Custom Modal 형식으로 구현 했습니다.
  • Presentation Controller 에서 화면 전환 시 dimmingView 처리와 UIGesture를 통해 화면 dismiss를 구현 했습니다.
보기모드 변경 날짜 선택 출연진 보기

✅ 지역화

  • 일본, 미국 에서 사용할 수 있도록 Localization을 적용 했습니다.
일본 지역화 미국 지역화

✅ 다크모드 대응

  • 라이트 모드와 다크 모드에서 색감 차이가 없도록 Semantic Color를 사용했습니다.
일별 박스오피스 화면 보기모드 변경 모달뷰 주간/주말 박스오피스 화면 캘린더뷰

✅ 가로모드, 세로모드 대응

  • 가로모드와 세로모드에 대응할 수 있도록 Layout을 설정 해주었습니다.
  • 기기 회전 이벤트에 Notification을 적용하여 layout을 변경해주었습니다.
일별 박스오피스 화면 주간/주말 박스오피스 화면

✅ 접근성 향상

  • 시력이 좋지 않은 사용자를 위해 Dynamic Type을 적용 했습니다.
  • 시각 장애인 사용자를 위해 Voice Over를 적용 했습니다.
일별 박스오피스 화면 주간/주말 박스오피스 화면

✅ Unit Test

  • 네트워킹에 대한 Unit Test
  • UseCase에 대한 Unit Test
  • ViewModel에 대한 Unit Test

⚠️ 트러블 슈팅

🛠 Struct타입 모델을 Diffable DataSource에 사용하기

DiffableDataSource 에서 기존에는 item identifier로 모델타입을 넣어 주었습니다. 
하지만 참고해본 `개발자 포럼`에서 값타입의 모델을 사용 할 경우, uuid값을 item identifier
로 넣어주는 것이 데이터 관리에 용이하고, 중복으로 인한 크래쉬를 방지하기 쉽다고 하여 적용 해보았습니다.
func createDailyCellRegistration() -> UICollectionView.CellRegistration<ListCell, String> {
        
    let cellRegistration = UICollectionView.CellRegistration<ListCell, String> { (cell, _, id) in

        let item = self.viewModel.dailyBoxOffices.value.filter { $0.uuid == id }
        self.setupCell(with: item[0], at: cell, id: id)
    }
    return cellRegistration
}
HomeCollectionView의 Cell Registration 부분에서 id 값에 해당하는 모델을 찾아
setupCell 메서드에 적용 해주었습니다. 

🛠 셀 업로드 속도 향상

  • 이미지 요청의 반환속도 때문에 전체 Cell 업데이트 속도의 지연이 있었습니다.
  • 이를 해결하기 위해 이미지는 PosterImageRepository에서 따로 받아오도록 작업하고, 나머지 셀 정보들은 바로 업데이트가 되도록 수정 했습니다.
  • 셀의 Text 정보들이 미리 업로드 된 후, 이미지를 받아왔을 때 또 한번 업데이트를 해주어야 하는 것에서 문제가 생겼습니다.
  • 셀을 업데이트 해주는 방법에는 snapshot의 reloadItem() 과 reconfigureItems() 메서드가 있습니다.

🛠 reloadItem과 reconfigureItems의 차이점

NSDiffableDataSourceSnapshot 에 존재하는 reloadItem() 메서드와 reconfigureItems() 메서드의 차이점에 대해 알아봤습니다.

  • reloadItem() 메서드

    • 다른 셀 타입으로 업데이트 해야할 때 사용합니다.
  • reconfigureItems() 메서드

    • 같은 셀 타입에, 정보만 업데이트 할 때 사용합니다.

Make blazing fast lists and collection views WWDC를 참고해본 결과, reconfigureItem() 메서드의 효율성이 더 좋다고 판단하여 이를 적용 해주었습니다.

로딩 속도 개선 전 로딩 속도 개선 후
로딩속도 약 3초 로딩 속도 1초 이내

🛠 화면전환시 메모리 allocation 증가 현상

선택영역의 카테고리들이 MovieDetailViewController로 들어갔다가 나올 때마다 증가하는 문제가 발생했습니다.

원인 분석 결과, RxSwift의 사용에서 문제가 있었습니다.

private func bindData() {
        viewModel.movieDetailData
            .observe(on: MainScheduler.instance)
            .subscribe { [weak self] movieDetailData in
                self?.movieMainInfoView.configure(
                    with: movieDetailData,
                    rating: "",
                    repository: (self?.posterImageRepository)!
                )
                self?.movieSubInfoView.configure(with: movieDetailData)
                self?.activityIndicator.stopAnimating()
            } onError: { [weak self] error in
                guard let self = self else { return }
                DefaultAlertBuilder(message: error.localizedDescription)
                    .setButton()
                    .showAlert(on: self)
            }
            .disposed(by: disposeBag)
    }

위 코드로 변경하기 전에 [weak self] 처리를 해주지 않았었는데, 이 때문에 참조가 남아있어 allocation이 계속 증가 한 것으로 판단되었습니다. 위 코드처럼 모두 [weak self] 로 처리해준 결과, allocation이 쌓이는 현상이 없어졌습니다.

🔗 References

About

일일 영화 순위, 주간/ 주말 영화 순위를 조회할 수 있는 BoxOffice 프로젝트 입니다.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages