본문 바로가기

개발/부록

실제 프로젝트에서의 활용 예시를 곁들인 Deep Link Deep Dive

출처: https://support.singular.net/hc/en-us/articles/360050910891-How-to-Create-Deep-Links

 

 

글을 시작하며...

 

 

사내에서 개발하는 스니커즈, 명품 제품에 대한 모델정보를 보여주는 지면에 공유하기를 다루게 되었습니다. 앱 내 웹뷰로의 진입을 가능하게 하기 위해 일반적인 링크가 아닌 딥링크를 사용하게 되었고, 그 과정에서 일반적인 링크과 딥링크가 어떻게 다른지, 또 딥링크에는 어떤 종류가 있으며 어떠한 특징들을 가지고 있는지를 이해하고 상황에 맞는 방식을 선택하는 과정을 소개하고자 합니다.

 

 

 

링크

 

먼저 링크가 무엇인지에 대해 한번 돌아보겠습니다. 사전적 의미는 다음과 같습니다.

 

링크, 또는 하이퍼링크(Hyperlink)는 웹 페이지, 문서 또는 다른 리소스를 서로 연결하는 참조입니다. 사용자가 링크를 클릭하거나 탭할 때, 그들은 링크된 새로운 주소로 이동하게 됩니다. 링크는 인터넷의 기본 구성 요소 중 하나로, 웹의 상호 연결성과 탐색성을 가능하게 합니다.

 

 

우리가 알고 있는 링크란 특정한 주소로 이동하기 위한 주문(Order) 같은 것입니다. 어떠한 url을 입력하면 사전에 약속된 특정 화면으로 이동할 것임을 인지하고 이를 수행하기 위해 입력하는 것이지요. 실제로는 가고자 하는 위치의 IP주소로 향하지만, 이를 Domain Name으로 표현하고 인지하고 있다는 점에서 일종의 추상화와 비슷합니다.

 

이를 통해 우리가 서비스의 제공자 입장이라면, 우리의 서비스로 들어올 수 있도록 링크를 제공하는 방식이 일반적이었습니다.

 

하지만 과거 주로 웹 브라우저를 통해 여러 가지 콘텐츠들이 많았다면, 시대가 변함에 따라 스마트폰을 중심으로 한 애플리케이션을 통해 전달하는 콘텐츠가 많아졌습니다. 기존에는 링크라고 한다면 단순히 해당 웹사이트로의 이동만을 고려했다면, 이제는 내가 제공하는 컨텐츠가 애플리케이션 안에 있다면, 애플리케이션으로 한 번에 들어올 수 있는 길이 필요했습니다. 이를 해결하기 위해서 딥링크(Deep Link)라는 개념이 나타났습니다.

 

 

 

 

URL Scheme

 

 

딥링크 방식에서 첫 번째로 소개할 방식은 URL Scheme이라는 방식입니다. 

 

URL Scheme은 인터넷에서 리소스를 식별하고 접근하는 방법을 정의하는 표준을 활용하는 방법입니다. 기본적으로 URL의 형식에 대해 이해하면 URL Scheme을 이해하는 것은 간단합니다. URL은 다음과 같이 이루어져 있습니다.

 

 

출처: https://dev.to/jordanholtdev/what-you-need-to-know-about-urls-22ik

 

 

 

  • Scheme : 어떤 앱에 접근할 것인지에 대한 프로토콜을 명시합니다. 일반적으로는 리소스를 요청하는 http, https 프로토콜을 자주 사용합니다.
  • Authority(Host) : 리소스가 호스팅 되는 서버의 도메인 이름이나 IP 주소를 명시합니다.
  • Path : 리소스 혹은 호스팅되는 서버 내에서 특정 리소스의 위치를 가리킵니다.

 

 

따라서 URL Scheme 방식은 Scheme의 역할을 활용하여 해당 리소스에 접근하기 위한 특정 프로토콜을 만들어두고, 이를 명시하여 원하는 애플리케이션으로 이동하는 방식입니다. 실제로 스마트폰을 기반으로 한 애플리케이션 중심의 시대가 오기 전에도 우리의 PC에는 애플리케이션이 존재했습니다. 메일, 캘린더, 전화 등이 사실은 애플리케이션의 일종입니다. 실제로 아래의 URL Scheme들은 해당 애플리케이션으로 이동할 수 있는 프로토콜로 주소창에 입력한다면 해당 애플리케이션이 실행됩니다.

 

  • mailto://
  • tel://
  • file://

 

하지만 이러한 URL Scheme이 처음 활용되던 때에는 단점이 있었습니다. 이것은 특정 앱에 대한 URL Scheme이 고유하지 않아 이를 어뷰징하여 다른 링크를 눌렀을 때에도 어뷰징 된 링크가 나타나도록 URL Scheme을 설정할 수 있습니다. 이렇게 동일한 스킴이 존재할 경우 각 OS에서 이를 처리하는 방식이 다릅니다.

 

  • AOS : 어떤 앱으로 열지 선택하는 팝업을 열어줍니다.
  • iOS : 마지막으로 설치한 앱이 열립니다.

 

따라서 AOS 사용자라면 원하는 앱을 선택해서 진입할 수 있지만, iOS 사용자라면 나중에 Override 된 어뷰징 앱에 의도치 않게 진입하게 될 수 있습니다.

 

이러한 문제를 해결하기 위해 각 OS에서는 방법을 고안했습니다.

 

 

 

App Link, Universal Link

 

먼저, 앱 링크(App LInk)AOS에서 제공하는 방식, 유니버설 링크(Universal Link)IOS에서 제공하는 방식입니다.

 

두 OS는 위에서 언급한 어뷰징 이슈를 방지하기 위해 앱에 고유 ID정보가 포함되어 있는 파일을 함께 업로드하여 심사 과정을 거치고, 이후 심사가 완료되고 배포된다면 특정 URL Scheme을 통해 해당 앱으로 접근할 수 있도록 합니다. 이렇게 만들어진 URL은 일반 웹사이트의 URL과 동일하여 고유합니다. 또한 이렇게 App Link와 Universal Link를 사용하면 동일 URL이더라도 앱이 설치되어 있다면 해당 링크에 맞는 지면으로 이동시켜 주고, 앱이 설치되어있지 않다면 Web URL로 인식되어 해당 웹사이트로 이동합니다. (고유 ID정보가 파일 업로드에 대한 내용을 다루는 글은 아니기 때문에 추가적인 설명은 생략하겠습니다 :) )

 

이러한 방식을 활용하여 일반적으로는 웹서비스를 함께 제공하고 있는 서비스에서는 앱이 설치되어있지 않은 환경에서는 해당 URL을 통해 웹서비스로 접근할 수 있도록 하고, 아니라면 앱 마켓으로 보내 해당 앱을 설치할 수 있도록 안내하는 방식을 자주 사용합니다.

 

하지만 이 방식에도 한계는 존재했습니다.

 

먼저 브라우저 주소창에 해당 URL을 직접 입력하거나 복사/붙여 넣기를 하는 경우에는 이를 표준 웹 브라우징 동작으로 간주한다는 것입니다. 이렇게 할 경우 반드시 웹사이트를 열도록 설정되어 있어 의도했던 딥링크가 동작하지 않는 이슈가 있습니다.

 

또한 앱을 설치되어있지 않은 유저에게는 원래 보내고자 했던 지면으로 보내는 것이 아니라 앱을 설치할 수 있는 앱 마켓으로 보내기 때문에 유저가 최초 진입을 결심했던 지점으로 이동하기 위해서는 앱 설치 후 다시 한번 해당 링크를 클릭해야 하는 허들이 생깁니다. 유저가 처음 진입을 결심하는 허들도 충분히 높으니 다음 클릭까지 유도하는 것은 굉장히 어렵습니다. 실제로 이 단계에서 이탈도 많이 발생합니다. 따라서 이러한 문제를 해결하기 위한 방법이 필요했습니다.

 

 

디퍼드 딥링크(Deferred Deep Link)

 

위와 같은 이슈들을 해결하기 위해 디퍼드 딥링크(Deferred Deep Link)라는 방법이 나타났습니다.

 

디퍼드 딥링크의 동작은 다음과 같습니다.

 

 

출처: https://www.tune.com/blog/optimizing-user-experience-with-deferred-deep-linking/

 

 

이는 앱이 설치되어 있는 상태라면 원하는 동작으로 그대로 수행하고, 앱이 설치되어있지 않은 경우는 앱 마켓으로 이동, 앱을 설치할 수 있도록 유저에게 안내해 주고, 앱 설치 이후 처음 진입하게 된다면 기존 딥링크에서 이동하고자 했던 지면으로 바로 이동할 수 있도록 해줍니다.

 

그렇다면 이러한 디퍼드 딥링크를 직접 구축해야 할까요?

 

역시 아닙니다. 

 

실제 이를 제공해 주는 여러 가지 업체들이 존재하지만 대표적으로 두 가지만 소개해보겠습니다.

 

 

다이나믹 링크 (Dynamic Link)

 

첫 번째는 Google Firebase에서 제공해 주는 다이나믹 링크(Dynamic LInk)입니다.

 

실제 사용해 본 결과 장점은 아래와 같습니다.

 

 

장점

  • 무료입니다.
  • 코드를 활용하여 동적으로 링크를 생성할 수 있습니다.
  • 각 상황에 대한 디버깅을 제공합니다. (실제로 만들어진 링크 뒤에 d=1이라는 쿼리파라미터를 넣어주면, 해당 링크 진입 시 아래와 같은 디버깅 페이지가 나타납니다.)

 

출처: https://firebase.google.com/docs/dynamic-links/debug?hl=ko

 

 

 

하지만 가장 큰 단점이 있습니다.

 

Firebase에서 Dynamic Link에 대한 지원을 2025년 하반기까지 하기로 선언하였기 때문에 이를 잠시 활용한다면 문제가 되지 않을 수 있지만, 계속해서 사용한다면 이후 마이그레이션의 과정을 거쳐야 합니다.

 

 

출처 : https://firebase.google.com/docs/dynamic-links?hl=ko

 

 

 

 

원링크 (One Link)

 

두 번째 소개드릴 방법은 AppsFlyer사의 원링크(One Link)입니다.

 

저희 회사에서도 원링크를 사용하고 있었고, 일반적으로 대다수의 회사에서는 원링크를 사용 중인 것으로 알고 있습니다.

 

아마 누군가에게 전달받은 링크에 onelink와 같은 내용이 들어있다면, 그것은 AppsFlyer사의 원링크를 활용한 링크라는 것을 인지하실 수 있습니다.

 

이를 활용하여 링크를 생성하게 되면 제공하는 기능인 Branded Domain을 활용하여 제공하려는 서비스의 링크를 잘 드러낼 수 있습니다. (ex, bgzt.onelink.me, applink, bunjang.com)

 

또한 요새 Next.js를 기반으로 한 프로젝트들이 많아지고 있지만, 여전히 CRA, Vite를 활용한 Client Side Rendering 방식의 Single Page Application 프로젝트들이 많이 있고, 이러한 프로젝트에서는 지면별로 커스텀한 OG태그를 활용할 수 없는데, 원링크를 생성하는 과정에서 title, description, thumbnail과 같은 주요 내용을 지정할 수 있다는 점에서도 사용함에 있어 이점이 분명합니다.

 

원링크 역시도 정적뿐만 아니라 동적으로 링크를 생성할 수 있지만, 동적으로 생성하기 위해서는 추가적인 유료 플랜이 필요하고, 유료 플랜을 사용하더라도 Firebase에서 대량으로 동적 링크를 생성하고 있었다면, 그만큼의 동적 링크는 생성하기 어려운 것으로 파악했습니다.

 

하지만 원링크는 이때 기록한 정보를 바탕으로 여러 가지 데이터를 측정할 수 있다는 점에서도 기존에 가지고 있던 목적인 디퍼드 딥링킹 방식과 우리 서비스의 도메인을 드러내는 점 이상의 역할을 수행할 수 있기 때문에 다양한 기업들에서 사용하는 것으로 보입니다.

 

추가적으로 원링크의 원리는 다음과 같습니다.

 

유저가 클릭한 시점에 유저에 대한 정보를 DB에 저장하여 기억하고, 앱이 설치된 이후 해당 정보를 요청하여 기존에 기록해 둔 정보로부터 유저가 이동하려고 했던 URL Path로 이동시켜 줍니다.

 

 

실제로는 이러한 ID값을 매칭하는 것이 아래와 같이 이루어지며 이는 결국 여러 가지 요소인 IP, 휴대전화 사이즈, 스크린 사이즈, OS, OS 버전 등을 예측하여 이에 대한 값으로 파라미터 집합을 만들어 매칭을 수행하는데, 굉장히 드물게 동일한 상황을 가진 유저가 들어온다면 이는 동일한 접근으로 인지할 수 있기 때문에 100%의 동작 보증을 하지는 않는다는 이야기가 있다는 점도 간단하게 이해하고 가면 좋습니다. (아마 이러한 현상이 발생하더라도 비즈니스에 크티리컬하게 문제가 발생하지는 않고, 실현될 가능성은 거의 없기 때문에 무시해도 괜찮은 수준으로 보이지만 서비스 제공자로서 미리 공지하는 것으로 보입니다.)

 

 

출처: https://www.appsflyer.com/ko/resources/guides/deep-linking-for-developers/

 

 

 

 

 

 

실제 활용하면서 겪은 문제들

 

 

저희 서비스에서는 각 도메인을 담당하는 팀이 나누어져 있는데, 팀별로 많은 수의 동적 링크 생성 때문에 다이나믹 링크를 사용하는 조직도 있었고, 일부 정적인 페이지에서는 원링크를 활용하여 쉽게 링크를 생성하여 활용하고 있었습니다.

 

이번 신규 개발건에서는 다양한 모델에 대한 정보를 제공해야 하기 때문에 동적으로 생성할 수 있는 링크가 필요했습니다.

 

하지만 개발 과정에서 내부적으로 링크를 활용할 수 있도록 세팅해 주시는 앱 내의 코드에서 일부 문제가 있었음을 발견했습니다. 다행히 서비스 내에서 가장 많은 동적 링크를 생성하는 도메인이었던 상품 도메인은 영향을 받지 않고 있었고 이로 인해 다이나믹 링크를 잘 사용하고 있었기 때문에 문제를 뒤늦게 발견하게 되었습니다.

 

그렇다면 이러한 상황에서 단순히 앱에서 수정해 주셨기 때문에 사용이 가능할까요? 안타깝게도 아닙니다.

 

앱은 기본적으로 배포 버전을 기반으로 유저의 디바이스에 다운로드되고, 유저가 업데이트를 해야 수정된 코드가 반영되기 때문에 유저에게 강제 업데이트를 진행하지 않는 저희 서비스는 모든 유저의 버전이 자연스럽게 배포 버전으로 올라올 때까지는 새로 만드는 피처에서 다이나믹 링크를 사용할 수 없었습니다.

 

또한, 기존에 메인 서비스는 다이나믹 링크만으로 문제없이 동작하고 있었기 때문에 원링크는 낮은 금액의 플랜을 사용하여 동적 링크 생성이 불가능한 상황이었습니다. 이 역시 연단 위의 계약을 진행하기 때문에 다음 계약 시까지는 동적 원링크 생성이 불가했습니다.

 

따라서 사용할 수 있는 방법은 정적 원링크를 사용하는 방법이었습니다.

 

 

문제를 해결하기 위한 방법

 

 

다행히(?) 정적 원링크도 동적으로 사용할 수 있는 방법은 한 가지 있습니다.

 

일반적으로 다이나믹 링크나 원링크로 생성하게 된다면 해당 URL이 긴 길이를 가지고 있더라도 해당 링크를 짧은 길이의 링크로 변경해 줍니다. 이는 실제 공유가 많이 일어나는 디바이스 화면이 크지 않기 때문에 짧은 URL은 신경 써야 하는 중요한 요소 중 하나입니다.

 

하지만 이렇게 생성한 원링크의 뒤에 AppsFlyer사에서 제공하는 원링크 쿼리 파라미터를 추가하게 된다면 결과적으로는 원링크로 이동한 URL에도 넣어준 쿼리 파라미터에 따라 리다이렉트를 진행하고, 작성해 준 쿼리 파라미터 역시 같이 붙어있는 상태로 랜딩 됩니다. 하지만 이 방식은 긴 URL을 가지기 때문에 유저에게 노출하기는 어렵습니다.

 

또한 주제에서는 벗어나지만 저희 피처 서비스는 CRA기반의 CSR 프로젝트입니다. 따라서 공유하기를 통해 동적으로 OG태그를 제공해야 하는 미션이 있었습니다. 이 역시도 잘 해결했지만, 이에 대한 글은 아니기 때문에 이번 글에서는 다루지 않고 추후 기회가 된다면 해당 내용을 소개해보겠습니다 :)

 

 

이러한 상황 속에서 저희가 선택한 방법은 다음과 같았습니다.

 

  1. 최대한 간결하게 브릿지 페이지를 만들고 파라미터로 지면에 대한 정보를 식별할 수 있는 id값을 받도록 합니다.
  2. 해당 브릿지 페이지의 링크는 단순한 URL이기 때문에 이를 Route 53을 통해 bridge 도메인을 하나 생성하여 Base URL만 서비스 도메인이 잘 드러나는 이름으로 변경하고, 해당 Base URL의 브릿지 페이지의 Path로 들어오는 경우는 실제 도메인으로 리다이렉트 해주도록 했습니다.
  3. 위와 같은 세팅이 완료된 후 공유하기를 누른다면 브릿지 페이지의 URL에 해당 페이지가 보여주고있는 모델의 id를 파라미터로 붙인 브릿지 페이지 링크를 제공합니다.
  4. 브릿지 페이지의 파라미터에 있는 id를 기반으로 SEO 태그를 동적으로 구성할 수 있도록 했습니다. (설명은 제외하겠습니다.)
  5. 해당 브릿지 페이지는 일반 링크이기 때문에 원링크의 역할을 할 수 없습니다. 따라서 페이지 진입 직후 내부적으로 window.location.href를 원래 보내려고 했던 페이지의 정적 링크를 베이스로 생성한 원링크 URL에 웹, 앱에 따라 이동할 수 있는 리다이렉트 URL을 작성해 줍니다.

 

위와 같은 방식을 통해 동적으로 짧은 도메인을 가진 원링크를 생성할 수 없는 상황에서 유사하게 짧은 URL을 사용하면서도 원링크를 사용함으로써 얻을 수 있는 이점인 앱 미설치 유저의 진입 역시도 디퍼드 딥링킹 방식을 활용하여 설치까지 안내한 후 설치 이후 첫 진입 시에 원래 이동하고자 하는 지면으로 랜딩 시키게 되어 유저의 허들을 낮출 수 있게 되었습니다.

 

 

 

정리

 

공유하기 작업을 진행하기 전에는 간단하게만 알고 있던 현재 사용하는 링크의 변천사와 각 링크의 한계 및 특징들을 해당 프로젝트를 진행하면서 알게 되었고, 유저에게 더 좋은 경험을 제공하기 위해 주어진 상황에서도 포기하지 않고 해결하려고 노력했던 좋은 경험이었기에 실제 문제를 해결한 케이스에는 약간의 도약이 있지만 간단하게나마 공유해 보게 되었습니다. 언제나 그렇지만 잘못된 내용이 있다면 언제든 지적해 주시면 감사드리겠습니다. :) 

 

'개발 > 부록' 카테고리의 다른 글

What is REST API  (0) 2020.04.22
GraphQL로 Redux Local 동작 대체하기  (0) 2020.04.21
Koa + MongoDB + Apollo Server + Graphql 연동하기  (0) 2020.04.18
[부록] Shallow Merge VS Deep Merge  (0) 2020.04.18
[부록] Cookie, Web Storage  (0) 2020.04.17