# 몽실 ## Ground Rule 주말 포함 매일 오후 2시 ~ 오후 10시 주 7일 기반으로 함. 주말같은 경우는 하는것이 기본이나 3일전만 이야기를 해주면 자유롭게 약속 잡고 그날은 쉬는걸로 월요일 6시 ~ 7시(20분 정도함), 수요일 7시(2시간 반), 목요일 4시(두시간) 월요일 오전 10시 ~ 12시 목요일 오전 10시 ~ 12시 목요일 오후 4시 ~ 6시 ## 기술스택 - MVVM - Combine - Clean Architecher - Firebase ## 디자인패턴 - 빌더 패턴 ## 계획 기능정리 데이터 구조 클린아키텍쳐 구조 짜기 UML ## 기능정리 ### 달력 뷰 #### 기본 - 달력을 보여준다. - 오늘 날짜를 빨간색 점으로 표시한다. - 일기를 쓴 날자를 이모티콘으로 표시한다. - 화살표로 달력을 바꿀 수 있다. - 목차 클릭 시 달력형태가 아니라 컬렉션 형태로 보여준다. -> 두 화면 모두 등록화면으로 이동이 가능함. #### 추가하면 좋을 기능 - 달력 이동을 쉽게 수정 ### 등록 뷰 #### 기본 - 명언을 보여준다. - 기분을 입력받는다. <- 새로운 뷰가 존재 - 기분을 일정높이만 보여주고 스크롤이 가능하다. - 최신글이 위로간다. -> 변경해야함 - 기분 이모티콘을 클릭하면 여러가지 기분들이 컬렉션 뷰로 보여진다. - 기분을 클릭하면 등록 화면에서 이미지가 바뀐다. - 명언 사진 공유, 스크랩(관심), 저장 기능, 일기 숨기기 #### 추가하면 좋을 기능 - 우상단 버튼으로 공유 기능등을 보여준다. - 댓글 수정, 삭제 기능 ### 관심 뷰 #### 기본 - 스크랩 한 명언들을 보여준다. - 클릭 시 수정화면으로 넘어간다. ### 설정 뷰 - 알림 설정을 on 하면 오후 6~7시에 알림을 보낸다 (오늘의 기분을 입력하세요) - 알림설정을 클릭하면 네비게이션으로 알림 ON, OFF 할 수 있다. - 앱 소개 - 런치스크린 화면을 보여준다. #### 추가하면 좋을 기능 - 비밀번호 변경 - 암호확인 화면 ## Domain ### Entities - Comment ```swift date: Date emoticonName: String script: String ``` - Diary ```swift url: String date: Date comment: [Comment] is ``` ### UseCases - CRUD - 명언 - 댓글 - imageFetch ### Interfaces #### Repositories - Dummy - CoreData - FireBase - imageRepository(GitHub Networking) - DTO ```swift id: String image: String squareImage: String ``` import Foundation ## Readme # 📒 Mongsil ### 프로젝트 기간 <2022-12-29 ~ 2023-01-30> ## 📖 목차 1. [소개](#-소개) 2. [실행 화면](#-실행-화면) 3. [고민한 점](#-고민한-점) 4. [트러블 슈팅](#-트러블-슈팅) 5. [참고 링크](#-참고-링크) ## 🌱 소개 ### 💻 개발환경 및 라이브러리 [![swift](https://img.shields.io/badge/swift-5.0-orange)]() [![xcode](https://img.shields.io/badge/Xcode-14.2-blue)]() [![iOS](https://img.shields.io/badge/iOS-13.4-green)]() <img src="https://img.shields.io/badge/Combine-orange?style=flat&logo=Swift&logoColor=ffffff"/> <img src="https://img.shields.io/badge/SPM-orange?style=flat&logo=Swift&logoColor=ffffff"/> <img src="https://img.shields.io/badge/Firebase-yellow?style=flat&logo=FireBase&logoColor=0c2a31"/> <img src="https://img.shields.io/badge/CoreData-orange?style=flat&logo=Swift&logoColor=ffffff"/> ## 🧑 팀원 <img src = "https://i.imgur.com/FixFdi0.jpg" width="200" height="200">|<img src = "https://i.imgur.com/0YI1hEB.jpg" width="200" height="200">| |:--:|:--:| |[Kiwi](https://github.com/kiwi1023)|[그루트](https://github.com/Groot-94)| ## 📱 실행 화면 |비밀번호 입력|달력|리스트| |:--:|:--:|:--:| |<img src = "https://i.imgur.com/XghAhiS.gif" width="250">|<img src = "https://i.imgur.com/QG5juhL.gif" width="250">|<img src = "https://i.imgur.com/xbeIFF9.gif" width="250">| |일기 추가|일기 수정/삭제|일기 메뉴화면| |:--:|:--:|:--:| |<img src = "https://i.imgur.com/CZkjqao.gif" width="250">|<img src = "https://i.imgur.com/4gmDikl.gif" width="250">|<img src = "https://i.imgur.com/LvVZy7u.gif" width="250">| |관심|설정| |:--:|:--:| |<img src = "https://i.imgur.com/qph5PiT.gif" width="250">|<img src = "https://i.imgur.com/oYsVULk.gif" width="250">| ## 👀 고민한 점 <details> **<summary>Clean Architecture를 왜 사용하는가에 대한 의문이 생겼습니다.** </summary> - Testable한 코드를 만들기 위해 MVVM을 알아보던 중 Clean Architecture를 함께 쓰는 관련 글이 많이 있었습니다. - MVVM과 Clean Architecture가 어떤 이유로 함께 사용되는지에 대한 의문이 생겨 회의를 진행했습니다. - Clean Architecture를 사용함으로써 MVVM의 Model 부분을 세분화해서 코드를 분리할 수 있었습니다. - 각 특성에 맞는 역할만을 수행하도록 분리해주면 모델부분의 코드들이 변화와 확장에 열려있게 되기 때문에 유지보수에 용이할 수 있음을 알게 되었습니다. - 이런 장점을 학습하기 위해 기존 CoreData와 새롭게 Firebase를 추가하여 로컬과 리모트 DB를 함께 사용할 수 있도록 리팩토링을 진행했습니다. </details> <details> **<summary>REST_API의 Get을 통해 다수의 셀에 이미지를 설정해줘야 하는 부분에서 셀 재사용 시 기존 Reqeust를 취소 처리하는 방법을 고민했습니다.** </summary> - CompletionHandler 방식을 사용할 때 Task를 cancel하는 메서드가 있었지만, Combine을 사용해야 하는 상황에선 cancel을 호출할 수 없었습니다. - Combine 학습을 통해 AnyCancellable을 취소하는 것으로 구독이 종료되고 구독에 의한 요청이 취소됨을 알 수 있었습니다. - Cell의 prepareForReuse()에서 image를 요청하는 AnyCancellable을 모두 제거해주는 방법으로 reqeust 취소를 구현했습니다. </details> <details> **<summary>Controller와 View 간의 데이터 바인딩을 진행할때에 Combine사용에 대한 고민이 있었습니다.** </summary> - Controller 내부에서 사용자의 Action을 입력 받는 등의 데이터 바인딩처리 하는 것에 Combine을 이용하는 것은 무리가 없었습니다. - 그러나 Controller 하위 View에서는 바인딩 처리를 어떠한 방법으로 할 것인가에 대한 고민이 생겼습니다. - RxSwift같은 경우 RxCocoa와의 호환을 통한 데이터 바인딩하는 오퍼레이터가 존재하는 것으로 알고 있습니다. 그러나 Combine의 경우 SwiftUI의 사용을 전제하는 프레임워크이기 때문에 UIkit에서는 해당 기능을 하는 오퍼레이터가 존재하지 않는 것 같았습니다. - 결국 Delegate 및 클로저를 통한 데이터 바인딩을 구현하였고, 앞으로 UIkit에서 해당 기능을 Combine을 통해 어떻게 구현하는지 학습예정입니다. </details> ## ❓ 트러블 슈팅 <details> **<summary>초기에 설정한 useCase의 read 함수 반환타입이 [Diary] 타입이였으나, 추후에 Remote DB 사용 시 비동기로 데이터를 전달받기 때문에 데이터의 전달 타입을 비동기로 처리해야 하는 문제가 있었습니다.** </summary> - 시도 - Combine을 사용해서 비동기 처리를 하기로 결정했기 때문에 Combine의 어떤 타입으로 값을 전달할 지 회의를 진행했습니다. - 해결 - Repository에서 값을 보낼 때 값의 비동기 처리와 Error 처리가 가능한 Future 타입을 사용하기로 결정했습니다. - 실제로 값을 리턴하는 형식은 외부에서 값을 전달하지 못하고, 여러 클라이언트에서 로직변경 없이 전달받을 수 있는 AnyPublisher 타입으로 변환해서 사용했습니다. </details> <details> **<summary>textView의 높이를 내용에 맞게 변경할 때 constraint의 충돌문제가 있었습니다.** </summary> - 시도 - textView의 높이를 일정한 높이까지만 늘리고 그 후엔 스크롤이 가능하도록 구현하려 했습니다. - 최고높이 도달 후 고정하기 위해 constraint를 설정했습니다. 그 후 다시 줄이고 늘이는 과정 반복 시 새로운 constraint와 기존 constraint의 충돌로 인해 경고가 발생하는 문제가 있었습니다. - 해결 - 최고높이 도달 시 마지막 constraints을 확인, firstAttribute가 height일 시 기존 constraint을 지우고 새롭게 설정해주는 방법으로 해결했습니다. ![](https://i.imgur.com/x76PMTg.png) </details> <details> **<summary>비밀 번호가 설정되어 있을 때 foreGround 진입시 이전 뷰와 상관없이 비밀번호 입력 창을 띄워야 하는 문제** </summary> - 시도 - 앱이 Foreground 상태 진입시 SceneDelegate(sceneWillEnterForeground)에서 NotificationCenter를 이용해 비밀번호 입력 창을 띄워야 한다고 생각하였습니다. 그러나 이 경우 모든 ViewController에서 해당 NotificationCenter를 observe하고 있어하는데 만약 viewcontroller의 갯수가 수백개라면 모든 viewcontroller에서 observe 코드를 작성해야 하나 고민이 생겼습니다. - 해결 - 우려와는 달리 최상위 viewController에서만 notificationCenter를 구독하면 하위 플로우의 모든 뷰들은 해당 noti를 구독하게 되어 한번만 구독 코드를 작성해주면 된다는 사실을 알게 되었습니다. ![](https://i.imgur.com/unGX1YA.png) (캘린더 뷰 이하 플로우 뷰는 자동으로 비밀번호 입력창을 띄우게 됨, 상위 플로우 뷰는 안띄움) </details> ## 🔗 참고 링크 - https://github.com/kudoleh/iOS-Clean-Architecture-MVVM - https://developer.apple.com/documentation/combine - https://zeddios.tistory.com/925 --- [🔝 맨 위로 이동하기](#-mongsil)