깃헙에 코드는 올려두었다. (피드백 환영합니다! )
https://github.com/gustn3965/SaveMoneyFlutter
(혹시나 구조를 보고싶다면, /lib/CleanArchitecture/ 폴더를 보면된다)
잡담
드디어.... 출시했다. 거의 2년만에 출시한것 같다. 그렇다고 2년동안 기능을 엄청넣은게 아니다... 리팩토링의 연속이였다...
이앱은 사실 22년 11월부터 시작했다. 입사후 1년부터? 만들었던것 같다.
목적은 내가 쓰기에 편한 가계부앱을 만들기위해 시작했다.
그때는 Xcode - iOS로 만들고 정말 간단하게 만들었다. 나혼자만 쓰려고 앱 배포할 생각이 없었다.
23년6월쯤에는 Combine로 바꿔보기도 하고.. 그렇게 1년동안 열심히 나혼자 기입만 했다. ( 실제로 지금 데이터는 계속 마이그레이션 해서 22년12월 데이터부터 쫙 있다 )
23년 10월쯔음 Flutter라는걸 알게됐다. 그래서 이왕 만들어볼꺼, 안드로이드도 같이 출시해보고 싶다라는 생각이 들었다.
이때부터 출시라는 목적도 생겨났다. 이때부터 퇴근후에 틈틈이 찍먹하면서 플러터로 마이그레이션? 을 진행했다.
열심히 약 2달동안 달리다가.. 다시 시들시들해졌다. ㅋㅋ (커밋내역이 24년1월19일이 마지막)
24년 1월부터 2월까지는 굉장히 독서에 관심을 가졌던 때다.
이때 글을 봐도 기억에 남지 않는다라는 느낌을 받았고, 독해능력을 올려야겠다해서 유튜브도 찾아보고해서,
비문학 독해를 풀었다.ㅋㅋ 거의 매일 출근하기 2시간일찍가서 독해풀고 그랬다.
아래는 도움받았던 유튜브다.
https://youtu.be/pAJuqbNDGZ4?si=opPJXabxqqqMA5FY
24년3~4월에 클린아키텍쳐를 배우고, 클린아키텍쳐 기반해서 앱을 다시 리팩토링을 하고싶다라는 생각이 솓구쳤다.
그렇게.. 24년 4월부터 다시 달린다. 클린아키텍쳐 기반으로 리팩토링!!
호기롭게 브렌치도 Refactoring/CleanArchitecture 도 만들고 다시 새로 만들기 시작했다. ㅋㅋ
ㅋㅋㅋㅋ 24년 5월부터 잠잠했다. 왜냐.. 독립 이슈가 있었다. (변명일지도)
4월29일날 수내역으로 이사왔고, 20년넘은 오래된 오피스텔이라, 청소도 하고, 화장실 줄눈도 해보고... 암튼 처음 독립하는거라 집을 굉장히 깨끗하게 만들어야겠다라는 욕심 + 집꾸미기로 1달동안 못했다.
그렇게.. 6월에 정체되어있는 내 자신을 보고 다시 달려야겠다는 생각이 들었다.
6월부터는 오늘까지 정말 퇴근하고, 주말에도 계속 달렸다.
실제로 이제는 master브렌치에 머지까지 하는 자신감?
암튼... 기간만 보면 장장 2년걸린 내 가계부앱.. 7월 4일에 iOS앱 출시했다. 앱스토어 링크
(드디어 올해 목표중하나를 이루었다)
지금은 구글플레이스토어에도 출시하려고 준비중이다.
이 프로젝트의 목적
처음에는 내가 사용하고싶은 가계부를 만드는게 가장 컸고,
이후로 플러터로 개발해보고 싶었던게 컸고.
이후에는 리팩토링이 가장컸다.
내가 공부한 클린아키텍처를 기반으로 재해석하여 리팩토링을 해보고 싶었다.
앱출시목적은 열심히 플러터로 만들었는데, 기왕 안드로이드도 출시해봐야지. 해서 출시중인거고..
그래서 이 앱이 막 엄청 쓰이길 바라는건 아니다. ( 초창기에 todoList 앱을 만든적에는 많은 사람들이 제발 써줘.... 라는 느낌이 강했다ㅋㅋ)
하지만 사용자들이 원하는 기능을 건의사항에 넣는다면 열심히 만들것이다.
이번 출시하면서도 계속 이기능만..이기능만...이것도... 추가해야하지하면서 미뤄진것도 있다.
이러다간 출시못할것 같아서, 백로그에 남겨둔 기능들도 있어서, 안드로이드 출시하고 나면 천천히 만들어보려고한다.
앱 기능
간단한 가계부다.
근데 나는 큰 범주별로 나눠서 기입하고 싶었다. 이게 실제로는 현금바인더, 현금챌린지...등등 라고 부르더라.
간단히 말해, 월급통장을 쪼개는것.
나는 데이트비용, 개인비용, 자동차비용 이렇게 3개로 쪼갰다.
앱에서도 이렇게 나누어서 소비를 기입할 수 있게 했다.
캘린더에서도 금액이 표시되면 편할것 같아서 캘린더도 추가했다.
지금까지 소비해온것을 차트로도 볼수있도록 만들었다.
간단한 검색기능도 넣었다.
공지사항이랑 문의사항도 만들어놨다. 돈을 쓸 여유가없으므로, 최대한 무료로 어떻게할까 하다가,
공지사항은 깃헙페이지의 호스팅으로. ( 이방법을 왜 이제 알았을까 하는 + 이게돼?라는 흥분감 )
문의사항은 깃헙페이지&구글폼으로 받도록 했다. ( gpt의 아이디어였다. ㄷㄷㄷ )
리팩토링 후기
사실상, 클린아키텍처 기반으로 리팩토링한 기간은 2달정도 걸린것 같다. ( 물론 퇴근후&주말이라 시간으로 따지면 몇주안될듯.. )
앱 구조는 깃헙 README에 작성해놨다. 내가 재해석한 클린아키텍처 기반이긴 하지만, 약간 Ribs를 닮은것 같기도.
단점
( 단점부터 적어야겠다. ㅎ )
boilerPlate code ⬆️
굉~~~~장히 귀찮다. 하나의 뷰를 만들기위해서 Coordinator, View, ViewModel을 만들어내야한다. (내가 그렇게 정했다.)
( 이것도 약간 시행착오가 있었다. 그 경계선이 애매할때가 있었어서, 몇개는 Coordinator가 없거나.. 그렇다. 처음부터 완벽할수는 없으니까.. )
혼잣말로 아우 귀찮아!!! 를 10번넘게 말했던것 같다.
간단한 화면은 빠르면 30분안에 작성하는 것 같다.
사실 이거밖에 없는것 같다 ?
단점이라고 하기에도 보기 어렵다라는 생각이 들기도 하는데, 왜냐하면 지금은 귀찮겠지만, 장기적으로 봤을때, 추후에 기능을 추가한다면 그에 걸쳐있는 코드들을 살펴보는 코스트가 점점 줄어들것이라고 생각이 들기 때문이다. 밑에 장점을 써놓았다.
굳이 굳이 굳이 단점을 또 적어보자면...
고민하는 시간 ?
단점을 한개적기에는 뭐해서, 굳이 꼽아봤다. 당장 기능을 넣고싶은데, 현재 구조에 배제되는 행동을 하기는 싫다. 근데 기존구조에 없던 구조일때, 고민해야한다. 어떻게 기존구조와도 비슷한 맥락이면서 넣어줄수있을까..
하단 바텀 탭도 그러하다. 초기에는 바텀탭이 없었다. 지금은 홈 / 차트 / 검색 / 설정 이 생겼다.
그래서 이걸 어떻게 Coordinator와 연결을 잘 시킬지.. 에대한 고민을 꽤했다.
또는 현재 화면이 전혀 다른 화면에도 영향을 주게 하려면 어떻게 해야할지.. .등등
생각해보면 프로젝트 성격에 따라 맞는 구조가 어떤건지... 약간의 느낌이 온것 같기도하다.
프로젝트 대비 너무 거창한 아키텍처를 도입하는건 득보다 실이 많은것 같기도하다.
( 하지만 난 아직 거창한 아키텍처를 모른다 )
장점
사이드가 줄어든다.
사이드가 굉장히 줄어드는 것 같다. 정말 처음에 가계부를 Swift로 구현했을때, 뭐하나 고치려고 하면 굉장히 사이드를 고려해야했다.
flutter로 처음에 만들때도 ViewModel을 이상하게 만들어버리느라, 이것도 점점 기능을 추가하려고하면 사이드를 고려해야했다.
사이드가 많다는것은 그만큼 의존성도 많았기때문이다.
하지만 지금 적용된 구조로 만들다보니, 의존성이 거의 없을뿐더러, 그로인해 기능을 추가할때도 다른 사이드를 고려하지않아도 되는 케이스가 많았다.
사이드가 줄어드니, 기능추가하기도 너무 편했다. 뷰는 뷰모델만 의존하고있어서, 다른 Coordinator, UseCase등을 신경쓸 필요가 전혀없었다. ViewModel도 UseCase에만 의존하고 있고..
또 최대한 Entity를 View가 모르도록 했다. (초기에는 쪼금 썼다.. ㅋㅋ ) View는 ViewModel이 만들어낸 모델을 보도록 했다.
DataBase의 테이블도 Entity와 다르게 따로 만들었다.
UseCase에서 DataBase의 테이블을 Entity로 변환하는 작업이 귀찮긴했지만, 확실히 사이드가 적어지고 경계선도 분명해져서 편했다.
coordinator 의 장점 ?
뷰 전환 관리를 Coordinator가 관리하도록 하니, 확실히 편하다.
View를 알필요가 없고, ViewModel만 알고있으면되고, 다른 화면이 필요하다면 Coordinator를 호출하면 그만이다.
구조의 고민
ViewModel의 구현체 고민..
ViewModel까지 protocol (Dart언어에서는 abstract class 추상클래스로 표현) 로 만들었는데, 계속 만들어낼수록 구현체가 더이상 추가될것 같지 않다라는 느낌을 받았다. ViewModel에서 실제로 핵심 앱기능을 담당하는 로직인 UseCase를 가지고 있어서, UseCase들이 구현체가 여러개일수 있는거라, ViewModel까지 구현체가 여러개일 필요는 없어보였다.
의존성이 없다 vs 의존성 있다라는 고민...
늘 고민하는것 같다. 예를들어 소비를 추가하는 화면과 소비를 수정하는 화면이 같은 코드를 써야할까?
나는 다른 코드를 사용했다.
장단점이 있는것 같다.
거의 비슷한 로직, 비슷한 뷰인데 다 따로만들기 시작한다면, 추후에 기능을 추가하게될때, 오히려 챙기지 못하는 사이드가 생길 수 있다.
반대로 비슷한 로직, 비슷한 뷰여서 하나로 퉁쳐버리면, 추후에 기능을 추가할때, 고려해야할게 많아진다. 점점 쌓이다보면 부담이 커질수밖에 없다. 오히려 크래쉬도 생길수있다.
동기랑도 이런얘기를 했었는데, 할수록 나는 전자가 낫다라는게 확신이들었다.
왜냐하면 크래쉬를 안고가는 단점보다, 챙기지못한 사이드가 훨씬 리스크가 적다.
또한 기능추가를 위해서 코드를 살펴보는 코스트가 전자가 더 적다라고 생각이든다. 후자는 여러 케이스들을 고려하면서 코드를 잘...짜넣어야하는데, 전자는 그것보다는 훨씬 부담이 적다.
어떤 화면이 알지못하는 다른 화면에 업데이트 유도하고싶을때..
고민하다가.. coordinator가 트리 구조이니, 바텀업으로 구현했다.
Widget -> ViewModel -> Coordinator -> Parent Coordinator -> child coordinator -> ViewModel -> Widget
이렇게 업데이트하도록 헀다.
기본골자는 ViewModel을 통해서Widget을 업데이트 하도록 했다. ( Widget = View )
Widget은 말그대로 data를 보여주는거에만 충실하도록 했다.
의존성 주입 방법..
결국 고민하다가, 처음부터 메모리에 있게하는 독자적인 DIContainer를 만들었다.
그래서 Coordinator는 DIContainer에게 요청하여, 필요한 ViewModel, Widget을 주입받도록 했다.
DIContainer는 ViewModel에 필요한 UseCase들의 구현체들을 주입해준다.
이는 빌드환경에 따라 구현체를 따로 주입해주도록 했다. 크게 mock, cbt, real로 구분했다.
나는 여기서 SQLite를 사용하는데, 실제 db에 접근해서 가져오는 UseCase와 Mock 데이터만 사용하는 UseCase.
때로는 real에는 필요하지 않는 Widget이라던가..
이번 게시글은 이렇게만 마무리하려고 한다!
다음에는 Flutter랑 앱 배포하면서 경험했던걸 작성해보려한다!
- https://vapor3965.tistory.com/119
'Flutter' 카테고리의 다른 글
Flutter 앱 배포 후기 2 프로젝트편.. (0) | 2024.07.06 |
---|