iSIGN+ Pass v2 마이그레이션 스토리 (1편) : 어떤 아키텍처를 선택할까?

SSY·2024년 12월 31일
0

Architecture

목록 보기
9/11
post-thumbnail

1. 시작하며

안녕하세요! 저는 펜타시큐리티의 아이사인 개발부 IM팀에 재직중인 송상윤이라고 합니다. 이번에 작성할 포스팅은 펜타시큐리티의 대표 모바일 제품인 iSIGN+ Pass v2의 아키텍처를 성공적으로 끝마치기까지의 여정입니다. 현재 안드로이드 개발자로 재직중이시고, 회사 제품의 개선 및 개인의 성장까지, 2마리의 토끼를 잡으시려는 분에게 아주 유용할 수 있다고 감히 말씀드려봅니다!

이번 포스팅에선 iSIGN+ Pass v2가 어떤 아키텍처 패턴을 채택했고, 그 이유까지 알아볼까 합니다. 그럼 시작하겠습니다!

2. 모바일 제품의 개발 방향성?

iSIGN+ Pass v2의 최종 목표는 테스트 자동화이며, 그러기 위해 아래 사항들이 선행되어야만 합니다.

  1. OCP(개방 폐쇄 원칙)원칙을 지켜나감으로써 빠르게 변하는 요구사항에 불필요한 영향 최소화하기
  2. 각 모듈의 고유한 테스트 코드 작성을 위한 계층 분리
  3. 계층 분리를 위해 아키텍처 고려하기

따라서 결국, 어떤 아키텍처를 선택하느냐가 모바일 테스트 자동화를 완료시키는데 중요한 핵심 Key라고 판단이 되었습니다. 그렇다면 안드로이드 아키텍처에는 무엇이 있을까요?

안드로이드 아키텍처는 수많은 변천사를 거쳐왔습니다. 대표적으로 MVC -> MVP -> MVVM -> MVI 순으로 알고있죠. 그리고 각기 아키텍처 패턴들은 장단점을 보유하고 있는데요. 해당 아키텍처들이 어떤 특성이 있는지 개괄적으로 알아볼까 합니다. 또한 그 특성을 읽어내려다시다 보면, 선택의 이유 또한 납득이 되실거라 생각합니다 :)

1. MVC패턴?

UI에 해당하는 로직과 비즈니스 로직이 하나의 Activity or Fragment에 밀집되는 아키텍처 패턴입니다. 이는 UI관련 로직이 하나의 화면에 모두 밀집되기에, 추후 유지보수가 매우 불편해질 수 있다는 단점이 있습니다. 하지만 간단한 앱을 만들 경우는 생산성이 빠를 수 있다는 장점이 있죠. 하지만 요즘 구글 스토어를 보시면 아시겠지만 대부분의 앱의 규모가 크며, 해당 앱들은 MVC패턴으로 작성하기에 난해해 보입니다. 즉, MVC패턴으로 큰 규모의 앱을 만들기에는 추후 하나의 Activity or Fragment에서 UI로직과 비즈니스 로직을 처리해야하기에 큰 어려움이 있다는 것입니다.

하지만 MVC패턴이 꼭 Activity or Fragment에 비즈니스 로직까지 포함되지는 않는 경우가 있는데요. MVC패턴의 불편함으로 인해, 하나의 View에 하나의 Agent모듈을 1:1매핑하고 그 로직에 비즈니스 로직을 작성하는 경우도 있습니다. 그러기에 1 View : 1 Agent모듈을 매핑하는 패턴에 착안하여 나온 패턴이 MVP패턴입니다.

2. MVP패턴?

이는 1개의 View에 해당하는 로직과 1개의 비즈니스 로직 모듈인 Presenter를 매핑시킨 패턴입니다. 예를 들어, MainActivity가 있다면 MainPresenter가 매핑되는 방식이죠. 그 후, ViewPresenter는 상호 의존 관계를 맺게 됩니다.이 방식은 약 2019년도까지 많이 사용하는 패턴이기도 했습니다. 어쩌면 이 패턴이 1개의 View와 1개의 Presenter를 매핑하기에 좀 더 깔끔한 패턴이라고 볼 수 있습니다. 하지만 문제가 존재하죠.

앱의 규모가 커지면 어떤게 필요할까요? 바로 테스트입니다. 이러한 테스트를 잘 하기 위해선, ViewPresenter를 단방향으로 의존해야 합니다. 그러지 않을 경우, Presenter의 단위테스트를 진행할 때, View의 참조성으로 인해 해당 View를 호출해야만 하는 문제가 생기죠. 따라서 PresenterView의 의존성을 끊어내고자 하는 패턴이 생기게 됩니다. 이 패턴이 바로 MVVM패턴입니다.

3. MVVM패턴?

MVP패턴의 치명적인 문제점을 한 문장으로 요약하자면, '비즈니스 모듈이 UI에 의존성을 가진다'입니다. 따라서 MVVM패턴은 이 의존성을 끊어내고자 합니다.

MVVM패턴 또한 MVP패턴과 유사하게 1개의 View와 1개의 ViewModel을 배치시킵니다. 하지만 결정적인 차이가 있는데, ViewModel내에서 View를 변경시키고자 할 때, 명령형방식이 아닌 옵저빙방식으로 View를 갱신합니다. 즉, ViewViewModel의 스트림 객체를 참조 및 옵저빙하고, ViewModel이 변경사항을 발행하면, View에선 해당 값을 수신받아 View를 갱신하는 방식입니다.

이로 인해 ViewModel의 단위 테스트가 간편해집니다. ViewModelView를 일절 참조하지 않기에, ViewModel객체를 생성해 단위테스트를 진행하고자할 때, View객체를 알 필요가 없어지는 것이죠. 그러기에 대규모 앱을 만드는데 있어 현재까지도 MVVM패턴이 널리 쓰이기도 합니다. 하지만 또 다른 문제가 발생합니다.

앱이 커지게 되었을 경우, ViewModel의 메서드 또한 늘어나게 됩니다. 그러기에 네이밍 정의에 어려움과 혼재가 생기며 유지보수가 어려워지게 됩니다.

또한 android에선 선언형 UI인 Compose가 도입되었습니다. Compose는 UI의 State Holder Class를 보유하고 이 클래스의 변경사항을 옵저빙받아 UI가 자동 갱신되도록 하는 방식에 초점이 맞추어져 있습니다.

물론, MVVM패턴과 Compose를 조합하여 충분히 개발이 가능합니다. 하지만, 규모가 커짐에 따라, ViewModelState Holder Class를 보유해야하며, View로부터 사용자 이벤트를 받고, 그에 따른 작업 처리를 해주어야 합니다. 이때, 필요한 작업처리는 크게 2가지로 나눠질 수 있는데, State Holder Class를 갱신하여 UI를 변경하거나, State Holder CLass를 변경하지 않고 1회성으로만 UI를 변경하려는 경우가 생기게 됩니다.

즉, MVVM패턴의 경우, ViewModel에서 할 수 있는 2가지 행위가 구분지어지지 않음에 따른 코드 유지보수성의 문제가 대두됩니다. 이는 곧 UiEventUiSideEffect를 구분짓는단 뜻입니다.

4. MVI패턴?

MVI패턴은 MVVM패턴에 착안하여 만들어진 만큼, MVVM패턴과 유사성이 많습니다. 하지만 선언형 UI인 Compose가 떠오르고 그에 따른 State holder Class의 보유, UiEvent/UiSideEffect를 구분지어 처리하고자 하는 것이 MVI패턴만의 차별점 입니다. 또한 MVI 패턴으로 개발을 하다보면 ViewModel내, UiEvent/UiSideEffect를 분리해 처리하는 과정에서 비슷한 패턴이 반복되는 경우가 많은데요. 해당 패턴이 보일러 플레이트 코드라고 보여질 수 있습니다. 하지만 이 둘을 확실히 분리하고 의존도를 없앨 수 있다는 점이 큰 장점이라 채택이기도 합니다 :)

3. iSIGN+ Pass v2는 어디로?

iSIGN+ Pass v2는 MVI패턴으로 마이그레이션이 되었습니다. 즉, 선언형 UI인 Compose를 채택하였고,State holder Class의 관리를 보다 간편하게 하고자 합니다. 또한ViewModel내의 UiEvent/UiSideEffect를 구분지음으로써 UI갱신 방식을 적절히 분리하고자 하였습니다. 마지막으로 MVVM패턴의 장점인 테스트 용이성도 계승받아 편한 UI Test/Unit Test가 가능한 것이 해당 패턴을 채택한 큰 이유이기도 합니다.

4. 마치며

단순 사이드 프로젝트로 앱을 만든다면 MVC패턴으로 만들어도 됩니다. 정답은 없으니까요. 하지만 앱이 커질 것을 예상하고 그에 맞게 대비하는 것 또한 중요합니다. 아래 이미지처럼 미래 변경사항을 예상하고 적절한 모듈을 적절한 곳에 배치하는 것이 바로 아키텍처라 생각듭니다 :)

지금까지는 iSIGN+ Pass V2가 마이그레이션을 진행하며, 어떤 아키텍처 패턴을 채택했는지, 왜 해당 패턴을 채택했는지를 알아보았습니다.

다음 편도 기대해주시면 감사하겠습니다 :)

profile
불가능보다 가능함에 몰입할 수 있는 개발자가 되기 위해 노력합니다.

0개의 댓글