김경록의 앱 개발 여정

[Swift UIKit] 비회원은 못보는 TableView Section, 어떻게 할까? 본문

Trouble Shooting

[Swift UIKit] 비회원은 못보는 TableView Section, 어떻게 할까?

Kim Roks 2025. 1. 24. 22:20

 

 

개요

 

운동 캘린더 앱 BurnFit

 

한창 열심히 운동할 때 애용했던 앱이 있습니다.

BurnFit이라는 앱인데요, 해당 앱은 위 이미지처럼 유료 요금제를 사용하는 유저에게만 제공하는 화면이 있었습니다.

이 화면은 하나의 UICollectionView로 구성되어 있을 것으로 예상되는데, 유저의 상태에 따라 다른 콘텐츠를 보여주는 구조입니다.

 

저도 최근 팀 프로젝트를 진행하며 비슷한 기능을 구현해야 했습니다.

디자인 시안 中 , 카테고리 선택 여부에 따라 보여지는 화면이 다르다.

디자인 시안에 따르면, 카테고리 선택 여부에 따라 보이는 화면이 달라져야 했습니다.

저희 앱은 기본적으로 회원가입 없이 사용할 수 있지만, 글쓰기와 같이 특정 화면 및 기능은 회원만 접근 가능하도록 제한하고 있었습니다.

 

이번 글에서는 해당 기능을 구현하며 겪었던 고민과 해결 과정을 공유해보려 합니다.

참고: 아래 예제 코드에서는 가독성을 위해 일부 네이밍을 변경하거나 코드 일부를 생략했음을 양해 부탁드립니다.

Section 나누기

위 이미지처럼 화면은 두 가지 섹션으로 구성되어 있었습니다.

  • 인기 팁 모아보기
  • 관심 카테고리

RxSwiftReactorKit 조합을 사용 중이었기 때문에, 섹션 나누기에는 RxDataSource를 활용했습니다.

Section ModleType

// SectionModel
struct HomeTableViewSection {
    var sectionType: SectionType
    var items: [Item]

    enum SectionType {
		....
    }
}

extension HomeTableViewSection: SectionModelType {
    typealias Item = Tip
    
    init(original: HomeTableViewSection, items: [Item]) {
        self = original
        self.items = items
    }
}

 

RxTableViewSectionedReloadDataSource와 데이터 바인딩

// ViewController

private let dataSource = RxTableViewSectionedReloadDataSource<HomeTableViewSection>(
        configureCell: { dataSource, tableView, indexPath, item in
            guard let cell = tableView.dequeueReusableCell(
                withIdentifier: PostListCell.reuseIdentifier,
                for: indexPath
            ) as? PostListCell else { return UITableViewCell() }
            
            cell.configureTitleLabelText(item.title)
            
            }
)
func bind(reactor: MyReactor) {
     reactor.state
        .map { $0.sections }
        .bind(to: myTableView.rx.items(dataSource: dataSource))
        .disposed(by: disposeBag)
}

기존 DiffableDataSource와 굉장히 유사했기 때문에 데이터 바인딩 방식만 조금 신경 써주면 됐습니다.

 

UITableViewHeaderFooterView

Second Footer라고 부르겠습니다.

문제:

헤더는 유지해야 하며, 로그인 상태가 아니거나, 관심사를 설정하지 않은 경우에는 해당 섹션에서 별도의 안내 뷰를 표시해야 했습니다.

해결:

UITableViewHeaderFooterView를 활용하여 각 섹션의 Footer로 표시했습니다.

  • Header는 유지하면서 Footer를 추가하면 레이아웃의 복잡성을 줄이고, 각 뷰 간 연관성을 유지할 수 있었습니다.

푸터 뷰 추가하기

 

func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
    switch dataSource.sectionModels[section].sectionType {
    case .first:
        return FirstFooter()
    case .second:
        let secondFooter = SecondFooter()
        secondFooter.delegate = self
        return secondFooter
    }
}

 

TableView Delegate

특정 조건에 따라 나오는 푸터이기 때문에, 많은 설정을 거쳤습니다.

// 사실 UIView 형태를 요구하기때문에 일반적인 뷰로 작성해도 괜찮다. 포인트는 '푸터 위치'를 사용하기 위함임
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        switch dataSource.sectionModels[section].sectionType {
        case .first:
            return FirstFooter()
        
        // 이게 관심 카테고리 선택 푸터!
        case .second:
            let secondFooter = SecondFooter()
            secondFooter.delegate = self
            
            return secondFooter
        }
    }

 

 

섹션별로 각각의 푸터를 제공해 줬습니다.

모든 유저가 해당 푸터를 보게 되는 건 원치 않기 때문에

 

  func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        switch dataSource.sectionModels[section].sectionType {
        case .first:
            return 50
        
        case .second:
            if coordinator?.checkIsLoggedin() == false || 선택된 카테고리가 없다면() {
                return 250
            } else {
                return 0
            }
        }
    }

로그인 상태와 관심사 설정 여부를 판단하고, 이를 기반으로 Footer의 높이를 변경하여

원하는 UI를 구성할 수 있었습니다.

또한,  Second Footer는 델리게이트를 위임하고 있는 모습을 볼 수 있는데

 

바로 위 코드에서 보시다시피 로그인 상태 확인의 책임을 코디네이터가 짊어지고 있기 때문입니다.

ViewController에서 관리되는 코디네이터를 사용하여 로그인 여부를 확인하고, 해당 푸터의 버튼을 눌렀을 때 로그인을 유도할 것인지, 관심사를 선택하러 이동시켜 줄지를 선택할 수 있겠죠

 

마치며..

완성

 

RxDataSource 활용을 통한 섹션 분리

RxDataSource는 기존 DiffableDataSource와 유사한 점이 많아 학습 부담이 적었습니다. 다만 의도치 않게도 Header에 separator가 생기는 문제가 있었는데, Separotor Style을 변경하고, 커스텀 셀에서 밑줄을 추가하는 방식으로 해결했습니다.

커스텀 Header/Footer의 활용

처음으로 섹션별로 다른 Header와 Footer를 적용해 보며 UI의 유연성을 높이는 경험을 했습니다. 특히 Footer를 활용해 특정 조건에서만 표시되는 뷰를 간단히 추가할 수 있었습니다.