[Web] 모바일 웹에서 앱 띄우기(딥링크)

yourjin·2022년 3월 12일
12

dev.log

목록 보기
1/13

이번에 모바일 웹에서 앱을 여는 기능을 개발하게 되었다. 아래 이미지에서 볼 수 있듯이, 이미 우리는 흔하게 사용하고 있는 기능이다.

하지만 처음 개발해 보는 내용이라 고생도 많이 했고, 새롭게 안 용어들도 많아서 이번 기회에 정리해 보려고 한다.

쿠팡 모바일 웹 화면

➕ Issue


개발 요구사항은 다음과 같다.

  • 앱으로 보기
    • 앱이 있는 경우, 앱을 열어 현재 보고 있는 페이지를 연다.
    • 앱이 없는 경우, 해당 앱을 다운 받을 수 있도록 마켓 페이지로 이동한다.
  • 모바일 웹으로 보기
    • 현재 보고 있는 페이지를 유지한다.

참 심플한 기능이다. 심지어 이미 사용하고 있는 공통 모듈도 있었기 때문에, 구현하는 데 어려움이 없어 보였다. 처음에 구현한 방식은 다음과 같았다.

  • 앱에서 띄우고자 하는 URL을 포함한 스킴을 생성한다.
  • 스킴을 인자로 하는 공통 모듈을 사용하여 앱을 연다.
  • 전달 받은 URL을 앱에서 그대로 띄워준다.

하지만 안드로이드에서 원하는 페이지가 나오지 않는 문제가 생겼다!

  • intent 방식으로 스킴을 전달할 때, url 파라미터를 추가해 앱에서 띄우고자 하는 웹 주소를 같이 전달한다. 이때 웹 주소는 UTF-8로 인코딩이 되어야 앱에서 띄울 수 있도록 되어 있기 때문에, 서버에서 전달할 때도 인코딩 된 URL을 전달했다.
  • 앱 개발자 분에게 문의한 결과, 안드로이드에서는 URL이 인코딩이 안된 채로 전달되고 있었다. 하지만 분명히 인코딩을 해서 보내고 있고, iOS에서는 잘 동작하는 것을 확인하였다.
  • 즉, 안드로이드에서 intent 방식으로 스킴을 전달할 때 인코딩이 되지 않아 생기는 문제임을 알 수 있었다.

➕ Progress


공통 모듈을 사용하여 쉽게 구현하려 했으나 실패하였으므로, 모듈을 이해하여 코드를 수정할 수 밖에 없었다.

이런 식으로 “특정 주소 혹은 값을 입력하면 앱이 실행되거나 앱 내 특정 화면으로 이동시키는 기능” 을 딥링크라고 한다. 딥링크는 다음 3가지 방식으로 구분할 수 있다.

  • URI 스킴 방식 : 앱에 URI 스킴(scheme) 값을 등록하여 딥링크 사용
  • 앱링크(App Link) : Android 제공 - 도메인 주소를 이용한 딥링크 사용
  • 유니버셜 링크 (Universal Link) : iOS 제공 - 도메인 주소를 이용한 딥링크 사용

2. URI 스킴 방식

모듈에서는 URI 스킴 방식을 사용하고 있었기 때문에 이를 집중적으로 살펴봤다.

스킴(Scheme)은 앱마다 등록할 수 있는 값으로, 이를 통해 우리는 앱을 구분할 수 있다. 그래서 우리는 특정 스킴 값을 호출하여 특정 앱을 열 수 있다.

URI 스킴 방식은 Scheme://Path 라는 두 개의 요소로 구성된다.

  • 예시: twitter://signup
    • Scheme = 앱을 특정함 (트위터)
    • Path = 앱 내 페이지를 특정함 (트위터 내 회원가입 페이지)

URI 스킴 사용 방법

  • 안드로이드 / ios에 스킴 값을 설정한다.
  • Javascript에서 window.location.href = (URI 스킴) 구문을 통해 이동할 수 있다.

URI 스킴의 한계

URI 스킴에서 가장 문제가 되는 점은, 스킴이 중복될 수 있다는 것이다. 즉, 앱으로 바로 이동하지 못하고 어떤 앱으로 이동할 지 사용자가 선택을 해야 한다는 것이다. 실제로 market:// 은 구글플레이 스토어, OneStore, 삼성 앱스토어 등 여러 앱스토어에서 함께 사용하고 있다.

스킴이 중복되는 것은 마케팅적으로 문제가 된다. 스킴의 생성 목적이 앱 사용자 수를 늘리기 위함인데, 자신들의 앱으로 연결하지 못한다면 의미가 없기 때문이다.

3. Android에서 딥링크 구현

iframe 사용

A common and old technique to solve this problem is using iframe to load the deep link URL and having a delayed JavaScript to redirect to store

결과적으로는 URI 스킴 이동과 앱스토어 이동이 모두 일어나지만, 시간차를 이용해 한 가지만 일어나는 것처럼 보이게 하는 일종의 트릭이다.

  • 예시: twitter://signup 로 이동한다고 가정했을 때
    • 앱이 설치된 경우, 앱이 실행되어 이동하고 이후의 자바스크립트는 실행되지 않는다.
    • 앱이 설치되어 있지 않은 경우, 스킴을 호출하여도 아무 작업도 수행되지 않는다. 그리고 setTimeout() 로 인해 일정 시간 뒤에 마켓 주소로 이동하는 자바스크립트가 실행된다. 따라서, 앱스토어로 이동되어 사용자가 앱을 설치할 수 있다.
  • 구현 방법
    setTimeout(
    		var visitedAt = (new Date()).getTime(); // 방문 시간
        
    		function() {
            if((new Date()).getTime() - visitedAt < 2000) { 
                location.href = "{마켓 주소}";
            }
        }, 500);
    
    var iframe = document.createElement('iframe');
    iframe.style.visibility = 'hidden'; // 보이지 않는 iframe으로 스킴을 호출한다.
    iframe.src = '{커스텀 스킴 주소}';
    document.body.appendChild(iframe);
    document.body.removeChild(iframe); // back 호출시 캐싱될 수 있으므로 제거
    • 보이지 않는 iframe으로 커스텀 스킴 주소를 호출한다.
    • 응답이 없으면 0.5초 뒤에 마켓으로 이동한다.

Chrome Intent 사용

Since Chrome for Android version 25 and later, the above code stopped working according to Chrome documentation Fortunately, Google provides the Intent URL for a better solution.

Chrome의 경우 intent URL 방식을 도입하면서부터 iframe 방식을 사용할 수 없게 되었다. 하지만 intent URL은 앱의 설치 여부에 따른 처리를 자동으로 해주기 때문에 더욱 유용하다.

  • intent URL 동작 방식
    • 앱이 설치된 경우, 앱이 실행된다.
    • 앱이 설치되지 않은 경우, 자동적으로 앱스토어로 이동한다.
  • intent URL 형식: intent://path/#Intent;scheme=yourapp;package=com.yourapp.example;
  • 구현 방법
    setTimeout(function() {
                  location.href = "intent://커스텀스킴주소#Intent; scheme=스킴; action=..;category=..; package=com.android.xxx; end;";
             }, 1000);

4. iOS에서 딥링크 구현

iOS에서는 안드로이드에서 iframe 방식과 동일한 트릭을 사용하여 구현 가능하다.

대신 iframe을 통해 이동할 필요 없이, 그냥 스킴 주소를 통해 이동할 수 있다.

  • 구현 방법
    setTimeout(
          function() {
              if ((new Date()).getTime() - visitedAt < 2000) {
                   location.href = "{마켓 주소}";
              }
           }, 500);
    
    setTimeout(function() { 
            location.href = "{커스텀 스킴 주소}";
        }, 0);

➕ Conclusion


안드로이드 intent 방식을 사용했을 때만 인코딩 이슈가 있었기 때문에, 결과적으로는 기존 방식에서 사용하던 트릭을 그대로 이용하기로 결정했다.

(intent 방식으로 처리할 때 내부적인 로직으로 인해 인코딩이 풀리는 것으로 보인다. 혹시 정확한 원인을 아시는 분이 있다면 댓글 부탁드린다.. 나는 원인을 찾지 못했다 ㅠㅠ)

setTimeout(
      function() {
          if ((new Date()).getTime() - visitedAt < 2000) {
               location.href = "{intent URL}";
          }
       }, 500);

setTimeout(function() { 
        location.href = "{커스텀 스킴 주소}";
    }, 0);
  • 이때, 마켓 주소 대신 intent URL을 호출하게 수정했다. 마켓 주소를 직접 호출하면 iOS에서는 자동으로 App Store 앱이 열리지만, 안드로이드에서는 Play Store 웹 페이지로 이동하기 때문이다.
  • 앱이 설치되어 있다면, 설정한 시간 차로 인해 커스텀 스킴 주소로 이동하고 이후 과정은 실행되지 않을 것이다. 따라서 앱이 설치되지 않은 경우만 intent URL이 호출되어 Play Store 앱으로 이동하게 된다.

➕ References


profile
make it mine, make it yours

0개의 댓글