[TIL] iOS 입문 주차 팀 프로젝트 : 키오스크 앱 만들기 day.01

Emily·2024년 11월 26일
2

KioskApp

목록 보기
1/4
post-thumbnail

팀 프로젝트가 시작되었다. 화면 이동 없이 하나의 view를 가진 키오스크 앱이 그 주제로, 화면 내 컴포넌트들을 팀원들과 나누어 구현하는 것이다. 내가 맡은 컴포넌트는 UICollectionView다. 리스트 형태의 테이블 뷰 밖에 만들어 본 적이 없어서 컬렉션 뷰를 경험해보고 싶은 마음에 자원해서 맡게 되었다. 프로젝트는 code base UIKit으로 만든다.

01) 프로젝트 기획 및 역할 분담

우선 우리 조는 키오스크로 주문할 메뉴를 치킨으로 정했다. Figma에 다같이 화면을 그려보며 구성을 정했다. 화면 구성은 타이틀 영역, 카테고리 탭 영역, 메뉴 선택 영역, 선택 내역 영역, 합계 영역, 주문/취소 버튼 영역으로 나뉜다. 사실 과제 요구사항에 화면과 기능이 다 정해져 있어서 메뉴를 치킨으로 정하는 것 외에 딱히 기획을 할 게 더 없었다. 각자 어떤 부분을 맡아서 구현할 지 나누는 게 관건이었던 것 같다. 나는 메뉴 선택 영역UICollectionView로 구현하기로 했다.

02) Git repository 생성 및 컨벤션 정하기

원격 저장소의 경우 조장님이 깃헙에서 레포지토리를 생성한 뒤 콜라보레이터로 조원들을 초대해주셨다. 코딩 컨벤션, 커밋 컨벤션, 브랜치 전략, 브랜치 네이밍 컨벤션 등 정해야 할 것들이 많은 걸 보고 협업이라는 게 실감났다. 나혼자 실습 할 때 단 한번도 스스로 규칙을 정하고 지켜보는 연습을 해본 적 없이 늘 되는대로 작업해왔어서 그런지 사실 모든 게 낯설었다. 그래도 팀원 분들이 각자 가진 배경지식을 모아주셔서 프로젝트 규모에 맞게 시도해볼 수 있는 규칙들을 정하게 되어 좋았다.

이 과정에서 새로운 컨벤션 용어를 알게 되었는데, 바로 케밥 케이스(kebab case)다. 띄어쓰기를 -로 표현하는 방식이다.

[Collection View]
- camel case : collectionView
- snake case : collection_view
- kebab case : collection-view

브랜치 네이밍 컨벤션을 케밥 케이스로 정했다. 이걸 정할 때 다들 케밥을 먹고 싶어해서 웃겼다.

브랜치 전략에 대해서도 처음 접했다. Git-flow라는 것을 적용하기로 했는데, 대략 내용은 이렇다.

  • main : 배포 중인 브랜치 (우리 프로젝트는 배포한 적이 없으므로 최종 merge 대상인 브랜치라고 보면 된다.)
  • develop : 실제로는 dev라고 명명했는데, 현재 개발중인 버전의 main이라고 보면 될 거 같다.
  • feature : 기능을 개발하는 브랜치. feature/각자맡은기능이름(케밥케이스)으로 각자 브랜치를 dev로부터 분리하여 개발한다.

03) 구현

코드베이스로 작업하기로 했기 때문에 조장님이 main에서 스토리보드 삭제 및 SnapKit 설치까지 진행한 뒤 dev 브랜치를 분리해주셨다. devclone하여 내가 작업하기 위한 feature/collection-view 브랜치를 분리했다.
나는 코딩을 할 때 내 뷰 영역의 대략적인 크기를 임시로 잡은 뒤 뷰 컨트롤러에 오토레이아웃을 넣고 작업을 시작하고 싶었다. cell 내부 컴포넌트들의 크기와 위치를 프리뷰로 계속 보면서 조정하고 싶었기 때문이다. 그런데 과제 요구사항에 따라 최소 지원 iOS가 16으로 되어있어 Preview 사용이 불가능했다. 첫번째 난관이었다. 결국 최신 버전의 프로젝트를 생성하여 프리뷰를 보며 구현한 뒤 프로젝트 파일에 옮기는 식으로 작업했다. iOS 개발자들이 프리뷰가 없을 때 UIKit으로 작업하기 참 힘들었을 것 같다는 생각이 들었다. 시뮬레이터가 빠르면 모르겠는데 이것저것 켜놓은 상태에서 작업하면 시뮬레이터가 화를 돋구는 속도로 돌아가기 때문이다.

UICollectionView

UICollectionView와 그 안에 들어갈 cell을 자체 정의하기 위해 각각 클래스를 생성하였다.

ChickenCell : 치킨 imageView, 메뉴 이름 label, 가격 label

class ChickenCell: UICollectionViewCell {
	static let identifier: String = "ChickenCell"
    
    private lazy var imageView: UIImageView = {}()	
    private lazy var nameLabel: UILabel = {}()
    private lazy var priceLabel: UILabel = {}()
    
    private func layout() {}
    
    // MockChicken은 mock data model이다
    func bind(_ chicken: MockChicken) {
    	imageView.image = chicken.image
        nameLabel.text = chicken.name
        priceLabel.text = chicken.priceText
    }
    
}

ChickenCollectionView : ChickenCell2×2개 가진 뷰. 메뉴가 5개 이상인 경우 가로 스크롤이 생긴다. 추후에 paging 기능을 넣고자 한다.

class ViewController: UIViewController {
    private lazy var collectionView: ChickenCollectionView = {
        let layout = UICollectionViewFlowLayout()
        // 가로 스크롤 적용
        layout.scrollDirection = .horizontal
        // 화면 넘어갈 때 spacing 0
        layout.minimumLineSpacing = 0
        
        let collectionView = ChickenCollectionView(frame: .zero, collectionViewLayout: layout)
        return collectionView
    }()
}
class ChickenCollectionView: UICollectionView {
    // init에 layout을 넣어야만 에러가 사라졌다. 이 부분은 공부가 필요하다고 느꼈다.
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: .zero, collectionViewLayout: layout)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setup() {
    	// data source와 delegate를 extension에서 채택하여 구현하기 때문에 self를 할당했다.
        delegate = self
        dataSource = self
        // 직접 열심히 정의한 치킨 셀을 등록해주었다.
        register(ChickenCell.self, forCellWithReuseIdentifier: ChickenCell.identifier)
    }
}

extension ChickenCollectionView: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    	// item 개수 = mock data로 정의한 배열의 count
        MockChicken.redSeries.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = dequeueReusableCell(withReuseIdentifier: ChickenCell.identifier, for: indexPath) as? ChickenCell else { return UICollectionViewCell() }
        // cell의 컴포넌트 내용과 mock data를 bind
        cell.bind(MockChicken.redSeries[indexPath.item])
        return cell
    }
}

extension ChickenCollectionView: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // cell tap 동작은 아직 구현하지 않았다.
    }
}

extension ChickenCollectionView: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    	// item(cell) 당 크기를 지정하는 부분이었는데, 한 행에 2개를 넣고 싶었기 때문에 처음에는 collectionView 너비 / 2를 넣었으나 하나씩만 들어가는 것을 보고 조금씩 간격을 주며 조절한 것이다.
        // 높이의 경우에도 2행 구성이기 때문에 세로 스크롤이 생기지 않고 영역에 딱 맞도록 하려고 collectionView 높이 / 2를 했다가 스크롤이 생기길래 간격을 조절하였다.
        let width = (collectionView.bounds.width - 10) / 2
        let height = (collectionView.bounds.height - 10) / 2
        let size = CGSize(width: width, height: height)
        return size
    }
}

1차적으로 구현한 모습. 이거 만들다가 너무 먹고 싶어져서 오늘 저녁에 레드 콤보 먹었다. (진짜임)

03) Pull Request

1차 구현을 마치고, 더 수정사항이 발생하려면 다른 구성요소들과 조합을 봐야할 것 같아서 브랜치의 merge를 우선 하기로 했다. 마지막 협업이 한달 전이었는데, 그 때는 이렇다할 커밋 컨벤션도 브랜치 전략도 없었기 때문에 PR이 열리면 그냥 화면공유로 다같이 보면서 깃헙 홈페이지에서 코드 충돌을 해결하고, 머지까지 진행했었다.

그런데 이번 프로젝트에서는 좀더 본격적인 규칙을 경험하게 되었다.

1. Reviewer 추가하기


우선, repository 소유자가 setting에서 최소 n명의 승인이 있어야만 merge가 가능하도록 지정해두면, 코드 충돌이 없더라도 내 맘대로 브랜치를 합칠 수 없다. PR (Pull Request)을 연 뒤 팀원들을 Reviewer로 지정하고, 나 자신을 Assign 하면 리뷰어들이 내 커밋에 대한 리뷰를 할 수 있다. 이 Review에는 코드에 코멘트 달기(Comment), 수정사항 요청하기(Request changes), 승인하기(Approve) 등의 행동이 포함되어 있다.

2. Comment에 나의 구현 내용과 고민 등을 남기기


지금 보니 내가 굉장히 성의없게도 말 한마디 없이 내가 만든 뷰의 캡쳐만 남겨놓은 것을 확인할 수 있는데😅 보통은 내가 무엇을 어떻게 구현했는지, 앞으로 논의하고 싶은 점은 무엇이 있는지 등을 남긴다고 한다. 밑에 보면 내 commit 내역을 볼 수 있다.

3. 팀원들과 의견 주고 받기

나의 코드를 보고 comment를 남겨주신 모습이다. 또, PR이 열린 뒤에도 commit이 계속 가능하다. 우리 팀은 3명이 승인해야 merge가 가능하도록 제어해놨는데, 나는 현재 2명이 승인해준 상태라 머지가 막혀있는 것을 볼 수 있다.

이렇게 본격적으로 협업 기능을 이용해 본 게 처음이라 신기해서 기록으로 남겨보았다.

profile
iOS Junior Developer

5개의 댓글

comment-user-thumbnail
2024년 11월 26일

다음엔 저희 집으로도 닭다리 하나만 보내주세요

답글 달기
comment-user-thumbnail
2024년 11월 26일

우린 4명 다 승인해줘야 머지 가능한데.. ㅎㅎ 아 참 저는 닭 날개요

답글 달기
comment-user-thumbnail
2024년 11월 27일

멋쨍이

1개의 답글
comment-user-thumbnail
2024년 11월 29일

목치킨이라니 참 이름만 들어도 목이 목목해지네요

답글 달기