안드로이드 앱링크 - andeuloideu aeblingkeu

딥링크란?

안드로이드 앱링크 - andeuloideu aeblingkeu

브랜디 페이스북 페이지의 게시글을 보면 https://goo.gl/JCZD4z와 같은 링크들이 있습니다. 안드로이드에서 이 링크를 클릭하면 앱이 실행되지만 앱이 설치되어 있지 않으면 웹브라우저에서 브랜디가 실행됩니다.

안드로이드 앱링크 - andeuloideu aeblingkeu

위와 같이 앱의 특정 화면으로 이동하는 기능을 딥링크하고 하죠.

딥링크 사용하기

<intent-filter>
    <action android:name=“android.intent.action.VIEW” />

    <category android:name=“android.intent.category.DEFAULT” />
    <category android:name=“android.intent.category.BROWSABLE” />

    <data
        android:host=“deeplink”
        android:scheme=“boyeproject” />
</intent-filter>

AndroidManifest 파일을 열어 버튼이나 링크를 눌렀을 때 실행하고 싶은 Activity에 위와 같이 intent filter를 설정합니다. 딥링크를 사용하기 위한 네 가지 요소이기도 하죠. 하나 하나 살펴볼까요?

### <action android:name=“android.intent.action.VIEW” />

먼저 첫 번째, 액션은 intent에 첨부되는 data의 URI가 가리키는 데이터를 사용자에게 보여주는 액션입니다.

### <category android:name=“android.intent.category.DEFAULT” />

두 번째, 이 카테고리를 intent filter에 선언하지 않으면 Activity에 어떠한 암시적 intent도 확인되지 않기 때문에 반드시 선언해야 합니다. 이 카테고리를 선언함으로써 앱 이름을 지정하지 않고 딥링크를 사용할 수 있게 해줍니다.

### <category android:name=“android.intent.category.BROWSABLE” />

세 번째, 이 카테고리는 웹 브라우저에서 링크를 열 수 있게 합니다.

### <data
###     android:host=“link”
###     android:scheme=“boyeproject” />

마지막으로 데이터는 이 페이지에 대한 딥링크로 어떤 URI를 사용할 것인지 지정합니다. host와 scheme이 이렇게 설정되었다면 ~boyeproject://applink~ 라는 URI는 무조건 해당 Activity가 실행됩니다.

Intent intent = getIntent();

Logger.*d*(“action : “ + intent.getAction());
Logger.*d*(“data : “ + intent.getDataString());

위의 코드를 통해 딥링크로 실행된 경우와 아닌 경우를 비교해봅시다.

딥링크로 실행되지 않은 경우딥링크로 실행된 경우
action android.intent.action.MAIN android.intent.action.VIEW
data null boyeproject://deeplink

딥링크로 실행된 경우와 그렇지 않은 경우 action과 data값이 다릅니다. 그렇기 때문에 action과 data를 비교해 딥링크로 실행된 경우를 찾아낼 수 있습니다.

if (Intent.*ACTION_VIEW*.equalsIgnoreCase(action) && data != null) {
    textView.setText(“DeepLink!”);
} else {
    textView.setText(“Not DeepLink!”);
}

위 코드로 딥링크로 실행된 경우에는 TextView에 DeepLink! 를 띄우고 아닌 경우에는 Not DeepLink! 를 띄워 실행해봅시다.

안드로이드 앱링크 - andeuloideu aeblingkeu

딥링크를 통해서 실행한 경우와 아닌 경우를 판별할 수 있게 되었습니다. 이제 딥링크에 추가적인 정보를 전달하는 방법을 알아보겠습니다.

딥링크를 통해서 추가적인 정보를 전달하고 싶을 땐 ~scheme://host/{parameter}~ 이렇게 딥링크를 설정하면 됩니다.

private static final String DEFAULT_PATH = “boyeproject://deeplink/“;

if (Intent.*ACTION_VIEW*.equalsIgnoreCase(action) && data != null) {
    if (data.startsWith(DEFAULT_PATH)) {
        String param = data.replace(DEFAULT_PATH, “”);
        textView.setText(param);
    }
}

위 코드는 parameter로 받은 값입니다. TextView를 설정한 코드죠.

안드로이드 앱링크 - andeuloideu aeblingkeu

parameter로 설정한 additional이 TextView에 설정된 것을 확인할 수 있습니다.

결론입니다.

만약 딥링크가 없었다면 브랜디 페이스북 페이지의 게시글에서 원하는 상품을 발견했더라도 사용자는 복잡한 과정을 거쳐야 했을 겁니다. 생각해보세요. 브랜디 앱을 열고, 해당 상품을 검색하고, 원하는 페이지를 찾기까지… 쇼핑보다는 방황이라고 말하는 게 더 어울리는 듯합니다. 물론 이런 서비스를 좋아할 사용자는 없을 테고요. 오늘부턴 사용자들이 클릭 한 번으로 앱의 원하는 페이지까지 바로 이동할 수 있도록 딥링크를 사용하는 건 어떨까요?


김보예 | MA팀

브랜디, 오직 예쁜 옷만

안드로이드 앱링크 - andeuloideu aeblingkeu
안드로이드 앱링크 - andeuloideu aeblingkeu

App Deep Link

URL Scheme, Deep Link 메커니즘은 Android 1.0에서부터 제공되었다. 개발자가 일단 앱을 설치하면 특정 장치에 대한 앱의 URI(Uniform Resource Indentifier)를 운영체제에 등록할 수 있다. URI는 HTTP, market 혹은 myapp과 같은 특수 문자를 포함하지 않는 어떤 문자열로 될 수 있고, 한번 등록하면, 끝에 ://를 붙이고(예를 들면, market://)  링크를 클릭하기만 하면 Google Play 혹은 스토어 앱이 구동된다. 이와 같이 사용자들에게 앱을 직접적으로 구동할 수 있게 해 주는 것이 딥 링크(Deep Link)이다.

  • 사용자를 앱 특정 Contents로 바로 연결하는 URL
  • Intent filter를 추가하고 수신 Intent에서 데이터를 추출하여 딥 링크를 설정
  • 다른 앱에서 동일한 Intent를 처리할 수 있다면 사용자는 개발자의 앱으로 바로 이동하지 않음.

안드로이드 URI Scheme

Intent filter를 통해 URI에 응답할 Activity를 매니페스트 파일에 등록한다. 이를 사용하기 위해서는 당연히 앱이 설치되어 있어야한다. 앱이 설치되어 있지 않다면, Page Not Found 에러 혹은 아무런 동작을 하지 않을 것이다.

    <activity
        android:name="com.example.android.MyAppActivity"
        android:label="@string/title_myapp" >
        <intent-filter android:label="@string/filter_view_example_myapp">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <!-- Accepts URIs that begin with "example://myapp” -->
            <data android:scheme="example" android:host="myapp" />
        </intent-filter>
    </activity>
   

앱이 최초 설치될 때 Intent-filter를 통해 앱이 수신가능한 딥 링크를 운영체제에 등록을 할 수 있고, 해당 URI Scheme의 이벤트가 발생하면 이를 해당 앱을 전달하여 구동된다. 하지만, myroute.co.kr 같은 도메인과는 다르게 URI Scheme에는 유니크한 제한이 없기 때문에 여러 앱에서 같은 URI Scheme을 사용하기도 한다. 예를 들어, market:// 스킴은 Google Play에서도 정의하고 있지만 Galaxy Store, 원스토어에서도 동일한 스킴을 정의해 두었다. 이렇게 동일한 URI Scheme이 있을 경우에는 Chooser를 통해 구동할 앱을 결정한다.

번외로, 예전에 원스토어에서 market:// 스킴을 등록했다가 다른 회사들의 요청 의해 철회된 적이 있다. 하지만 최근 Galaxy Store가 이를 등록함으로서 원스토어에서도 다시 정의한 것으로 보인다. 그리고 이는 구글 정책상 open framework으로서 앱스토어로서 동일하게 구현할 수 있게 하기 위해서이다. 

안드로이드 앱링크 - andeuloideu aeblingkeu

수신되는 Intent에서 데이터 읽기

앱이 Intent filter를 통해서 구동되면 Intent에서 제공하는 데이터를 사용하여 렌더링해야하는 경우들이 종종 발생한다. 이를 위해서 getData() 및 getAction() 메소드를 호출하여 수진 Intent와 연결된 데이터와 작업을 가져올 수 있다. 액티비티 생명주기 동안에 이러한 메소드를 통해서 언제든지 확인가능하며, 일반적으로 onCreate()나 onStart()와 같은 초기 콜백에서 호출한다.

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Intent intent = getIntent();
        String action = intent.getAction();
        Uri data = intent.getData();
        
		...
    }
    

Deferred Deep Linking

앞에서 언급 했듯이 딥 링크는 특정 페이지로 도달 할 수 있도록 하는 링크를 의미한다. 하지만 앱이 설치되어 있지 않다면 이는 무용지물이다. 조금 더 구체적인 예를 들어보자면, 회사에서는 많은 돈을 사용해서 포털에 광고를 하고 이 광고배너를 클릭시 모바일 앱을 구동하고 싶어한다. 그리고 특정 프로모션 페이지로 이동하기를 원할 것이다. 하지만 고객의 핸드폰에는 회사의 앱이 설치되어 있을 수도 있고 그렇지 않을 수도 있다.

앱을 미설치한 고객의 경우 앱 스토어로 이동하면서 기존에 있던 Deep Link의 정보를 유실하게 되는 경우가 있다. 이를 해결하고자 등장한 개념이 디퍼드 딥 링크로 기존 고객은 앱 내 특정 페이지로 바로 이동하고, 미설치 고객들은 앱 스토어로 이동 후 고객이 설치 및 실행하면 앱 내 특정 페이지로 이동하는 것이다.

안드로이드 앱링크 - andeuloideu aeblingkeu

위에서 언급한 것처럼 외부에 광고 배너를 노출했다면, 우선적으로 회사의 웹 페이지의 랜딩페이지로 이동한다.

그리고 꼼수(?)를 통해서, 바로 app scheme을 실행시키고, 반응이 없을 것을 대비하여 약간의 Delay를 통해 앱 스토어로 이동할 수 있도록 한다.

// Link to landing page in your company
https://link.yourcompany.com/banner/front_url={https://encodedUrl}

// fallback url for market
market://details?id=com.yourcompany.mobile
   &url={encoded scheme URI. e.g myapp://list?key1=value1&key2=value2}
   &referrer={encoded DATA. e.g key1=value1&key2=value2}
   
<script type="text/javascript">
window.onload = function() {
	var now = new Date().getTime();

    setTimeout(function() {
    	if(new Date().getTime() - now < 1000) {
        	// Link to the App Store should go here -- only fires if deep link fails                
        	location.href = "app store url";
        }
    }, 500);
    
    setTimeout(function() {
        // Link to the App Store should go here -- only fires if deep link fails                
        location.href = "app scheme";
    }, 100);
};
</script>

Deep Link 테스트

Android Debug Bridge(ADB)를 활동 관리자(am) 도구와 함께 사용하여 딥 링크용으로 지정한 Intent filter URI가 올바른 앱 활동으로 확인되는지 테스트 할 수 있다.

$ adb shell am start -W -a android.intent.action.VIEW -d <URI> <PACKAGE>
    
$ adb shell am start -W -a android.intent.action.VIEW -d "example://myapp" com.example.android

Google Play에서의 install referrer 역시 ADB를 통해서 테스트 할 수 있다.

$ adb shell am broadcast -a com.android.vending.INSTALL_REFERRER
	-n com.yourcompany.mobile/{com.yourcompany.mobile.install}.ReferrerReceiver
    -es referrer "key1=value1"

URI Scheme의 한계

URI Scheme는 제약없이 설정할 수 있기 때문에 앱 별로 고유한 딥 링크를 점유하는 것이 불가능하다. 위에서 언급한 것과 같이 다른 앱에서 Scheme값을 중복한다면 다른 앱에서 하이재킹(hijacking)될 수도 있다. 이러한 보안 문제로 iOS에서는 9.1 버전 이후로는 URI Scheme에 대한 추가 개발하지 않겠다고 공지했고, 몇몇 웹 브라우저와 앱에서는 URI Scheme에 대한 경고 메세지를 띄우거나 Scheme이 동작하지 않도록 하였다.

App Link(Dynamic Link)와 Universal Link

URI Scheme이 가진 문제점을 해결하기 위해 2015년 하반기에 Android와 iOS 플랫폼은 각각 새로운 딥 링크를 개발하여 발표하였고, iOS는 Universal Link, Android는 App Link를 발표했다. 구동되는 환경은 다르지만, 개념적으로는 비슷한 형태의 딥 링크이다.

이는 도메인의 고유성과 HTTPS의 SSL(secure sockets layer) 인증서를 이용해 딥링크를 안전한 자원으로 관리, 사용하여 기존에 존재했던 보안 문제를 해결하고자 했다.

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "myapp",
    "package_name": "com.yourcompany.mobile",
    "sha256_cert_fingerprints": ["XX:YY:ZZ:..."]
  }
}]
<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <action android:name="android.intent.action.VIEW" />
    <data
      android:pathPattern="/document/.*\\"
      android:host="www.yourcompany.com"
      android:scheme="http" />
    <data
      android:path="/category"
      android:host="www.yourcompany.com"
      android:scheme="http" />
</intent-filter>

App Link의 한계 

App Link는 사용자 클릭이 트리거(trigger)되었을 때만 동작합니다. 예를 들어 jQuery 이벤트인 document ready에서 링크를 실행했을 때는 앱이 설치되어 있음에도 불구하고 fallback URL로 동작합니다. 또한 특정 앱에서 동작하지 않도록 막은 경우도 있습니다. 대표적으로 Pinterest, WeChat, Facebook Messenger, Telegram 등의 앱에서는 지원되지 않습니다. 마지막으로 iOS는 9.2 미만, Android는 마시멜로(6.0) 미만 버전에서는 사용할 수 없습니다. 이런 이유 때문에 Universal Link와 App Link가 실행되지 않는 상황에 대응하기 위해 URL schemes와 복합적으로 사용해야 합니다.

Android 6.0(마시멜로 API 23)이상의 Android 앱 링크를 사용하면, 특정 유형의 링크에 적용되는 기본 핸들러로 앱 자체를 지정가능하다. 하지만, Android 6.0 미만 버전에서 사용할 수 없고, Pinterest, Facebook Messenger 등 특정 앱에서는 동작하지 않도록 막는 경우도 있다. 그리고 Intent Scheme 역시 Google Chrome에서만 사용할 수 있는 기능이기 때문에 여전히 문제점들이 있다.

복합적인 Deep Link/App Link의 구현

Deep Link와 App Link에는 여전히 문제점을 가지고 있다. 결국 이를 해결하기 위해서는 유저의 상태를 보고 이를 해결해야한다. App Link가 동작하는지, 앱이 설치되어 있는지를 단계적으로 확인하는 모습이 필요하다.

참고문헌

developer.android.com/training/app-links

shinjekim.github.io/android/2019/08/07/Android-DeepLink/

engineering.linecorp.com/ko/blog/how-to-use-deeplink-in-trackit/