1.4.0 release 개발일지
이번에 1.4.0을 개발하면서 있었던 것들을 정리해두고자 함.
해당 버전에서 주로 학과 기능 공지 개발을 담당하였음.
🔨 Xcode 14.2
iOS 15.0 +
# 구현해야 할 기능
- API
- 학과 공지
- 학과 공지 구독
- 학과 리스트
- api v2로 모두 변경
- 화면
- 학과 공지와 관련한 모든 뷰
- 구현
- 학과 리스트 검색
- 구독한 학과 리스트 검증
# 학과 구현 외 버그 개선 작업
- notification 버그 개선 (사일런트 푸시 및 기존 내가 받은 알림이 저장되지 않던 버그)
- 학교 url이 변경되어 그에 따른 대응
🏗️ 프로젝트 구조 개선 작업
- Tuist 도입
처음에 개발을 맡게 되었을 때는 얼마나 한 2~3주 정도 걸릴 것 같다고 생각했었는데, 생각보다 아주 오랜 시간이 걸리게 되었음.
📅 스케줄 계획
- 2월: 프로젝트 구조 변경 계획
새로운 기능을 만들기 전 기존 프로젝트의 구조 정리를 먼저 시도.
- 기존에 분리되어 있던 Map 모듈에 기능을 덧붙이기로 하면서 동일한 기능임에도 Map에서 사용하기가 어려워지는 문제를 발생.
- 즉, 서비스에서 사용하는 동일한 Model 마저도 Map에서는 사용할 수 없어서 새롭게 만들어야 했음.
- 그렇다면 App에서 구현된 몇몇 기능을 SDK로 넘긴 후 Map도 SDK를 사용할까?
- 나쁘지 않은 선택지라고 생각했으나, App이 먼저 만들어진 후 SDK가 나중에 만들어졌기에 SDK는 App에 지나치게 의존적이었고, Map에서 사용할 수 있도록 구조를 개선하는게 공수가 커서 나은 선택지라고 생각되지 않았음.
그렇다면 모든 것들을 개선할 수 없기에 순차적으로 우선 아래와 같은 구조로 바꿔볼까 함.
SDK에서 Core를 모듈 형태로 분리하여 KuringApp - KuringSDK - Core라는 3개의 레이어로 분리
그리고 KuringMap - KuringCore로 2개의 레이어로 분리
이렇게 분리하고 공통으로 사용되는 DesignSystem적인 부분이나 Logger, ColorSet 등을 별도의 레이어로 분리하여 사용하고자 했음.
🚨 하지만, 2번의 분리하고자 하는 시도는 결과적으로 실패로 끝나게 되었음.
우선,
1. Microfeatures Architecture 영상을 보고, 한번에 뒤집어 까려고 시도하기엔 숙련도가 매우 부족했던 것.
2. 기존에는 최선이라고 느껴지던 구조가 리팩하는 시점에는 생각보다 많이 얽혀 있었던 것.
3. UIKit + RxSwfit 보다 SwiftUI + Combine의 기반에서 어떤 구조가 적합한지 아직은 사례가 부족했던 것.
- 기존에 가장 잘 다루던 ReactorKit과 구조가 닮았으나 TCA가 아직 0.x라서 도입하기엔 1.0에서 격변하지 않을까 걱정.
- MVI, MVState, claen-swiftui 등의 예제를 확인하면서 우리 프로젝트에 어떤것들을 적용할 수 있는지 리서치를 오래 진행.
4. 취업을 하면서 사이드 프로젝트에 할애하는 시간이 어쩔 수 없이 줄어든 것.
🚨 Microfeatures Architecture를 시도하면서 한 고민들
- AS IS:
- (NoticeListView - NoticeListViewModel) 이렇게 하나로 묶여있다고 치면,
- bookmark 기능은 NoticeListViewModel에서 구현되어 있음.
- 하지만 bookmark 기능이 SearchView, WebView 등에서 사용되고 있었고, 기존에는 .environment를 통해 최상단에서 주입해주고 있었으나, 다른 모듈로 분리해야 했어서 이걸 어떻게 분리할지 고민.
- 당시에는 App 모듈에는 NoticeModule NoticeListView만 public 으로 제공하고 있었는데 NoticeListViewModel 부분도 public으로 제공하여 최상단에서 주입받아서 사용할 수 있게끔 하면 문제를 해결할 수 있었을 것으로도 생각했으나, ViewModel은 변화가 잦기에 외부와 소통하는 별도의 인터페이스를 두어야하나 고민.
- 결과론적으로 Bookmark 기능을 별도의 모듈로 빼내고자 했으나, 또 하나의 문제가 Ribs처럼 View가 없는데 별도의 모듈이 맞나? SDK로 가야하는건 아닌지 고민,,
- 그렇다면 다른 모듈에서 들어온 SDK에서 이벤트를 전부 관리해서 뿌려주기까지 해야하는데, Combine의 PassthorughSubject 등으로 처리할 수 있겠으나, 그건 또 하나의 SDK 구조의 대공사가 필요했음.
- 여러번의 시도를 거쳤으나 결국 문제를 해결하고자 시도한 방법이 또 다른 문제를 야기하는 순환에 빠지게
(2023.07.22)
현재는 안드로이드의 Compose에서 MVI를 공식적으로 밀어준다고 하는데, 이렇게하면 안드랑 로직이 동일해지는 RIBs의 장점도 챙길 수 있을 것 같아서 고민중!
(2023.08.14)
TCA 1.0.0 나오면서 천천히 재조립 해보기로!
(2023.10.08)
(Tuist + TCA 1.0.0)의 구조에서 (SPM + TCA 1.0.0)의 구조를 택한 v2로 이전하여 새롭게 구조를 잡기로.
✅ 대체 학습
- MVI Pattern, MV State Pattern을 기반으로 데모 앱 만들어보기.
- MV State Pattern 기반의 네이버 검색 데모 앱 만들기 성공
- 가장 취할 수 있던 것은 UIKit에서 화면전환을 위해 사용했던 코디네이터패턴이나 RxFlow 방식이 SwiftUI에서는 무진장 어색했는데, 나름 실마리를 찾은 기분
- 3월: 학과 공지 개발 시작!
서버 개발자님이 설계가 아직 되지 않은 상황이었기에 어떤 것들이 필요한지 논의하고, API Request 및 Response 형태롤 합의한 후 그에 맞게끔 작업을 진행.
😼 특히 아직 나오지 않은 API 명세서를 기반으로 우선 구현한게 좋았음. 추후에 약간 달라지는 부분이 있긴했으나 빠르게 클리어
구현해야 하는 부분에서 공지 부분을 공통으로 사용하고자 함.
거의 비슷하지만 학과쪽에는 학과를 선택할 수 있는 DepartmentSelector와 새로운 학과를 추가할 수 있는 AddButton과 관련한 로직 몇가지를 더 들고 있으면 되었기에 닮게 사용할 수 있었음.
기존 NoticeViewModel에 departmentService를 만들어서 주입하여 구현하는 방법을 고안하였으나, SwiftUI에서 뭔가 예쁘게 맞아 떨어지는 느낌이 아니었어서 다른 방법을 시도.
NoticeViewModel과 별도로 DepartmentViewModel을 만들어서 별도로 구현하면 더 나을 것 같았음.
하지만 이건, 하나의 문제가 있었는데 bookmark 로직을 처리하는데 있었음.
bookmark를 기존에는 Combine을 적용한 것이 아니라 프로퍼티 형태로 업데이트를 시켜주고 있었는데, bookmark쪽에서 싱크가 맞지 않는 문제로 인해 작업이 너무 방대해지고 코드가 지저분해지기 시작함.
그리고 학과 개발에 있어서 디자인 패턴을 선택함에도 문제가 있었는데, NoticeViewModel에 이미 구현된 공지 load() 및 reload()의 알고리즘을 프로토타입 패턴으로 해결하려던게 지금 생각해보면 아쉬웠음.
-> 디자인 패턴에 너무 매몰되지 말자.
-> SwiftUI에서 디자인 패턴을 사용할 때 뭔가 고민이 많아지는듯.
✅ 아무쪼록 내가 생각한 최종적인 결론
BookmarkFeature
NoticeFeature
로 분리하여 특정 View에서 필요한 것들만 채택하여 사용하고, 전역에서 사용되는 것들만 Environment를 통해 주입하고자 함.
이렇게 개발할 경우 하나의 View에서 여러개의 StateObject로 된 Feature를 채택해 사용하여 TCA의 Scope를 나누는 것과 닮았다(?)고 느껴지기도 했음.
다만, SwiftUI의 메모리나 작동 원리에 대해서 아직도 부족함을 느끼고 있기 때문에 갑자기 발생하는 이슈에 잘 대처할 수 있을지가 고민.
🚨 최종적으로는 NoticeViewModel에서 Department의 일부를 같이 구현하는 방식으로 작업.
이유: 기존 프로젝트를 private에서 public으로 돌리는 준비를 하면서 TCA 기반으로 디자인이 완전히 바뀐 v2를 준비하고 있기에 구현에 집중했고, 특히 축제에서 사용한다고 했기에 회사일 그리고 개인적으로 하는 것들과 병행하기에 기간이 빠듯했음.
🌕 사용자가 받은 알림이 저장되지 않던 버그
사일런트 푸시 부분은 json field를 변경하는 것으로 해결이 되었으나, 사용자가 받은 알림이 앱 내의 알림 리스트에 저장되지 않고 있는 버그가 존재했음.
foreground 상태에서는 사용자가 알림을 받았을 때, 바로 local에 저장하는 로직이 작동하지만, background 상태에 있을 때는 사용자가 알림을 클릭했을 때, 그 해당 알림만을 저장하는 로직으로 작성되어 있었음.
이를 해결하고자 geDeliverdNotification을 통해 시도했고, useCase에 따라 테스트 코드까지 작성하여 개발을 진행했음.
기존 로직을 건들여야 하는 조금은 큰 공사였지만, 정말 치열했던 시간이었음.
해당 메소드의 문제는 사용자가 알림 센터에서 알림을 지워버릴경우 앱에 들어와서 어떤 알림을 받았는지 모른다는 것이었음.
따라서 서버에서 사용자가 받은 알림을 저장하지 않는 이상, 알림 정보를 온전히 보존하는게 불가능했음.
불가능하다고 판단하기 전에 어떻게 해결할 수 있을까를 엄청나게 고민했고, 결국은 근본적인 문제를 해결할 수 없다는 것에 도달하여 close되긴 했지만, 나름 값진 경험이었다고 생각됨.
🌕 학교 url의 변경
이게 바뀔줄은 솔직히 상상도 못했어서, baseURL을 서버 값이 아니라 앱에 하드코딩을 박아두었던 것이었는데,,, 결국 해당 공지의 url이 잘못되는 문제가 발생했음.
이건 핫픽스와 비슷하게 작업했고, 서버에서 fullUrl을 내려 받아서 하는 형태로 개선함.
✅ API V2로 변경
무엇보다도 요청 및 응답 구조가 달라지는 것도 존재했고, v2로 바꾸면서 하나하나 더 꼼꼼하게 체크했었음. 기존에 되던게 안되면 안되기 때문에.
기존 v1을 기반으로 한 코드에는 테스트코드가 작성되어 있었으나 v2 및 추가된 기능에 대한 부분은 테스트코드를 작성하지 않아 CI에서 잡지 못한 일부 오류가 존재했음. 특히, v1(요청만, 응답 없음)과 v2(요청 및 응답 있음)의 경우 api를 하나하나 대응하면서 실수가 발생하기도 했음.
✅ View구현
iOS 15를 기반으로 하다보니 16에서 지원해주는 것들이 사용되지 않아 bottomSheet등 presentDetent .medium이 필요했는데 구현이 끝난 후 present만 바꾸고자 봤는데,, 어 ...?
View의 계층이 TabView위에 올라와야 해서 해당 BottomSheet는 최상단에 위치해야 하는데 데이터를 전달해야 디자인 시스템처럼 처리하기엔 조금 지저분해진 느낌이 있었음.
🧞♂️ 후기
그래서 아무튼 회사일 그리고 개인 스케줄 속에서도 정말 즐겁게 잘 마무리 할 수 있었음.
최근에는 TCA를 열심히 학습하고 있었는데, WWDC23을 보고나서 SwiftUI TCA를 써야하나?라는 생각이 너무 강하게 들기 시작했음.
그냥 조각조각 잘 나누면 충분한데 말이지.
학과를 개발하면서
- RIBs 작은 형태의 데모 앱 학습 ✅ (사내에서 사용하기에 반드시 학습이 필요하다!! 아자아자!!)
- TCA 학습 ✅
- SwiftUI Animation Udemy ❌ (1강에서 멈춘 나..)
- Widget ✅ (사내 프로젝트니까 Live Activity까지 아자아자!!)
- 피그마 디자인 공부 ✅ (이건 취미인데 짱 재미있다!)
- 현재는 WWDC23 학습중.
최근에 새로운 디자이너님도 오시고 V2를 기획하고 있음.
아마 Tuist를 활용한 마이크로피처 그리고 TCA를 기반으로 작업이 리팩토링이 진행될 것 같은데, 무사히 마쳤으면 좋겠다.
개발적으로는 요정도를 성취했다!
- 블로그 유튜브 등등 하는데, 이걸 적지 않으면 다른 것들도 하지 않을 것 같아서 그럼 다음 스텝도 화이팅해보자!
'project > Kuring(공지알림)' 카테고리의 다른 글
[iOS] 학과 검색 알고리즘 개선 (초성검색) (1) | 2024.03.06 |
---|---|
[iOS] Spotlight (SearchAPI) (3) | 2023.10.11 |
[iOS] Debug Scheme 분리하기 (3) | 2023.02.03 |
[iOS] 내가 만든 SPM에서 No Such Module.. (0) | 2022.08.24 |
[iOS] SPM 프로젝트 이름 변경시 오류 (0) | 2022.08.24 |