각 핵심서비스를 이어주는 Routing Part.1

iOS 실무 프로젝트 진행 시, 매번 내비게이션은 항상 높은 도전이었습니다.
이번 시간에는 Routing과 관련하여 어떠한 이슈가 있었는지 그것을 어떻게 아이디어를 풀었는지에 관해 설명해보고자 합니다.

Swift의 Routing의 한계

라우팅에 대한 정의는 저마다 도메인에 대해 정의가 서로가 약간 다릅니다만, 앱에서는 다음과 같은 정의를 내릴 수 있습니다.
앱 내/외부에서 다음 페이지를 푸시, 모달 또는 새로운 화면으로 대체하는 액션이라고 정의할 수 있겠습니다.

다음은 앱에서 라우팅 액션과 관련한 어떠한 것들이 있는지 간략히 정리해보았습니다.

  • Universal & Deep Link, 기타 서드파티 링크들 (Firebase Dynamic Link, Appsflyer 등등)
  • APNS 수신 시, Payload 처리
  • 인웹(WebView)에서 네이티브 페이지 호출링크
  • 현재 페이지에서 모달, 푸시 호출

처음 앱을 만들 때는 페이지도 적고 내비게이션이 복잡하지 않고 단순하여 시간을 조금 들인다면 처리가 가능하지만, 규모가 큰 서비스라면 어떨까요? 위의 기능이 추가할 때마다 점점 복잡해지고 테스팅할 요소들 또한 많아지게 됩니다.

다양한 실험적인 서비스를 더욱 기민하게 서포트하기 위해 개발자분들 모두 부단히 노력하지만, 라우팅 때문에 발목을 잡히는 경험을 종종 겪었습니다.

제가 여기서 언급한 다양한 실험적인 서비스는 다음과 같습니다.

A, B 페이지를 추가한다. 지표측정과 A/B 테스트를 진행 결과는 예상보다 사용자들의 반응이 좋았다. 따라서 C 페이지를 추가한다. 하지만 C 페이지가 사용자들에게 반응이 안 좋기 때문에 A, B 페이지를 걷어내고 D, E 페이지를 추가한다. 그 후 3개월 정도 지표측정 결과에 따라서 신규페이지가 들어갈 것인지, 기존페이지가 들어갈 것인지, 결과 추이를 보고 결정이 된다.

저희 팀에서는 이러한 내비게이션 처리가 프로젝트 막바지에 되는 것이 아닌, 애초에 아키텍처 설계부터 녹여내면 새로운 피처들을 연동시키는 데보다 유연하게 대처할 수 있지 않을까? 하게 되어 같이 고민하게 되었습니다.

Routing를 보다 기민하고 유연하게 대체하는 방법은 없을까?

위의 제목을 주제로 팀원들과 다양한 아이디어도 내보고, 다른 회사 서비스들은 어떻게 할까? 하면서 많이 고민하였습니다. 고민을 하다 보니 다음과 같은 결과를 도출하게 되었습니다.

딥링크, 인웹링크, 심지어 네이티브  모두 그냥 URL 표기법으로 사용하면 안될까?
안드로이드, iOS 플랫폼 가릴것 없이 링크로 라우팅을 해버리자

URL 링크를 이용한 라우팅

우리는 URL링크를 가지고 내비게이션 스택을 만들 수 있다고, 판단하였습니다.
간단한 URL 링크를 예를들어봅시다.

비게이션 스택을 만들 수 있다고, 판단하였습니다.
간단한 URL 링크를 예를들어봅시다.

이해를 돕기 위해, 영화 정보를 보여주는 앱을 만들어봅시다.

각각의 페이지는 Navigation(Push)로 이동됩니다.

우리는 딥링크 또는 APNS 를 이용하여, 사용자에게 보여줄 영화의 디테일 페이지까지 순서대로 내비게이션 스택을 쌓아야 합니다.
일단 URL 링크부터 만들어보도록 하죠.

movie-app://deep.link/home/category-list/movie-detail?category_id=123&movie_id=456

위의 URL를 딥링크로 전달받은 메서드에 URLComponent로 변환합니다.

  • scheme: movie-app
  • host: deep.link
  • path: home/category-list/movie-detail
  • queryItems: [“category_id”: “123”, “movie_id”: “456”]

위와 같은 형태로 변환이 되었네요.
path에 적힌 순서대로 HomePage > CategoryMovieListPage > MovieDetailPage 순으로 쌓이게되죠.
그다음은 queryItems의 각 Page에 필수로 주입받아야 할 각각의 ID를 전달 받은 queryItems를 이용하여 주입하게 됩니다.

위의 아이디어로 딥링크를 처리할 수 있게 되네요.

앞서 소개해드린 딥링크를 처리를 URL로 처리하게 아이디어를 제안했습니다. 이번에는 다른 상황에 대해 살펴보도록 하죠.
바로 현재 페이지에서 다른 페이지로 호출할 때는 URL로 쓰이게 되면 현재 페이지는 유연하게 대체하지 못하는 상황이 발생이 됩니다.이 부분은 html의 A 태그를 차용했습니다.
일반적으로 href에 “/nextPage”라고 입력하면, 현재 페이지에 다음 nextPage를 푸시해달라고 요청하는 것입니다.
이점을 이용하여 각 페이지에서 페이지를 요청할때 현재 페이지에서 다음 페이지 패스와 추가 파라미터를 전달하게 되면, 현재 페이지 주소를 전체를 알고 있지 않아도 되는 결과를 얻을 수 있습니다.

자 이제, 링크의 대한 이용에 대해 모든 정의와 설계가 끝이 났습니다. 하지만, 구현에서 또 다른 도전이 있었습니다. 각 핵심 서비스 독립원칙을 위배되지 않고 구현하는 방법이었습니다.

핵심서비스 모듈의 독립 원칙

저희는 Package모듈단위에 핵심서비스를 분리한다고 하였습니다.

도식화하게 되면 다음 페이지의 구조가 쉽게 이해되실 겁니다.
여기서 문제는 각 핵심 서비스 간의 페이지 전환(추가/삭제)입니다. 현재 페이지가 다른 핵심 서비스의 구현체를 import하고 페이지를 구현하게 된다면, 저희가 핵심 서비스 분리에서 가장 중요한 전제인 각 핵심 서비스 간의 분리(독립) 원칙이 위배되고 맙니다.
그에 따라, 저희 팀에서는 독립성을 보장하면서 자연스럽게 라우팅이 되는 방법에 대해 고민한 결과 디자인 패턴(템플릿 패턴)을 도입하게 되었습니다.

전화하지 마세요. 우리가 연락할게요(Don’t call us, we’ll call you)

헐리우드 원칙

각 핵심서비스에게 직접 import하여 구현하지 않고, 특정 누군가(Controller)에게 해당 페이지 Path와 items를 전달하면 알아서 그가 만들어주고 페이지 이동을 시켜주는것입니다.
다음에 호출할 페이지를 생성하고, 내비게이션에 추가해주는 컨트롤러가 있다면, 그에게 위임을 하면 각 핵심서비스의 독립성 보장과 더불어 페이지간에도 간섭이 발생하지 않게 됩니다.

그렇게 해서 탄생한 라이브러리가 LinkNavigator 입니다.

다음 시간에는 LinkNavigator 를 사용하면서 위의 첼린지를 보다 구체적으로 어떻게 해결했는지 다뤄보도록 하겠습니다.

0 Shares:
You May Also Like
Read More

각 핵심서비스의 독립

대부분의 앱의 첫 출시는 내부의 기능과 콘텐츠들은 매우 심플하고 복잡하지 않습니다. 그러나 사업이 확장되고, 더 많은 서비스가 등장하고,…
Read More

SwiftUI 기반 MVI(TCA) 아키텍쳐

일단 여기서 언급하는 아키텍쳐는 클린아키텍쳐도, 모듈 기반 아키텍쳐도 아닌, 별개로 보셔도 됩니다. 먼저 안드로이드와 iOS의 비즈니스 로직을 동일하게…