자유/잡담

매쉬업 해커톤 후기 (2018.6.30 ~ 2018.7.1)

Chipmunks 2018. 7. 1.
728x90

짧은 후기

2018년 6월 30일 토요일 오후 3시부터, 2018년 7월 1일 일요일 오전 9시까지


길다면 길고 짧다면 짧은 해커톤을 마쳤다!  🏃‍♀️🏃‍♀️


매쉬업 동아리는, IT 연합 동아리이다. 다양한 분야의 지식과 경험을 가진, 직장인과 학생 모두 모여 모바일 앱 서비스를 함께 만들어간다. 👨‍💻👩‍💻🍻


올해 3월 말, 나는 iOS 파트를 지원하여 3개월 간 재밌는 시간을 보냈다. 그리고 5월에 9월까지 이행할 프로젝트가 선정되었다.


그 프로젝트의 기본 토대를 다지기 위한, 이른 6월 말에 무박 2일의 해커톤을 진행했다.


각 프로젝트 팀에는 숙련자와 초보자들이 있다.

숙련자에겐 빠르게 프로토타입을 만들어, 서비스에 어떤 결함이 있고 어떻게 보충해야할 지 생각해보는 기회가 된다.

초보자에겐 개발 프로세스를 경험하고 남은 방학동안 서비스 제작을 위해 무엇을 준비해야 하는지, 몸소 느끼게되는 기회가 된다.


물론, 동아리 목적 상 친목 도모도 목적이다.

팀과는 적어도 일 주일에 한 번, 전체와는 이 주일에 한 번 모였을 정도로 빈도가 생각보다 적다.

그리고 프로젝트 팀과도 몇 번 만나보지 않은 상태다.

동아리 사람들은 물론 프로젝트 팀원들끼리 오래 커뮤니케이션을 하여, 서로를 알고 친해지도록 하는 것이 목적이다.


18시간의 해커톤이 지루하지 않도록, 중간 중간 레크레이션 시간과 먹거리 시간이 적지 않았다. 많이 신경을 써준 것이 눈에 보였다. 이런 자리를 만들어 줘서 너무 감사하다!


시간대 별로 느낀점을 작성하고자 한다. 초보자의 입장에서, 지난 해커톤을 돌아보고 남은 시간에 서비스를 만들기 위해 어떤 것을 준비해야 하는지 정리하고자 하는 목적도 있다.


2018.6.30 PM 12:00, 해커톤 전, 사전 토의를 하다.

장소는 건국대학교 앞 탐앤탐스 카페이다.


사전에 협의한 대로, 해커톤 시작하기 전에 모여 간단하게 아이디어를 모바일 앱으로 구체화 시키고자 하였다. iOS 팀과 백엔드 팀, 디자인 팀 사람들이 하나 둘 씩 모였다. 어색한 공기가 흘렀지만, 원래 처음엔 다 그런게 아니던가!


사전에 개략적으로, 즉 러프하게 그린 와이어프레임을 토대로 실제로 앱 구상을 시작했다. 디자인 팀원분들은 앱 구상에 적극적으로 참여를 한 다음, 앱 디자인에 맞는 컨셉과 레퍼런스들을 찾았다. 백엔드 팀과 iOS 팀도 앱 구상에 각자의 입장에서 얘기했다. 아이디어에 맞게 어떤 로직이 필요한지, 어떤 화면 디자인이나 배치가 더 좋으며 그걸 어떻게 구현할 지 가볍게 토의했다.


각자 참고할 자료들을 가볍게 훑고 해커톤 시작 시간이 되자, 자리를 옮겼다.


2018.6.30 PM 15:00, 해커톤 장소

해커톤 장소가 생각보다 넓고 쾌적했다. 바깥은 덥고 습하며 금방이라도 비가 쏟아질 듯 한 구름 낀 날씨지만, 실내 온도는 끈적임 없이 시원했다. 우리 팀 장소가 에어컨 바로 아래였다. 나는 쌀쌀한 감이 있어, 집에서 가져온 얇은 가디건을 걸쳤다.


책상도 시원하게 큼지막하고 의자도 괜찮았다. 같이 개발을 작당하기에 충분했다. 사전에 협의한 대로, 나는 6구 짜리 멀티탭을 집에서 가져왔다. 여러 개발 세미나와 해커톤을 준비했을 때도, 6구 멀티탭은 늘 현명한 선택이었다. 세팅하기 전, 운영진분들이 나눠준 물티슈로 책상과 의자를 깨끗이 닦았다. 그 후 노트북과 그 충전기, 마우스를 세팅했다.


같은 iOS 팀원 분이 가져온 노트북 거치대와 키보드, 손목 보호 마우스 장비를 보았다. 10대때는 자세나 손목 등을 고려하지 않고 무식하게 했었지만, 20대가 되고 나서는 확실히 몸에 많은 무리가 가지 않게 예방하는 것이 좋다고 느낀다. 성능이 좋은 컴퓨터와 키보드, 마우스, 헤드셋 장비도 좋지만 그 전에 몸의 무리를 막아주는 장비들을 먼저 구입하는게 우선이라고 생각한다. 물론 평소에 자주 스트레칭을 하거나 운동을 하는 것은 당연하다.


시작 시간이 되었는데도, 사람들이 다 오지 않아 지연됐다. 그 동안 먹거리들을 챙기고 해커톤 때 필요한 iOS 코드들을 살펴봤다. 레퍼런스 책을 들고 올까도 생각했지만, 부피가 너무 커서 차마 그러진 못했다. 서비스를 만들 때 필요한, 화면 전환을 어떤 방법으로 하는지 찾아봤다.


화면 전환의 첫 번째 방법은 내비게이션바를 이용한 방법이 있다. 내비게이션 컨트롤러로 화면 전환하고, 다시 화면을 되돌리는 것이 간단한 방법이다. 화면 전환이 발생하는 뷰 컨트롤러들을 스택(Stack)으로 관리한다. 내비게이션 바나 show 방식으로 넘어갔다면 내비게이션이 자동으로 생성된다.


Editor -> Embeded In -> Navigation Controller 로 기존 컨트롤러에 내비게이션 컨트롤러를 추가할 수 있다. 다음 뷰 컨트롤러 호출 메소드는 pushViewController(_: animated:) 이다. 이전 뷰 컨트롤러로 되돌아가는 메소드는 popViewController(_: animated:) 이다.


두 번째 방법은 UIViewController 에 구현된 present 메소드를 사용하는 방법이 있다. 이는 다른 뷰 컨트롤러를 identifier로 참조하게 된다. 모달 스타일로 간단한 애니메이션을 지정해줄 수도 있다. 그리고 호출된 뷰 컨트롤러에서는 dismiss 메소드로 이전 화면으로 되돌아갈 수 있다.


present 메소드와 dismiss 메소드는 비동기 방식이다. 그래서 그 작업이 끝난 뒤에 실행할 함수를 completion 인자로 받고 있다.


1
2
3
4
5
@IBAction func btnPresent(_sender: Any) {         
    let uvc = self.storyboard!.instantiateViewController(withIdentifier: "AnotherVC")
    uvc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
    self.present(uvc, animated: true)
}
cs


1
2
3
@IBAction func btnDismiss(_sender: Any) {
    self.dismiss(animated: true)
}
cs


세 번째 방법은 화면 전환용 객체, 세그웨이를 이용하는 방법이다. 스토리보드에서 서로 다른 두 뷰 컨트롤러를 세그웨이로 연결하여 identifier 을 지정해준다. 그 과정에서 화면 전환 방식을 설정할 수 있다. 코드 상에서 그 identifier 을 불러와 화면을 이동시킨다. UIControllerView 에서 performSegue(withIdentifier: sender:) 메소드로 화면 전환시킨다. 같은 메소드로 뒤로 돌아갈 수도 있다. 돌아가고자 하는 뷰 컨트롤러에서 독바에 위치한 Exit 로 Unwind 세그웨이를 연결시킬 수 있다.


잠깐의 시간 동안 살펴본게 전부지만 확실히 화면을 제어하기 위해서는 반드시 익혀야 할 기초임이 틀림없다. 해커톤이 끝난 다음 확실하게 공부해볼 계획이다.


2018.6.30 PM 16:00, 해커톤 오프닝과 레크레이션

사람들이 어느 정도 모이자 오프닝이 시작됐다. 매쉬업 단장님이 위에서 서술한 해커톤을 하는 의도와 목적을 설명해 주셨다. 후에 재미난 레크레이션이 준비됐다. 각 프로젝트 팀에서, 팀명과 팀구호를 정했다. 전주 음악 찾기와 초성 영화 퀴즈, 소리 영화 퀴즈, 휴지 한 조각 멀리 던지기 등 재밌었다. 누구나 다 알만한 노래와 영화들이라 머리 쓰지 않고 편안하고 즐거웠다.


잠시 노트북을 덮고 오프닝 피티와 게임에 집중하는 시간이었다.


2018.6.30 PM 17:00, 각 프로젝트 소개와 저녁 식사

팀별로 3절지를 나눠줘, 색연필로 자기 프로젝트를 설명하는 문구들을 작성했다. 우리 팀은 물론 다른 팀의 전지들을 살펴봤더니, 디자인 팀분들의 금손이 빛을 발휘했다. 색연필로 저게 가능한가 할 정도로, 나로서는 정말 상상도 못했다. 각 프로젝트의 컨셉에 맞게 개성 있는 소개가 완성됐다.


이후 두툼한 서브웨이 샌드위치들이 대량으로 테이블에 올려졌다. 내 노트북을 잠시 정리하고 샌드위치를 흡입했다. 다양한 여러 소스들을 발라 먹는 것도 소소한 재미였다. 다 먹고 난 후 테이블을 닦아 다시 환경을 정리했다.


2018.6.30 PM 19:00, 달력을 담당하다

프로젝트에서 달력 화면을 담당했다. 우리 프로젝트는, 체크메이트: 위치 기반 급여 자동 계산 앱이다. 날마다 급여를 계산해주고 이를 달력으로 한 눈에 볼 수 있게 배치를 했다. 컨셉 참고 자료는 뱅크샐러드의 달력 화면이다.


< 캘린더 컨셉 참고 자료, 뱅크샐러드 >


유투브 영상을 참고해 UIStackView 와 UICollectionView 를 활용해 위와 같은 달력을 만들었다. 아직 디자인 시안이 나오지 않아, 투박한 디자인이었다. 나름대로 위 화면과 비슷하도록 컨트롤들을 배치했다.


대략적인 달력 과정은 다음과 같았다.


Date 객체로 현재 날짜 정보를 가져온다. 가져온 날짜 정보로부터 Calendar 객체로 요일, 주, 월, 년도 정보를 뽑아낸다.


1
2
3
4
5
6
7
8
9
import Foundation
 
let date = Date()
let calendar = Calendar.current
 
var day = calendar.component(.day, from: date)
var weekday = calendar.component(.weekday, from: date) - 1
var month = calendar.component(.month, from: date) - 1
var year = calendar.component(.year, from: date)
cs


문자열 배열로 월별 이름을 만든다. 정수형 배열로 월별 요일 수 정보를 만든다. 프로젝트에서는 영어 월 이름을 사용한다. Next 버튼과 Prev 버튼으로 월을 이동한다. 다음 월과 이전 월의 이름과 요일 수를 가져온다.


UICollectionView도 UITableView와 마찬가지로 커스텀 셀(Cell)을 만들고 identifier 을 지정한다. 그 identifier 으로 셀을 재사용하여 화면에 표시해준다. 그리고 표시할 셀의 수를 지정해줘야 한다. 해당 뷰 컨트롤러는 UICollectionViewDelegate 와 UICollectionViewDataSource 프로토콜을 적용해야 한다.


UICollectionViewDelegate 에서 다음의 두 개의 Required 한 메소드가 있다.


1
2
3
func collectionView(UICollectionView, numberOfItemsInSection: Int-> Int
 
func collectionView(UICollectionView, cellForItemAt: IndexPath) -> UICollectionViewCell
cs


첫 번째 메소드가 표시할 셀의 수를 리턴해 지정한다. 두 번째 메소드가 해당 위치에 표시할 셀을 리턴해 지정해준다.


처음에는 해당 달에 요일수를 리턴했지만, 맨 처음에 앞 달의 요일들도 있어 그 부분을 고려해야 한다. 위 영상에서는 처음 월과, 다음 버튼과 이전 버튼을 눌렀을 때를 구분하여, 현재 달이 아닌 요일들을 빈칸처리해준다.  현재 달의 빈 요일(EmptyBox)는 NumberOfEmptyBox 변수로 그 개수를 구한다. 대략적인 원리는 다음과 같다.


오늘에 해당하는 요일을 가져온다. 그 요일은 일주일 값을 가진다. 여기선 0부터 6까지의 값을 가진다. 그 다음, 현재 일부터 1일까지 계속 뺀다. 이 때, 그 요일도 동시에 뺀다. 대신 0부터 6까지 순환한다. 그래서 1일날 전까지의 요일 값을 구한다. 그것이 바로 Empty Box 의 개수다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NumberOfEmptyBox = weekday
dayCounter = day
            
while dayCounter > 0 {
    NumberOfEmptyBox = NumberOfEmptyBox - 1
    dayCounter -= 1
 
    if NumberOfEmptyBox == 0 {
        NumberOfEmptyBox = 7
    }
}
 
if NumberOfEmptyBox == 7 {
    NumberOfEmptyBox = 0
}
            
PositionIndex = NumberOfEmptyBox
cs


현재 달의 Empty Box 를 저장했으면, 다음 달과 이전 달로 이동했을 때 간단한 연산으로 구할 수 있다.


1
2
3
4
5
6
7
8
9
10
// 다음 달 EmptyBox 계산
NextNumberOfEmptyBox = (PositionIndex + DaysInMonths[month]) % 7
PositionIndex = NextNumberOfEmptyBox
 
// 이전 달 EmptyBox 계산
PreviousNumberOfEmptyBox = (7 - (DaysInMonths[month] - PositionIndex) % 7)
if PreviousNumberOfEmptyBox == 7 {
    PreviousNumberOfEmptyBox = 0
}
PositionIndex = PreviousNumberOfEmptyBox
cs


위 연산 과정들에 대해 아직까지 이해가 좀 되지 않는다. 달력 커스텀을 위해서라면, 위 유도 방법은 꼭 알아야 하므로 나중에 시간이 되면 공부를 해봐야겠다.


이 때 쯤에, 졸음이 밀려와 잠깐 숙면했다. 한 10분~20분 정도 엎드려서 잤다. 멍 때린 채, 노트북 화면을 바라보고 목 아프게 꾸벅 꾸벅 졸고 있을 시간에 확실한 자세로 자는 것이 좋다. 화장실에 가서 찬 물로 세수하는 것 보다는, 확실하게 자는게 나한테는 더 낫다. 뭔가 조치를 취한다해도 일어서서 졸 수 있는 나에게는, 비효율적인 방법이다...


2018.6.30 PM 22:00, 달력 세부 화면

아마 세부 화면은 UITableView 으로 구현을 할 것이기 때문에, 미리 세팅을 해놓았다. 뷰컨트롤러에 UITableViewDelegate 와 UITableViewDataSource 프로토콜을 받는다. 마찬가지로 커스텀 셀을 만든 다음 불러왔다.


아직 그 화면까지 정해진게 없어서 더 이상의 레이아웃 배치는 하지 않았다. 그 다음에 달력 화면에서 UICollectionViewCell 을 터치했을 때, 그 정보를 달력 세부 화면으로 보내야 한다. 그 부분을 건드려보기로 한다.


1
2
3
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {        
    self.performSegue(withIdentifier: "DetailSegue", sender: indexPath)
}
cs


didSelectItemAt 으로 셀 아이템을 선택했을 때 처리해줄 수 있다. performSegue 메소드의 sender 인자로 indexPath 정보를 보내준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "DetailSegue" {
        if let destinationVC = segue.destination as? CalendarDetailViewController {
            guard let indexPath = sender as? IndexPath else { return }
                
            guard let cell = Calendar.cellForItem(at: indexPath) as? DateCollectionViewCell else {
                 return
             }
                
            destinationVC.Month = currentMonth
            destinationVC.Day = cell.DateLabel.text!
            destinationVC.Price = cell.PriceLabel.text!
        }
    }
}
cs


UIViewController 에서 세그웨이로 화면을 전환하기 전에, func prepare(for segue: sender:) 메소드로 원하는 액션을 취할 수 있다.


세그웨이의 identifer 속성으로 구분한다. 세그웨이의 도착지 정보인, 달력 세부 화면 컨트롤러를 받는다. 그리고 sender 매개변수로 가져온 IndexPath 를 바탕으로 해당 셀의 정보를 가져온다. 현재 월과 해당 셀의 정보들을 같이 보낸다.


이제와서 보니 문자열로 보내는 것 보단 숫자로 보내주는 편이 더 안전한 방법인 것 같다.


2018.7.1 AM 00:00, 레크레이션, 피카츄 배구 게임

드디어 두 번째 날이 시작됐다. 시계를 볼 때 마다 시간이 홀쩍 지나가서 해커톤이 길다고 느껴지지 않았다. 달력 세부 화면을 대충 완성을 한 타이밍이었다. 자정에 갑자기 피카츄 배구 게임 대회가 열렸다. 각 팀에서 대결해서 우승자를 가렸다. 피카츄 배구 고인물(?)이 등장하여 모두를 평정시켰다...😱


2018.7.1, 야식

야식으로 치킨과 피자가 왔다. 피자는 한 판으로, 한 조각씩 나눠 먹었다. 치킨도 하나가 와서 개당 한 두 조각을 집어 맛있게 먹었다. 양이 적어서인지 잘 모르겠는데, 도통 언제 먹었는지 기억이 나지 않는다...! 자정 전에 먹었는지 후에 먹었는지... 분명 맛있게 먹은 다음, 양치를 하고 세수를 했던 것은 기억이 난다. 아마 이 때 쯤에 축구 경기가 있었던 것도 같기도 한데... 잘 모르겠다! 😀

간단하게 씻고 와서, 치킨 뼈들을 한 곳으로 모았다. 피자판과 치킨판을 따로 한 곳에 두고, 음식물은 일반 쓰레기 통에 넣든, 변기로 버릴 수 있는 것들은 화장실에서 버렸다. 그런 다음 물티슈로 책상을 정돈했다.

2018.7.1 AM 01:00, 디자인 시안 배포, 제플린

기다리고 기다리던 디자인 시안이 나왔다. 디자인 시안은 제플린으로 배포가 됐다. 그동안 제플린 말만 들어봤는데, 직접 써 본 적은 이 때가 처음이었다. 프로그램을 인스톨해야 하나 싶었는데 다행히 웹용 대쉬보드로 조회가 가능했다!


컨트롤 간의 간격 수치, 컨트롤 크기, 색상, 글꼴 모두 보여 곧 바로 적응시키는데 최적화 됐다! 이 때 나는 아직 달력 디자인 시안이 나오지 않아, 원형 컨트롤에 대해 알아보고 있었다.


커스텀 뷰 컨트롤을 만드는 방법과 CAShapeLayer 을 찾아봤다. 그러나 내 생각대로 동작하지 않았다. 뷰의 라이프 사이클과 그래픽으로 제어하는 지식이 부족해서인 것 같다. 후에 이 부분 문서를 더 많이 찾아봐야겠다.


그리고 이 때부터 버전관리를 사용했다. 우리 프로젝트의 보일러플레이트를 만든 iOS 팀원님의 깃허브를 토대로, 브랜치를 나누어 각자 맡은 부분을 개발하기 시작했다. iOS 프로젝트 파일에서 조금만 손을 봐도 쉽게 충돌이 나는 것을 볼 수 있었다. 그래서 최대한 충돌이 나지 않게, 스토리보드를 잘게 나누어서 개발했다. 각 스토리보드에 한 명씩 맡아 개발을 이어 나갔다.


이런 번거로운 점을 해결하는 방법은 위와 같은 방법도 있을 것이고, 또 코드로 뷰 단을 만드는 방법도 있을 것이다. 일단 뷰 단으로 모든 컨트롤을 만드는 법에 익숙하지 않기에, 스토리보드를 나누는 편으로 진행했다...! 코드로 뷰 단을 만드는 방법도 알아두면, 협업할 때 참 편할 것 같다!


2018.7.1 AM 03:00, 캘린더 디자인 시안 적용

따로 만든 캘린더 프로젝트를 그대로 가져오고, 캘린더 디자인 시안대로 새로 배치하기 시작했다. 이 과정에서 오토레이아웃을 어떻게 적용시키는게 맞나 혼자 고민했다. 따로 문서를 찾아 본 것은 아니었다.


일단 최대한 변하지 않는 뷰들을 기준으로 위치와 크기를 잡는 쪽으로 제약(Constraint)들을 적용시켰다. 무작정 아무 뷰나 잡고 제약을 줘버리면, 나중에 디자인 시안이 변경 됐을 때, 재빨리 바꾸지 못할 것이고 더 꼬여버릴 염려가 있기 때문이다.


그럼에도 잘 했는지 조금 불안하긴 하다. 다음 프로젝트 진행 때는 디바이스별로도 확인을 해봐야 겠다. 세부 화면 디자인도 나왔기 때문에, 테이블뷰 커스텀 셀들을 만들고, 시안대로 배치하고, 실제로 뷰 컨트롤러에서 적용 시켰다.


하나 하나 완성하는 대로 커밋을 남기고, 내 브랜치로 푸쉬했다. 그리고 중간 중간 다른 화면을 맡은 iOS 팀원분들과 같이 마스터 브랜치로 합치는 작업을 했다. 그 과정에서 충돌이 어떻게 일어나는지, 또 어떻게 고치는지 확인했다. 충돌이 일어나지 않고 푸시가 됐으면, 왠지 기분이 묘했다.


원래 계획했던 기획안대로 iOS 프론트 디자인 시안을 적용시키는 것으로, iOS 팀은 해커톤 활동을 마무리 했다.


새벽 5시부터 날이 밝아오기 시작했다.


2018.7.1 AM 06:00, 잠깐의 휴식

주변을 둘러보니 친목을 다지는 소음과 동시에 조용히 숙면을 취하신 분들이 많았다. 얼추 해커톤 활동을 마무리 해서, 이 때다 싶어 잠 좀 깰 겸 잠깐 게임 한 판을 했다.😃


2018.7.1 AM 07:00, 숙면

아침 8시 최종 발표까지 컨디션 조절을 위해 약간의 숙면을 취했다. 😴


2018.7.1 AM 08:00, 최종 발표 FIN

해커톤 클로징과 최종 발표가 오전 9시까지 이어졌다. 각 프로젝트 팀의 결과물과 앞으로의 계획들을 말했다. 전체적으로, 해커톤 동안 대부분 디자인 시안을 다졌다. 디자인 시안의 일부를 모바일 프론트 단으로 옮겼고, 백엔드는 데이터베이스 설계작업과 초기 일부 API 를 작업했다. 이 정도만 하더라도, 앞으로 이어갈 프로젝트에 큰 도움이 될 것 같다.


마지막에 생일이신 분들, 케이크 촛불식했다. 생일 축하드려요!!~ 🎂🕯🎉


다 같이 사진을 찍고, 프로젝트 팀끼리도 사진을 찍고 가벼운 발걸음으로 집을 향했다.




결과물

깃허브 링크 : https://github.com/mash-up-kr/Checkmate-ios



iOS 팀원분들, 백엔드 팀원분들, 디자이너 팀원분들 모두 수고하셨습니다!!

댓글