기능 단위로 확장 가능한 샘플 앱 만들기

SeongHo Hong
7 min readFeb 16, 2022

--

안녕하세요👋 센드버드에서 iOS 개발자로 일하고 있는 홍성호 입니다. 지난번 회고했던 온보딩 과정을 마치고 평화로운 나날을 보낼거라 생각했지만… 새로운 버전의 Chat Sample App을 설계하고 개발하는 작업을 하게 되었습니다. 아직 완성된 것은 아니라서 앞으로도 변경될 가능성이 있지만, 생각정리 & 경험 공유 차원에서 지금까지 고민했던 것들을 풀어보겠습니다!

기존 히스토리 분석

기존에도 Chat Sample App이 있었는데, 특별히 앱 구조를 설계해서 작업한 것은 아니어서 Business Logic과 View 코드가 ViewController에 함께 담겨 있었습니다🥲 이렇게 되면 SDK 코드를 파악하는데 어려움이 있는데요. 그래서 또 다른 product인 Calls Sample App에서는 하위 기능 단위로 샘플 앱 자체를 나눠서 이런 복잡성을 다루고 있습니다.

examples-calls-ios — 기능 단위로 샘플 앱이 만들어져 있습니다

오 그러면 Calls Sample 구조를 따라 가면 되겠네요! 해결!!! 이길 바랬지만ㅎ 이 구조 대로 라면 기능이 추가될 때 마다 필요한 코드를 모두 복제해서 사용해야 합니다. 추후에 SDK 버전 변경이 있거나 공통 코드를 수정할 때 유지 보수 비용이 많이 들게 되는것이죠.

요구 사항을 간단히 정리해보면 기능 단위로 앱을 나누기, 공통 로직을 관리할 수 있는 구조로 만들기가 되겠습니다. 추가로 개발자들이 알아보기 쉬운 구조로 가면 좋겠다는 의견도 있었습니다🤔

MVVM 구조 — View, ViewModel 추상화

그럼 공통 기능을 Common framework에 넣어두면 되겠다는 생각까지는 했습니다. 그런데 세부 기능을 어떻게 추가할 것인가 하는 고민이 들었어요. MVC가 아닌 MVVM 구조로 해도 대다수의 개발자들이 잘 이해할 수 있을 것 같다는 의견을 반영해서, MVVM 구조로 시도해봤습니다.

~BaseViewController, ~BaseViewModel에 기본 기능들을 구현해둡니다. 그리고 Feature Sample App에서는 BaseViewController와 BaseViewModel을 상속 받아서 구현을 하는 시나리오 입니다.

BaseVC, BaseVM 에서 상속 받는 구조

그런데 이렇게 하면 샘플 앱을 통해서 학습하려는 사람은 부모 Class인 BaseViewController, BaseViewModel를 제대로 이해해야만 사용할 수 있게 됩니다. 그리고 부모가되는 BaseViewController는 다양한 View를 추가/변경할 수 있는 구조로 만들어야 하는 어려움이 생깁니다. 여러 피드백을 바탕으로 현실적으로 다양한 View와 기능을 수용가능하게 만드는 것은 어렵다는 생각을 했습니다.

추상화 하지 않기?

기능 단위로 자율성이 있는 앱을 만들면서 유지보수도 하기 좋게 만드는게 쉽지 않은 일 같아요. 그리고 추상화를 하면 초보 개발자들이 코드를 이해하기 어렵다는 단점도 있습니다. 그래서 추상화를 하지 않는 방안에 대해서도 피드백이 나왔는데요🧐

MVVM 개념을 이해하지 못하면 코드를 이해하기 어렵겠지만, 그렇다고해서 비즈니스 로직을 ViewController에 모두 담아버리면 기존 샘플 앱과 차이점이 없다고 생각했어요. 그리고 추상화를 안하면 변경이 발생했을 때 어떤 일이 벌어질지 생각해봤습니다.

변경이 발생했을 때 일어나는 일

올해 안에 SDK 메이저 버전업이 있을 예정이라서 변경에 대한 상상을 반드시 해야 했습니다. 손으로 그리면서 생각했었는데 도형으로 옮겨보면 다음과 같은 모습이 됩니다. SDK 변경이 발생하면 모든 개별 앱들에서도 변경이 일어나는 구조가 됩니다.

이 문제를 해결하기 위해서는 SDK를 감싸는 Wrapper를 두면 될 것 같습니다. 그러면 SDK 가 변경되어도 Wrapper 안에서만 변경이 일어나게 됩니다.

이렇게 모든 문제가 해결된 것 처럼 보이지만 SDK의 인터페이스들을 하나하나 wrapping 하는게 번거로운 작업이기도 하고, 샘플 앱을 학습하는 사람이 인터페이스 하나하나 wrapping을 거쳐서 확인해야 한다는 번거로움이 생겼습니다.

하나하나 wrapping 하기엔 수많은 인터페이스들…

UseCase

SDK 인터페이스를 하나하나 Wrapping 하지 않고 변경을 관리하는 방법에 대해 고민하다가 UseCase를 만들어서 그 안에 하나의 개념으로 묶을 수 있는 로직들을 넣어두기로 했습니다. MVVM 같은 View와 관련된 패턴을 사용하지 않고 ViewController가 바로 UseCase를 사용하도록 만들어봤습니다.

ViewController에 대한 재사용은 어느정도 포기하게 되었지만, SDK에 대한 변경을 어느정도 감당하면서 비슷한 로직을 묶어두는 객체가 생겼습니다. 전체적인 구조를 보면 다음과 같은 형태입니다!

기본적인 로직을 구현해둔 UseCase들이 존재하고, 그것을 기반으로 확장할 때는 위임 또는 상속을 통해서 추가 기능을 구현할 수 있습니다. (FeatureUseCaseA 예시)

기본적인 로직을 활용할 필요 없고 세부 feature에만 해당하는 것은 FeatureUseCaseB 처럼 사용됩니다. 앞서 SDKWrapper를 생략하면서 FeatureUseCaseB 가 SDK에 직접적으로 의존성을 만들어 내고 있는 부분이 아쉽긴 한데요. 학습의 편의성을 위해서 여기서 발생하는 의존성은 허용하기로 했습니다. 물론 추후 논의를 통해서 개선할 수도 있을 것 같습니다.

마무리

구조를 설계하면서 여전히 어떤 방법이 최고라는 확신은 없는데요. 게다가 Feature 별로 하위 앱들을 만드는 구조는 처음 해봐서 플랫폼 간에 논의도 많이하고 서툰 부분들이 많았던 것 같습니다. 글 읽으면서 더 나은 방법이 떠오른다면 피드백 주시면 감사하겠습니다. 제발 이 프로젝트를 기반으로 좋은 샘플들이 만들어지길 바랍니다🙏

재미도 있고 고통도 있던 이 경험을 통해서 좀 더 나은 개발자가 되면 좋겠습니다. 위로가 될지 모르겠지만 함께 머리 싸메고 고생한 Elliot과 Liam 에게도 고마움을 전합니다 😇

그나저나 저희 회사에서 채용을 하고 있던데…! 저랑 함께 다양한 개발 경험 사실 분들 찾습니다!! 솔직히 저도 아직 회사를 잘 모르지만ㅋㅋ 좋은 개발 경험 만들어갈 수 있도록 노력 중이에요. 조금이라도 궁금하시다면 아래 링크 보시거나 저한테 연락주세요!

--

--

SeongHo Hong

Software Engineer 🧑‍💻https://github.com/cozzin