5개월간 iOS앱을 리팩터링하며 알게된점(MVC에서 MVVM으로)(3)

Youth·2024년 1월 5일
0

회고

목록 보기
3/10

해당 포스팅은
5개월간 iOS앱을 리팩터링하며 알게된점(SOPT회고)(1)
5개월간 iOS앱을 리팩터링하며 알게된점(더 나은 MVC로의 여정)(2)에서 이어지는 글입니다


MVVM구조

mvvm로의 리팩터링을 결정한 후 가장 먼저 진행된 이야기가 있었습니다

과연 어떤 방식의 MVVM을 도입할것인가?

viewModel이 로직과 model을 들고있는 형태라면 그 또한 mvvm이라고 말할 수 있기에 여러가지 템플릿과 방식중에서 한가지를 사용해야겠습니다

여러가지 레퍼런스를 참고하던 도중 ViewModel이 input구조체와 output구조체 그리고 input을 output으로 바꾸는 transform이라는 메서드를 가지고 있는 구조를 보게되었고 해당 구조는 viewModel의 추상화가 간결하고 명확해 보여서 확장이나 유지보수에 유리할것같은 느낌이 들었습니다

그렇게 해서 input output구조의 viewModel을 가진 MVVM으로의 리팩터링을 진행하게 되었습니다

Data Binding은 어떤 방식으로 진행할것인가?

사실 글로는 간단해 보이지만 viewModel의 구조를 input output구조로 선택하는것에 있어서 꽤나 많은 망설임이 있었던건 사실이었습니다

이때까지만해도 viewModel의 구조를 선택하는데 여러가지 선택지가 존재했습니다

  1. closure를 사용하거나 observer pattern을 사용해서 라이브러리도움 없이 viewModel구현하기
  2. 라이브러리를 사용해서 data binding이 있는 viewModel구현하기

지금부터는 제 개인적인 회고가 될것같긴한데 이야기를 풀어나가보겠습니다
아카데미시절 iOS에 대해 제대로 공부해본적이 없이 단순히 코더로서의 생활을 할때 가장 무서웠던것이 다른 팀원들이 제안해주는 새로운 프레임워크나 기술이었습니다

아카데미에가서 swiftUI로 개발을 시작하고 여름부터 UIkit을 공부하면서 처음에 버벅이고 아얘기존과 다른 방식의 기술을 배운다는게 무서웠었는데 시간은 없는데 배우고 당장 구현하면서 써야할 기술들이 너무 많았어서 그런가 새로운 기술의 도입과 학습은 마냥즐겁지만은 않은 상태로 아카데미 생활을 마무리했습니다

다행스럽게도 SOPT에서 좋은 사람들을 만나서 개발을 마주하면서 경험했던 무서운 감정들을 이제는 좀 당당하게 마주할수있는 상황이 되긴했지만 여전히 완전히 새로운 기술을 배우고 시간안에 구현해야한다는 약간의 불안함은 존재했습니다

그래서 저는 1번을 팀내에서 제안하는 사람이었습니다 rx나 combine도 좋지만 그전에 closure나 observer pattern으로 기초를 단단히 하고 다른 프레임워크를 배우는것도 좋아보기도 했으니까요

하지만 input output구조 자체는 stream이라는 패러다임 함수형프로그래밍 패러다임이 적용되어야하는 구조였고 만약에 해당 구조를 그대로 하겠다고 하면 rxswift나 combine을 배워야했습니다

운이좋게도 딱 그 시점에 WWDC스터디를 하던 친구가 SOPT내에서 combine스터디가 열린다는 말을 해줬고 그러면 이번기회에 제대로 combine을 공부해보자는 다짐을 하고 스터디에 들어가기로 하고 라이온하트는 inputoutput구조의 MVVM으로 리팩터링을 진행하기로 결정했습니다

[REFACTOR] 라이언하트 MVVM원칙과 이유

Combine스터디(open combine뜯어보기)

combine 스터디를 진행하기로 결정했을 때 저의 목표는 단 한가지였습니다
구글링해서 나오는 사용법을 익히는거 말고 진짜 하나하나 깊게 공부해보는 deep dive가 제 목표였습니다

스터디조원분들에게 8주동안 얕게 전부 훑기 보다는 조금 진도를 줄이더라도 하나하나 확실히 이해하는 방식의 스터디를 제안했고 흔쾌히 허락해주셔서 deep dive방식으로 스터디를 진행하게 되었습니다

왜 combine을 써야할까?

combine을 공부해야겠다고 다짐했을 때 왜 써야하는지에 대한 스스로의 명확한 정의는 내려야한다고 생각을 했습니다 그래서 OT인 1주차말고 2주차에서는 combine을 사용해야하는 이유에 대해 공부하고 공유했었습니다

[iOS]Combine은 왜 써야할까?

open source 뜯어보기

3주차부터는 아무래도 본격적으로 combine에 대한 공부를 해야하는데 목표는 deep dive지만 rxswift처럼 combine이 오픈소스가 아닌지라 단순히 원리를 공부할 수 있는 방법이 없었습니다

swift에서 combine을 알아보려고 하면 protocol의 추상화정도만 알 수 있었습니다 단순히 프로토콜만 보고 함수명을 보면서 알아가는 방법밖에 없었습니다

그나마 WWDC에 combine영상이 두개정도있는데 그걸보니까 combine의 구독관계가 어떻게 유지되는지, 몇개의 operator가 어떻게 동작하는지 정도는 deep dive가 가능했습니다

Rxswift랑 combine이랑 많이 비슷하다는 이야기를 많이들어서 실제로 combine을 공부하다가 구현이 궁금하면 rx를 보면된다라는 말을 자주들었었습니다

같은 역할을하는 객체의 구현부를 보면서 combine도 이런식으로 구현이 되어있겠네라고 생각한다는 이야기를 들어서 그렇게 공부를 하려고 했는데 그래도 뭐가없을가하고 github을 뒤적이다 swift로 combine을 구현해놓은 open combine이라는 오픈소스를 발견하게되었습니다

사실 처음에는 별기대안하고 들어갔다가 정말 combine에 구현되어있는 모든부분이 구현되어있다는 점과 star수를 보고 믿음이 가기 시작했습니다

이 레퍼런스를 스터디원들과 공유했고 정말 combine의 구현과 로직을 공부할수있게되어서 즐거웠습니다

combine을 쓸때 정말 자주쓰는 cancellable에 대한 코드 분석 아티클 2편
operator가 실제로 코드상에서는 어떻게 동작하는지에 관한 분석 아티클 1편
combine에서 비동기처리할때 flatmap을 쓰는 이유에 대한 분석 아티클 1편
combine에서 catch가 어떻게 stream을 관리해주는지에 대한 분석 아티클 2편

8주동안 하나에 공부부터 정리 포스팅까지 한 아티클당 8~9시간의 시간을 쏟아서 블로그에 올리고 팀원들과 함께 이야기하면서 수정 보완해나아갔습니다

물론 아직 combine스터디에서 공부한 내용중에 정리가 끝나지 않아 못올린 아티클도 많지만 정말 코드하나하나 bp를 몇십개씩 찍어가면서 데이터는 뭐가들어갔고 어떻게 바뀌었고 어떻게 전달하는지에 대한 분석을 열심히 했던것같습니다

8주의 시간이 지나고 여전히 모르는 부분은 많지만 코드를 쓰면서 stream의 흐름과 변경이 조금씩 눈에들어오기 시작했고 자연스레 combine을 활용한 라이온하트 리팩터링을 진행할 수 있게 되었습니다

open source를 뜯어본 후기

open combine을 전부이해했다고 그렇기에 combine을 이해한다고 당연히 말할수없지만 적어도 제가 아티클을 작성했던 몇가지 객체와 동작에 대해서는 코드와 동작로직을 이해하고 있다고 생각합니다

open combine은 정말 추상화가 많이 되어있어서 메서드 하나의 구현부를 보려고 하면 이렇게나 많은 객체중에서 하나를 꾸역꾸역 찾아가야했습니다
처음에는 코드를 쉽게쉽게 들어갈수없는 부분때문에 피로감이 좀 심했습니다

그래서 그런가 추상화를 프로젝트에 적용하고있지만 추상화가 그렇게 편해보이지 않기도했던것같습니다

하지만 combine을 제대로 공부하려면 목마른 사람이 우물파듯이 추상객체들을 넘어가며 코드를 분석해야했습니다

그렇게 시간이 지나고 이 글을 쓰기 얼마전에 새로운 프로젝트에서 팀원이랑 코드를 보면서 비효율적으로 반복되는 코드가 보이는 곳을 발견했었습니다

그러다 문득 그 비효율적인 해결방법을 떠올리다가 위 영상에서 봤던 추상객체를 이용한 의존성 주입을 하면 하나의 메서드에 공통 인터페이스를 채택한 객체를 넘겨주는 방식으로 코드를 바꾸면 어떨까라는 생각이 정말 문득 들었습니다

OCP를 활용한 로그인 로직 리팩터링

아마도 제가 2023년 1년동안 개발을 해오면서 가장 즐거웠던 한 순간이 아니었나 싶습니다, 처음으로 open combine을 뜯어본 8주의 시간이 하나도 헛되지 않았구나 나 조금은 성장했구나라는 생각이 드는 순간이었습니다

개발자로서 성장하기 위해서는 많은 코드를 보고 참고해야하는구나라는 개인적인 신념이 하나 더 생긴 날이었다고 생각합니다

예전에 야곰이라는 분의 비대면 세션을 한번 들어본적이있는데 그때 야곰님이 라이브러리 한번 뜯어보세요라고 제안을 해주신적이있었습니다

당시에 다른 라이브러리들 코드를 볼때 protocol을 사용해본적이없는 사람이었던지라 정말 말그대로 읽을수가없어서 바로 포기했던 경험이 있는데 적어도 그때의 나보다는 성장했구나라는 생각이 들었습니다

아무튼 딴길로 샌거같지만 open combine을 통해서 코드를 보는 눈이 약간은 넓어졌고 protocol지향 프로그래밍이라는 패러다임을 가지고 있는 swift에대해 약간은 좀더 알게되었던것 같습니다

추상화가 준 선물 unit test

이렇게 mvvm으로의 리팩터링을 무사히 마무리하고나니까 라이언하트리팩터링의 마지막관문 unit test를 공부해야하는 시간이 다가왔습니다

사실 unit test를 처음해보는 제입장에서는 딱히 unit test없이도 불편함이 느껴지지 않았었습니다 단순히 unit test는 꼭 필요하다라는 말을 여기저기서 주워들었을뿐 한번도 필요성을 체감했었던 적이 많이 없었습니다
(그리고 불편함도 이건 어쩔수없는거야 정도로 넘어가긴 했었던거같네요 ㅎㅎ...)

딱 한번 불편했던때가 있다면 아카데미에서 우주라이크라는 앱을 개발할때 초대코드를 받아서 방에들어가서 뭔가를하는 뷰를 개발했었는데 그때마다 팀원들한테 초대코드를주고 들어와달라고 해야했던 부분이 정말 불편하게 느껴졌었습니다

주말에 아무도 답장을 안해주면 개발을 더이상 할 수없는 상황이 불편하긴했었습니다... 그때는 앱특성상 어쩔수없다라고 생각했었네요

unit test를 하면 초대코드없이도 앱이 잘돌아가는지를 테스트할수있다라는 이야기를 듣고나서 정말 매력적인 방식이라고 생각을 했었습니다

unit test에 대한 공부를 하루정도 한 뒤에 가장 먼저 정한건 어디를 test할건가였습니다
그래서 총 세가지 부분에 unit test를 적용하기로 했습니다

1. API Unit test

이번 글은 unit test에 관한 설명을 하는글이 아니기에 API Unit test에 관한 글은 따로 링크를 남겨놓겠습니다

API를 test하는 방법

API test하기위해 mockURLSession을 만드는 순간 이걸 위해 추상화가 필요하구나라는 생각이 정말 많이 들었습니다

뭔가 unit test를 목표로 하지 않았을때는 왜 추상화를 했어요? 라고하면 단순 추상화의 장점을 이야기했던것같습니다

예를 들어서 결합도를 낮추기위해서? 결합도를 낮추면 특정객체가 특정객체에 영향을 주지않기때문에 문제가발생해도 그 문제가 다른객체에까지 영향을 미치지 않으니까 정도로 이야기를 했었는데 unit test를 하기위해서라는 답변이 하나 더 추가될것같은 느낌이 들었습니다

그만큼 unit test를 위해서는 추상화를 통한 결합도를 낮춰야 하는 사전 과정이 필요하다는 말이 피부에 와닿았던것같습니다

원하는 동작을 하는 객체를 만들어서 외부 주입을 해줌으로써 test를 가능하게 하는 메커니즘을 위해서 추상화를 해야하기에 이 또한 추상화 필요성이라고 이야기할 수 있을것같습니다

2. ViewModel Unit test

viewModel을 테스트할때는 viewmodel또한 의존성주입을 통해 결합도를 낮춰놔서 네트워킹레이어의 mock객체를 넣어서 어떤 action을 통한 요청이 들어왔을때 원하는 데이터가 잘 나오는지를 테스트했습니다

아무래도 viewmodel이 input output구조이다보니 stream을 확인해가면서 action이 stream으로 들어왔을때 올바른 값이 stream으로 잘 나가는지 혹은 operator를 통해 값이 잘 변환되는지를 확인했습니다

혹은 어떤 action이 들어왔을때 원하는 viewmodel의 메서드를 호출했는지여부를 확인하는 코드를 구성했습니다

3. ViewController Unit test

viewcontroller는 과연 unit test로도 충분한가?에 대한고민이 가장 많았습니다
xcode에는 UItest가 있기때문도 있었고 unit test를 공부할때 뱅크샐러드의 블로그를 가장 먼저보게되었는게 해당블로그에서 말하는 통합 UI테스팅이 정말 매력적으로 보이기도했던 탓도있었습니다

하지만 UItest를 하다보면 실제로 앱을 구동해야하고 실제로 API를 호출해야하기에 빠른 test가 불가능해서 이부분이 도입을 안하게된 가장 큰 이유가 되었습니다 실제로 API를 호출하게되면 그 비용도 무시할수없고 현재 앱은 테스트용 서버가 없어서 실제 릴리즈 서버를 사용해야하는 부분도 무시할수 없었습니다

API test를 할때도 실제 API를 호출하게되면 걸리는시간 비용등을 고려해서 실제 API를 호출하는 방식이 아닌 mock객체를 이용한 방식을 사용했는데 viewcontroller에서 api를 호출하는게 뭔가 앞뒤가 안맞는다고생각을 해서 viewcontroller에 대한 test도 진행을 하지만 ui가 잘그려졌는지 원하는 모양과 색깔인지보다는 데이터가 원하는 컴포넌트에 잘 들어갔는지

예를들어서 viewdidload가 호출되었을때 "안녕하세요"라는 string의 data가 viewcontroller의 titleLable의 text에 잘 들어갔는지를 확인하고 데이터가 tableview에 각 cell에 잘들어갔는지를 확인하는정도로 viewcontroller를 test하는 방식으로 진행했습니다

Unit test해보니 어떠셨나요?

막상 해보니까 정말 어려웠던것같습니다 그리고 우선 로직을 짜놓은 상태에서 어떤 상황이발생할거다 하고 테스트코드를짜다보니 어떤 상황이 발생할까?라는 생각을 정말 많이 했던것같습니다

실제로도 분명히 잘돌아가는 로직을 테스트하는데 머릿속에서 발생한 오류가 발생할수도 있는상황을 통과하지못해 로직을 디벨롭시키기도 했습니다

이래서 TDD라는걸 하는건가 싶긴했습니다
물론 TDD에 대해서는 회의적인 시각을 가진분도 좋아하시는분들도 많으시겠지만 적어도 테스트코드를 많이 짜다보면 어떤 문제가 발생할지에대한 예측이 가능하고 그부분을 고려한 로직을 짤수있게될것같다는 생각은 들었습니다 테스트코드를 짜다보면 어떤 기능개발을 맡게되었을때 여러상황에 대한 시뮬레이션이 가능한 능력이 조금씩 성장하겠다는 생각이 들었습니다 그리고 TDD를 만약에 하려면 이전에 테스트코드작성이 익숙해야한다는 말에도 공감을 하게되었습니다

그래서 저희팀내에서도 아얘 TDD를 하기에는 여러가지 어려운부분이 많아서 우선 테스트 코드를 작성하고 만약에 내가 생각한 방식대로 동작을 안한다고하면 그 상황에대한 테스트코드를작성하고 로직을 수정하고 테스트를 통과시키는 약식(?) TDD를 진행해보기로 했습니다


5개월간의 리팩터링을 마무리하며

정말 개발공부를 시작한 이후 가장 치열했던 5개월이었던것같습니다
매일매일 4~5시간동안 토론을하는건 일상다반사였고 배워야할것들이 계속 쏟아지는 상황에서 어쩔때는 그냥 남들이쓰니까~라고 타협하고 싶었던 시간도 꽤나 있었던거같네요 ㅎㅎ...

그럼에도 불구하고 5개월이라는 거의 반년에 가까운시간동안 매일매일 치열하게 고민하고 발전해나갔던 시간이 저한테는 너무나도 즐겁고 소중했던 시간이었던것같습니다

iOS개발을 하면서 처음으로 MVC에서는 MVVM으로 아키텍처를 변경해보는 과정에 참여해봤고 Unit test라는걸 해봤으며 처음으로 protocol을 활용해서 앱자체의 결합도를 낮추고 추상화하는 과정을 거쳐본 너무 의미있는 시간이었다고 생각합니다

32기 앱잼이 끝난지 5개월이 지난시점에서 왜 출시하지도 않을 앱을 리팩토링하냐라는 말을 되게 많이 들었는데 저는 그때마다 iOS개발을 시작한지 1년반이라는 시간동안 가장 성장을 많이한 5개월이었다고 너무 자신있게 말했었던 기억이 납니다

이 글을 sopt를 하시는 혹은 하고싶으신 분이 보게되신다면 앱잼을 하고나서 출시여부와 상관없이 더 좋은 코드를 만들기위해 리팩터링을 진행해보셨으면 좋겠다는 마음에 3편에 걸친 긴 포스팅을 기획하고 작성하게 되었습니다

사실 저는 앱잼을 할때 아이디어가 좋은 팀, 출시경험을 할수있을 팀을 선택해야한다고 생각했거든요

근데 저는 팀의 아이디어 출시유무 보다 정말 마음맞는 팀원들을 만나서 이렇게까지 성장했고 이렇게 긴 글을쓰면서까지 의미있는 시간을 보냈다고 말하면서 웃으며 마무리할수있었다고 생각합니다

사람을 보고 앱잼팀을 선택하셨으면 좋겠다는 말을 꼭 하고싶었고 이번 포스팅으로 5개월간의 프로젝트를 마무리해보도록 하겠습니다

그럼 20000!!

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

1개의 댓글

comment-user-thumbnail
2024년 1월 16일

리팩토링 과정에서 겪으신 근본적인 의문, 의사결정 과정까지 엿볼 수 있는 너무나도 좋은 글 감사합니다!

저도 배워가는 과정 속에서 MVC 패턴으로 프로젝트를 진행하며 massive한 VC에 대해 막연히 '이렇게 하면 안좋구나,,' 정도로만 생각하고 MVVM 패턴으로 리팩토링을 고민하고 있었는데, Youth님 글을 읽고 조금 더 근본적으로 차근차근 접근해야겠다는 생각이 드네요.

문제 설정 방식부터 해결해나가는 과정까지 좋은 인사이트 얻어가요!

답글 달기