iOS - 개인 프로젝트 "독서하라쿤" 회고
처음으로 진행한 출시 프로젝트를 돌아보고자 회고록을 작성합니다.
프로젝트 소개
독서 생활을 더욱 풍부하게 만들어줄 귀여운 라쿤과 함께하는 도서 기록 및 관리 앱 “독서하라쿤”
ver 1.0 주요 기능
- 도서 저장
검색 화면에서 다양한 추천 도서를 확인하거나, 직접 검색하여 읽고 싶은 책을 손쉽게 저장하고 관리할 수 있습니다. - 메모 기록
책을 읽는 도중 떠오른 생각이나 중요한 내용을 메모하여 독서의 깊이를 더할 수 있습니다. - 독서 타이머
독서 시간을 정확하게 측정할 수 있는 타이머 기능을 제공합니다. - 통계
독서한 책과 시간을 캘린더와 차트를 통해 시각적으로 한눈에 파악할 수 있습니다.
- 개발 기간
2024/03/01 ~ 2024/03/22 - 아키텍쳐
MVVM + Custom Observable Class 를 활용한 옵저버 패턴
사용한 오픈소스 라이브러리
- Alamofire
- DGCharts
- FSCalendar
- FSPagerView
- Kingfisher
- SVProgressHUD
- Tabman
- Snapkit
- Then
- Toast
DB 설계
책을 저장하고, 해당 책에 관한 메모와 통계 정보를 저장하고,
다른 화면에서 사용하기 위해 Book 과 1:N 관계 (To-Many Relationship) 으로 구조를 설계했습니다.
이 DB 구조의 특장점은 메모 테이블 전체 혹은 통계 테이블 전체를 기간별로 필터링하기 쉽고,
특정한 자식 데이터의 부모 데이터를 쉽게 알 수 있기에 설계했던 내용대로 큰 이슈없이 프로젝트를 진행할 수 있었습니다.
// 초기화 구문 제외
class Book: Object {
@Persisted(primaryKey: true) var id: ObjectId
@Persisted var title: String // 책 이름
@Persisted var memoList: List<Memo> // 메모 리스트
@Persisted var statsList: List<Stats> // 통게 리스트
@Persisted var link: String // 알라딘 링크
@Persisted var author: String // 저자
@Persisted var descript: String // 책 설명
@Persisted var isbn: String // isbn13
@Persisted var cover: String // 커버 이미지 URL
@Persisted var categoryName: String // 카테고리명
@Persisted var publisher: String // 출판사
@Persisted var regDate: Date // 책 등록일
@Persisted var endDate: Date? // 다 읽은 책 날짜
@Persisted var bookStatus: Int // 책 상태 (읽을 책, 읽고 있는 책, 다 읽은 책)
@Persisted var page: Int // 책 페이지
@Persisted var totalReadingTime: Int // 총 읽은 시간 (초 단위)
}
class Memo: Object {
@Persisted(primaryKey: true) var id: ObjectId
@Persisted var title: String // 메모 제목
@Persisted var content: String? // 메모 내용
@Persisted var photo: String? // 메모에 넣을 사진
@Persisted var regDate: Date // 메모 등록일
@Persisted(originProperty: "memoList") var superTable: LinkingObjects<Book>
}
class Stats: Object {
@Persisted(primaryKey: true) var id: ObjectId
@Persisted var readingTime: Int // 읽은 시간
@Persisted var readingDate: Date // 읽은 날짜
@Persisted(originProperty: "statsList") var superTable: LinkingObjects<Book>
}
구현하며 발생한 이슈
가장 큰 이슈는 처음 사용하는 라이브러리, Background Timer, Custom Modal · Alert 입니다.
1. 처음 사용하는 라이브러리
이번 프로젝트는 라이브러리를 굉장히 많이 사용했고, 그만큼 다양한 기능을 구현했습니다.
처음 사용하는 라이브러리를 활용해 개발하기 위해 주로 github 에서 ReadMe 와 예시 프로젝트를 다운받아 내부 코드를 뜯어 보았고,
국내 해외 블로그를 다양하게 살펴보며 종합적인 정보를 통해 구현했습니다.
특히, FSPagerView 를 사용할 때, 기본적으로 제공해주는 image, title 이외에도 다른 내용이 더 필요했기에
커스텀 Cell 을 사용해야 했는데 정보가 적어서 직접 컨트롤 해야했습니다.
pagerView 의 itemSize 를 조절하고, SearchPagerViewCell 을 생성하여 하나하나 조작해보며 추천 리스트를 구현했습니다.
2. Background Timer
타이머 기능을 처음 구현했기에 굉장히 많은 시간을 들였지만,
고려해야 하는 지점이 굉장히 많았기 때문에 얻어가는 내용이 굉장히 많았고,
쉽게 얻을 수 없는 값진 경험이었습니다.
중요한 포인트 4가지를 짚으며 구현 내용은 링크를 첨부하겠습니다.
1. 스레드의 RunLoop 개념
2. UserDefaluts 를 이용한 백그라운드 타이머 구현
3. sceneDidBecomeActive 메서드를 이용한 토스트 메세지 구현
4. 타이머가 실행 됐을 때 다양한 상황에 대한 대응
https://youngjoo00.tistory.com/8
3. Custom Modal · Alert
애플의 기본 Modal, Alert 이 앱 컨셉에 맞지 않다고 판단하여, 사용자의 UI/UX 경험을 생각해 커스텀 Modal · Alert 을 생성했습니다.
이전까지와 다르게 뷰를 그리고, 화면전환하는 새로운 개념들을 익히는데 많은 시간을 들였지만
결과물을 통해 앱의 컨셉이 확실하게 살아났고, 앞으로 프로젝트에서도 잘 쓸 수 있는 개념이기에 좋은 경험이었습니다.
중요한 포인트 3가지를 짚으며 구현 내용은 링크를 첨부하겠습니다.
1. UIPresentationController 와 frameOfPresentedViewInContainerView 함수, containerView, PresentedView 의 개념
2. UIViewControllerTransitioningDelegate
3. Extension + UIViewController
https://youngjoo00.tistory.com/9
시간 관계상 구현하지 못했던 부분
기존에 사용하던 기술 7, 새로운 기술 3 의 비율로 프로젝트를 진행하는 것이 이상적이라고 생각했지만
새로운 기술을 구현하며 발생한 이슈를 해결하기 위해 많은 시간을 들였던 것 같습니다.
modern CollectionView 를 사용해보고 싶었으나, 이슈가 발생했고 시간관계상 포기했기에
제대로 사용하지 못한 부분도 아쉽게 느꼈습니다.
CRUD 기능, 메모에 이미지 추가 기능, 다국어, 캘린더, 통계 등
추가할 기능이 많지만 약 2~3주 기간동안 모든 것을 구현하기에 불가능하다고 생각되어
기능을 최소 단위로 끊어서 1.0 ver 으로 출시를 진행했습니다.
추후 업데이트로 부족한 기능을 메꾸면 되기 때문에 과감하게 최소 단위로 끊어서 개발하고, 출시했던 판단은
출시 후 회고를 진행하니 현명한 판단이었다고 생각합니다.
코드 돌아보기
MVVM 구조
프로젝트 1주차 까지는 MVVM 에 익숙하지 않았기에 MVC -> MVVM 리팩토링하고,
View, ViewModel 의 역할을 어떻게 분리해야 하는지에 대해 많은 고민과 시간이 들었습니다.
2주차부터는 처음부터 MVVM 구조로 코드를 작성하는 것이 가능해졌습니다.
굳이 MVVM 이 필요없다고 느껴지는 Custom Alert 과 같은 부분은 MVC 로 개발했고,
이 외에 주요 화면과 주요 기능은 MVVM 으로 진행했습니다.
코드 추상화, 모듈화
싱글톤 패턴, 라우터 패턴, 레포지토리 패턴 등 코드를 추상화하고 모듈화 하는 것에 익숙해져 자주 애용하게 되었습니다.
Enum + Switch 구문을 이용해 재사용하는 화면에 transtion case 를 붙여 유지보수성을 챙겼고,
이전까지는 열거형을 활용해 뷰를 재사용하는 구조를 짜는 것이 어려웠으나 이번 기회로 익숙해질 수 있었습니다.
Dynamic DisPatch, Static DisPatch
더 이상 상속하지 않는 final 키워드 사용은 익숙해져 필요한 클래스에 잘 작성해두었지만,
아직까지 private 과 같은 접근 제어자 사용은 생각을 잘 못하는 부분인 것 같습니다.
다른 사람과 협업하거나, 컴파일 속도를 향상시키는 등 다양한 부분에서 중요하기에 앞으로 습관을 들이고, 익숙하게 사용해야겠습니다.
NWPathMonitor
네트워크 상태를 감지할 수 있는 애플의 프레임워크를 사용해
SceneDelegate 의 willConnectTo 메서드에서 사용자의 네트워크 상태를 감지했습니다.
1. 네트워크가 없는 경우 현재 Scene 에 새로운 window 를 생성
2. windowLevel = statusBar 지정
3. 새로운 window 에 미리 생성해둔 networkErrorView 를 addSubview
4. errorWindow 에 할당하여 최종적으로 뷰를 띄워줍니다.
반대로 네트워크가 있는 경우 errorWindow 를 nil 로 할당하고, isHidden = true 로 기존 화면을 띄웠습니다.
현재 모든 화면에서 네트워크를 감지하고 있기 때문에,
'네트워크가 필요한 화면에서만 사용하는 것이 필요하겠다' 라는 생각이 들었습니다.
추후 확실하게 정리하고, 컨트롤 가능한 코드로 수정해야겠습니다.
정리
앱 출시를 준비하면서 정보를 찾을 때 해외 문서, 영상, 라이브러리의 example project 를 조금씩 확인하다보니
이전보다는 확실히 개발 역량이 향상되고 있음을 느꼈습니다.
앞으로 그 유명한 Rx를 학습하고, 리팩토링을 진행하게 될탠데
Rx가 어떤 강력한 기능을 제공해서 대부분의 기업에서 선호하는지, 하루 빨리 사용해서 프로젝트에 녹여보고 싶습니다.
이번에 출시 프로젝트를 진행하며, 사용자의 관점에서 바라보고 다양한 이슈에 대한 대응을 할 수 있는 좋은 기회였습니다.
앱을 출시하고, 감사하게도 주변인들에게 많은 피드백을 받게 되었고, 벌써 업데이트 할 내용이 20개 정도 되는 것 같습니다.
첫 출시였기 때문에 아직 완벽하지 않지만 꾸준히 성장해서, 사용자 중심의 개발자로 성장하고 싶다고 생각했습니다.