Skip to content

[Clone/Instagram] ⭐전체적인 MVVM Architecture Pattern 리펙터링 with combine. | 마주한 에러, 문제 & 새롭게 알게 된 개념 & 개선해야 할 기능 & 느낀점 #12 #26

@SHcommit

Description

@SHcommit

TODO : adopt MVVM with combine

이번에 해볼 것은 Combine을 통해 MVVM을 분리 with DI. 안전하게 수행하기 위해 adoptCombineWithMVVMBranch 이 브랜치에서 리펙토링을 진행하고 있다.

클린코드, 가독성, DI 를 고려한 코드를 짜도록 고민을 많이 하면서 리펙터링을 했다.

새로 구현한 기능, 기능 개선

  • 회원가입 기능 Combine, MVVM 적용

RegistrationViewModel RegistrationController commit log etc..

  • 로그인 기능 Combine, MVVM 적용

LoginController, LoginViewModel commit log etc...

  • 프로필 관련 Combine, MVVM 적용

ProfileController, ProfileHeader, ProfileHeaderViewMdoel commit log etc...

SearchViewModelProtocols

SearchViewModel

로그인 기능 리펙터링 중 마주한 문제 1

TextField.publiser(for:)를 사용했으나 텍스트 영역을 벗어나야만 publish 되는 경우

로그인 구현 에러 1

email, password textfield가 적어도 한개씩 입력 되어야 로그인 버튼을 작동할 수 있는 기능을 컴바인을 통해 리펙터링 하고 있었다. 위 사진과 같이 구현했다. viewModel.passwd의 경우 @published로 선언했다. checkIsValidTextFields(withLogin:) 을 통해 사용자가 입력하는 실시간 타이핑에 대해 반응 하려고 위와 같은 코드를 작성했다.

TextFiled publisher는 선언이 됬다. Combine은 아직까진 UIKit에 대한 UIControl의 기능들을 추가 해주지 않았기에 TextField 포커싱에서 벗어나야만 텍스트가 변했다는 published를 해준다.

사용자가 타이핑하는 한 글자마다 publish를 원했기에 publisher(for:) 사용은 이번 구현 목표와 맞지 않았다. 따라서 NoticifationCenter를 사용해서 기본적으로 지원되는 notification을 이용했다.

로그인 구현 에러 수정1

여러번 작성될 것 같아서 함수로 만들었다 :)

참고한 글

검색 기능 리펙터링 중 마주한 잠재적 에러

에러3

말 그대로 잠재적 에러다.. 어떤 특정 문자열을 입력할 경우에만 항상 범위를 벗어난다고했다. 심지어 그 단어가 한개 일 지라도 잘 필터링 됬는데 에러가 발생했다. 예전에는 왜 그런지 원인을 계속 찾지 못해서 컴파일러 기분따라 그러는가보다 하고 넘어갔다가 오늘에서야 이상함을 느꼈다. 컴바인을 사용했음에도 (물론 로직은 동일) 에러가 나서 계속해서 원인을 탐색하다가 드디어 이유를 알게 되었다....

에러 3 -1

파이어베이스에서 이미지를 받아올 때 엄청 소량의 데이터임에도 불구하고 몇십번만 fetch하면 바로 돈 내라고해서 그덕에 개발을 못한 경험이 3번(3일정도) 있었다. 그래서 searchController의 tableView(numberOfRowsInSection:) 목록 크기를 대폭 낮춰서 Constant인 3으로 해버려서 이런 문제가 발생했던 것이다.

  • filteredUsers(검색에의해 필터링 된 데이터들 ) PassthroughSubject 타입으로 초기에 설정했다가 @published타입으로 바꿨는데 이때 관련 로직을 깜빡하고 수정하지 않아서 이상한 indexPath.row가 나왔던 것,,,

또 하나 발견한 에러는 string.contains()는 대소문자를 구별한다. 근데 나의 경우 사용자의 첫 글자를 대문자로 입력하도록 설정해서 검색이 잘 안됬다. 그래서 데이터를 filter할 때 lowercase()를 사용함으로 해결했다.


개념 정리

컴바인을 공부하면서 Notification을 공부했었지만 이참에 간략하게 다시 정리한다.

Notificaiton

Swift에서 notification은 애플리케이션 내에서 "알림을 방송"하는 역할을 갖는다. 그렇다면 방송을 하기 위해 어디로 송신, 어느 체널로 주파수를 맞춰야 방송을 들을 수 있는지 정해야 한다. Notification을 전달하는 중심은 NotificationCenter이다.

NotificationCenter

등록된 observer들에게 정보의 BroadCast를 가능하게 하는 Notification Dispatch 매커니즘 통해 동작.

DispatchQueue, NotificationCenter등을 통해 직접 접근할 수 없는 system 기능을 이용할 수 있도록 도와주는 것이다. NotificationCenter를 통해 notification을 하기 위해서는 observer(or subscriber), publisher가 있어야 한다. 즉 publisher가 published한 notification은 NotificationCenter를 통해 subscriber, observer등에게로 전달된다. .default를 통해 하나의 객체로 관리할 수 있도록 만들었는데 싱글톤의 개념과 달리 커스텀이 가능하다고 한다!! ( 방학때 더 공부해 봐야겠다..)

그리고 UIControl 관련 기능을 아직.. 지원하지 않지만 NotificationCenter.default.publisher(for:object:) default로 정의된 UIControl관련 notification기능을 publish 할 수 있는 publisher를 사용할 수 있다 :) CombineUtils.setupBindings()을 통해 사용할 수 있도록 했다.

왜 이런 코드를 짰을까?

고찰

SearchController에서 검색에 의한 tableView cell 업데이트 관련 코드

updateSearchResults(for:) -> searchResult.send(text) -> viewModel.transform -> searchResult subscriber 실행 -> 데이터 필터링 후 테이블 리로드 .

이제 cell 업데이트가 시작된다.

원래 내 계획은 viewModel의 searchResult에서 필터링 과정 후 그 결과를 PassthroughSubject<[userInfoModel],Never> 타입의 filteredUsers(필터링 된 특정 데이터 배열)에 send하려고했다. 근데 filteredUsers를 PassthroughSubject타입으로 하자니 필터링 된 전체 값을 반환할 때 sink를 통해 반환해야하는데 이때 또 다른 변수를 선언해야 했기 때문에 PassthroughSubject가 아닌 @published로 타입을 바꾸었다. 이때 @published에 맞게 바꿔야했었는데 깜빡하고 오류가 난 부분만 수정해서 wrong code를 바탕으로 계속해서 코드를 구현해갔다. 그래서 sink가 필요하지 않아도 계속해서 publised된 값을 받아서 indexPath.row가 filteredUsers의 범위를 초과했다는 오류를 계속 발생시킨 것이다. 코드를 더 깔끔하게 분산시켜야겠다.. 코드 짜는 내내 이리갔다 저리갔다 해서 잘못된 로직을 파악하지 못 했던 것 같다.

추가, 개선 해야할 기능

  • 검색하기 위해 서치바를 클릭할 때 tableView reloadData가 실행된다. 이것을 없애야한다.

  • ProfileVC 관련 tableView delegate들 input/output패턴으로 VM으로 리펙터링 해야 한다.

  • Login 관련 기능

추후 비밀번호 최소 입력 개수를 제한하고 알림창을 띄우는 기능을 추가할 것이다. 또한 토스의 회원가입 절차처럼 만들어 볼 것이다.

고민거리..

약간 고민인게 struct타입의 모델에 @published, PassthroughSubject 같은 publisher 타입의 변수를 선언해도 되는지 했갈린다.... 데이터를 저장 + 흐름까지 갖게 되어서 선언하면 편한데.. 본질이 망가지는게 아닌지 고민스럽다.. 결론은 그냥 Model선언을 안하면 되는데.. 그래서 안 했다. (Model의 영향이 많이 줄었네..)

근데 해도 될 것 같다.

새로 리펙터링 해야할 것

  • Feed VC 관련
  • Upload VC 관련

SearchController 를 SearchViewModel과 분리할 때 SearchViewModelProtocols 요기에서 이 두 함수를 꼭 UserViewModel로 분리해야한다. SearchViewModel에 의존적이다.

개선해야핳

시험이 7일 남았다.. 큰일났다.. 학교 공부보다 스위프트를 할 때 시간이 빨리간다.

// 여담..

컴바인 공부만 3주? 한달 째 되가는데 한달 전에 주문한 책이 드디어 도착했다. 기말고사 빨리 끝나면 좋겠다. ˘ᗜ˘

tetestt

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions