Skip to content

3주차 과제#4

Open
limtjdghks wants to merge 13 commits into
mainfrom
week03
Open

3주차 과제#4
limtjdghks wants to merge 13 commits into
mainfrom
week03

Conversation

@limtjdghks
Copy link
Copy Markdown
Collaborator

@limtjdghks limtjdghks commented May 1, 2026

🔗 3주차 과제

📄 작업 내용

기본과제

  • 왓챠 UI, 화면설계서를 보고 다음과 같은 기능/view를 구현해주세요.
    • 탭바를 구현해주세요. ‘구독’ 탭이 아닌 다른 탭들은 내용이 비어있어도 좋습니다. 탭바 간 이동은 가능해야 합니다.
    • 피그마에 있는 가로 스크롤 View들을 모두 구현해주세요.
    • ‘메인으로’ 버튼을 이번 과제의 메인 뷰로 연결해주세요.
  • Snapkit, Then 라이브러리를 사용하여 UI를 구현해주세요.

심화과제

  • 가장 상단에 있는 ‘구독’ 이 스크롤 시 헤더에 고정되게 해주세요. (힌트: Sticky Header)
  • 신작 업데이트 플로팅 뷰를 구현해주세요.
구현 내용 IPhone 17 pro IPhone 13 mini
GIF

💻 주요 코드 설명 및 Trouble Shooting 🚀

메인 뷰로 이동하기

WelcomeViewController.swift

  • toMainButtonMainTabBarController로 이동하는 액션을 추가하였습니다
  • navigation으로 할...까하다가 그건 아닌거같아서 찾아보니 주로 root뷰컨을 변경하는 방법을 사용한다고합니다
@objc private func toMainButtonDidTap() {
        guard let window = view.window else { return }
        window.rootViewController = MainTabBarController()
        
        UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: nil, completion: nil)
    }

탭바 구현하기

MainTabBarController.swift

  • UITabBarController를 이용해서 탭바를 구현하였습니다
  • 각 뷰에 타이틀과 아이콘을 설정하여 탭바 아이콘을 만들었습니다
private func setViewControllers() {
        let subscribeVC = SubscribeViewController()
        let purchseVC = PurchaseViewController()
        let webtoonVC = WebtoonViewController()
        let searchVC = SearchViewController()
        let storageVC = StorageViewController()
        
        subscribeVC.tabBarItem = UITabBarItem(title: "구독", image: .subscribeIcon, selectedImage: .subscribeIcon)
        purchseVC.tabBarItem = UITabBarItem(title: "개별 구매", image: .purchaseIcon, selectedImage: .purchaseIconSelect)
        webtoonVC.tabBarItem = UITabBarItem(title: "웹툰", image: .webtoonIcon, selectedImage: .webtoonIconSelect)
        searchVC.tabBarItem = UITabBarItem(title: "찾기", image: .searchIcon, selectedImage: .searchIconSelect)
        storageVC.tabBarItem = UITabBarItem(title: "보관함", image: .storageIcon, selectedImage: .storageIconSelect)
        
        viewControllers = [subscribeVC, purchseVC, webtoonVC, searchVC, storageVC]
    }

가로 스크롤 뷰 구현

WatchaPartyCollectionViewCell.swift

  • 컬렉션뷰에서 사용할 cell을 구현하였습니다
  • self.addSubview를 이용해서 레이아웃을 추가하면 버튼이 가려져서 보이지 않는 문제가 있었습니다 해당 문제를 해결하기 위해서 contentViewsubView를 추가하도록 하였습니다
private func setUp() {
        contentView.backgroundColor = .gray600
        contentView.addSubviews(watchaPartyImg, alarmButton, timeInfoLabel, titleInfoLabel)
    }
  • Model에 저장되어 있는 데이터를 사용하기 위해서 dataBind 메서드를 생성하였습니다
extension WatchaPartyCollectionViewCell {
    func dataBind(_ itemData: WatchaPartyItemModel) {
        watchaPartyImg.image = itemData.itemImg
        timeInfoLabel.text = "오늘 \(itemData.startTime)에 시작"
        titleInfoLabel.text = "# \(itemData.movieTitle)"
    }
}

WatchaPartyViewController.swift

  • register메서드를 사용해서 CollectionView에 사용 할 셀을 지정하였습니다
override func register() {
        collectionView.register(WatchaPartyCollectionViewCell.self, forCellWithReuseIdentifier: WatchaPartyCollectionViewCell.identifier)
    }
  • UICollectionViewDataSource를 상속해서 데이터를 셀에 주입하였습니다
  • 이번 과제에 액션은 따로 없어서 UICollectionViewDelegate는 비어있습니다
extension WatchaPartyViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        itemList.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: WatchaPartyCollectionViewCell.identifier, for: indexPath) as? WatchaPartyCollectionViewCell else {
            return UICollectionViewCell()
        }
        cell.dataBind(itemList[indexPath.row])
        return cell
    }
}

extension WatchaPartyViewController: UICollectionViewDelegate {
    
}

구독 뷰 구현

SubscribeViewController.swift

  • 컬렉션뷰를 SubscribeViewController에 인스턴스를 생성해서 contentView에 추가했더니 뷰에 나타나지 않는 문제가 발생했습니다
  • UIViewController를 추가하려면 addChild를 써야한다는걸.. 알았습니다..
  • addSubview로 연결하면 생명주기가 상위 뷰컨과 연결되지 않기때문이라 합니다
    override func setUp() {
        view.addSubviews(headerView, scrollView)
        scrollView.addSubview(contentView)
        addChilds(bannerView, newContentCollectionView, watgorijeumCollectionView, commingSoonCollectionView, watchaPartyCollectionView)
        contentView.addSubviews(bannerView.view, newContentLabel, newContentSubLabel, newContentCollectionView.view, watgorijeumImg, watgorijeumSubLabel, watgorijeumMoreButton, watgorijeumCollectionView.view, commingSoonLabel, commingSoonMoreButton, commingSoonCollectionView.view, watchaPartyLabel, watchaPartyMoreButton, watchaPartyCollectionView.view)
    }

UIViewController+.swift

  • addchild.didMove()를 반복하는 문제를 해결하기 위해서 익스텐션을 추가하였습니다
extension UIViewController {
    func addChilds(_ viewControllers: UIViewController...) {
        viewControllers.forEach {
            addChild($0)
            $0.didMove(toParent: self)
        }
    }
}

📚 참고자료

👀 To Reviewers

심화과제는 세미나가 끝나고.. 시간이 남는다면 진행해보도록 하겠습니다
헤더는 스티키 헤더를 구현하기전에 임의로 구현한거라 밤티임니다...

  • 구독뷰를 구현하면서 중간부터 이렇게 구현하는게 아닌거같음을 느꼈는데 돌이킬 자신이 없어서 일단 그대로 쭉 구현했습니다
    • 더 좋은 방법이 있을 것 같은데 어떻게 했어야 가독성을 늘리고 유지보수를 고려할 수 있었을까요..
  • 베이스뷰컨을 만들어서 컬렉션뷰컨에 채택해서 사용하고있었는데, 컬렉션뷰 같은 경우에는 베이스뷰컨을 사용하기보다는 따로 컬렉션뷰에서 사용 할 클래스를 새로 만드는게 좋을까요?
class NewContentViewController: BaseUIViewController {
    // MARK: - 프로퍼티
    
    private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
    private var itemList = NewContentItemModel.dummy()
...
  • 컬렉션뷰나 셀 같은 클래스는 폴더구조를 어떻게 정리하면 좋을까요..!
  • 현재 UIViewControlller익스텐션으로 addchilddidMove()addChilds 메서드를 사용하면 같이 설정되도록 구현해놓았는데, 역할 분리 및 코드 가독성을 위해서 따로 분리하는게 좋을까요?
extension UIViewController {
    func addChilds(_ viewControllers: UIViewController...) {
        viewControllers.forEach {
            addChild($0)
            $0.didMove(toParent: self)
        }
    }
}

@limtjdghks limtjdghks self-assigned this May 1, 2026
}

private func setUI() {
let appearance = UITabBarAppearance()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UITabBarAppearance 라는게 있군요!!ㅠㅠ 전 기본 리퀴드 스타일 제거를 못해서 커스텀 탭바를 만들었는데!
가려운곳을 긁어주셨습니다 .. 굿굿👍

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구독 탭 아이콘도 피그마 잘 찾으면 미선택시 회색 아이콘 있습니다..!
지금은 select 상태만 적용되어 다른 탭으로 이동시에도 구독탭이 선택된 것 같아 보이네용

Comment on lines +33 to +34
private let watgorijeumSubLabel = SectionSubLabel(title: "예능부터 드라마까지!")
private let watgorijeumMoreButton = MoreButton()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

섹션 헤더는 컴포넌트화 할 수 있을 것 같아용

Copy link
Copy Markdown

@hemssy hemssy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3차 세미나 과제 고생하셨습니다👏👏
공통 컴포넌트 분리도 잘하셨고 코드가 전체적으로 일관성있네용
합동세미나 파이팅하세요


private func setViewControllers() {
let subscribeVC = SubscribeViewController()
let purchseVC = PurchaseViewController()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 변수명 purchseVC -> purchaseVC로 오타수정해주세용

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

베이스뷰컨 활용하셨군요!! 👍👍
앞으로 추가 뷰 점점 많아질텐데 이렇게 잘 잡아두면 나중에 편할거에용

class CommingSoonViewController: BaseUIViewController {
// MARK: - 프로퍼티

private let collectionView = UICollectionView(frame: .zero,collectionViewLayout: UICollectionViewLayout())
Copy link
Copy Markdown

@hemssy hemssy May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 컬뷰를 기본 UICollectionViewLayout으로 잡고나서 setCollectionLayout에서 다시 교체하고 있는데,
처음 만들 때 UICollectionViewFlowLayout를 넣어주는 방법도 괜찮을것같아용

private let collectionView = UICollectionView(frame: .zero,collectionViewLayout: UICollectionViewLayout())
private var itemList = CommingSoonItemModel.dummy()

final let inset = UIEdgeInsets(top: 0, left: 14, bottom: 0, right: 0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 외부에서 접근안하는거는 private 붙여서 관리해주면 좋을것같아요!!

view.addSubviews(headerView, scrollView)
scrollView.addSubview(contentView)
addChilds(bannerView, newContentCollectionView, watgorijeumCollectionView, commingSoonCollectionView, watchaPartyCollectionView)
contentView.addSubviews(bannerView.view, newContentLabel, newContentSubLabel, newContentCollectionView.view, watgorijeumImg, watgorijeumSubLabel, watgorijeumMoreButton, watgorijeumCollectionView.view, commingSoonLabel, commingSoonMoreButton, commingSoonCollectionView.view, watchaPartyLabel, watchaPartyMoreButton, watchaPartyCollectionView.view)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런건 줄바꿈 해주시면 더 좋지 않을까요?

Copy link
Copy Markdown

@y-eonee y-eonee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보면 메인 뷰컨에서 여러 뷰컨트롤러를 데리고 와서 사용하는 것처럼 보여요
제 생각에는 뷰컨트롤러 여러개를 데리고 오는것보단, 안에서 여러 컬렉션뷰를 생성해서 분기처리해서 레이아웃을 잡아주는게 더 좋을 것 같습니다!
컴포넌트 분리의 목적이었다면, 뷰컨트롤러보단 **View로 컴포넌트를 분리하는게 좋습니다
고생하셨어요~~

Copy link
Copy Markdown

@kwonseokki kwonseokki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 성환님~ 리뷰가 조금 늦어졌는데
전체적으로 컴포넌트도 잘 나누었다고 생각이 들고 코드도 깔끔하게 작성해 주신 것 같아요
별다른 리뷰 사항은 눈에 보이지 않아서 남겨두신 질문에 대한 제 생각을 남겨봤습니다.

  • 베이스뷰컨을 만들어서 컬렉션뷰컨에 채택해서 사용하고있었는데, 컬렉션뷰 같은 경우에는 베이스뷰컨을 사용하기보다는 따로 컬렉션뷰에서 사용 할 클래스를 새로 만드는게 좋을까요?
    -> 컬렉션뷰에서 필요한 기능들이 공통적으로 베이스뷰컨에 있다면 그대로 사용해도 괜찮다고 생각하나 컬렉션뷰에 강하게 결합된 기능들이 많아진다면 그때가서 분리해도 괜찮다고 생각합니다.

  • 현재 UIViewControlller익스텐션으로 addchild와 didMove()를 addChilds 메서드를 사용하면 같이 설정되도록 구현해놓았는데, 역할 분리 및 코드 가독성을 위해서 따로 분리하는게 좋을까요?
    -> 이것도 같은 맥락으로 addchild, didMove 정도는 공통적으로 자주 사용되는 정도라면 괜찮다고 생각합니다 다만 특정화면 로직이나 기능에대한 결합도가 생길 우려가 있다면 그떄 가서 분리를 고려해도 괜찮을 것 같아요

  • 컬렉션뷰나 셀 같은 클래스는 폴더구조를 어떻게 정리하면 좋을까요..!
    -> 저는 보통 Feature를 기준으로 폴더 내부에 VC, Component 폴더를 만들어두고(ex. Feature/Component/SomeFeatureCell...) 그안에서 관리합니다 다만 이런 컨벤션적인 부분은 정답이 있다기보다 팀 컨벤션 영향이 큰 부분이라 팀 컨벤션을 따라가거나 다른 레포 구조를 참고해서 일관성있는 방향으로 설정하는게 중요한 것 같습니다.


// MARK: - 레이아웃

private func setUp() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별건 아니지만 setUI 라던지 어떤것을 setup 해주는지 네이밍 해줘도 괜찮을 것 같네요~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3주차 과제

6 participants