요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

국내 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하고 있을까요?

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

Android, iOS 웹뷰에서 딥링크 열기

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

국내 IT 기업은 한국을 넘어 세계를 무대로 할 정도로 뛰어난 기술과 아이디어를 자랑합니다. 이들은 기업 블로그를 통해 이러한 정보를 공개하고 있습니다. 요즘IT는 각 기업의 특색 있고 유익한 콘텐츠를 소개하는 시리즈를 준비했습니다. 이들은 어떻게 사고하고, 어떤 방식으로 일하고 있을까요?

 

이번 글은 토스의 페이테크 계열사 '토스페이먼츠'의 기술 블로그에서 딥링크 유형과 특징에 대해 소개합니다.

 

딥링크, 커스텀 링크, App Link… 이게 다 뭔가요? 네이티브 앱 개발자라면 한 번쯤 들어봤을 용어인데요. 이번 포스트에서는 각 딥링크 유형의 특징과 차이점을 자세히 알아보고, Android와 iOS 웹뷰에서 딥링크로 국내 카드앱·은행앱으로 이동하는 예시를 살펴보겠습니다.

 

딥링크란?

 

웹링크가 사용자를 특정 웹사이트로 이동시키듯이, 딥링크는 사용자를 특정 앱으로 이동시켜서 원하는 화면을 보여주거나, 사용자 액션을 유도합니다. 예를 들어, 사용자가 온라인 쇼핑몰에서 결제 수단으로 토스페이를 선택해요. 그럼 위 그림에 있는 왼쪽 화면이 나오고 ‘다음’을 누르면 토스 앱의 결제 페이지로 이동하죠. 딥링크를 사용했습니다! 앱의 사용자를 늘리거나 마케팅 캠페인에 굉장히 유용합니다.

 

사용자 입장에서는 친숙하지만, 개발자는 딥링크를 구현할 때 신경 쓸 게 많습니다. 웹에서 앱을 이동시킬 땐 어떤 설정이 필요한지, 사용자 폰에 앱이 설치되어 있는지, 안 되어 있으면 사용자를 어디로 이동시킬지 모두 구현해야 합니다.

 

 

딥링크의 유형

 

1. 커스텀 스킴(Custom Scheme)

커스텀 스킴은 가장 오래되었고 널리 사용되는 딥링크 유형입니다. Android, iOS에서 모두 사용할 수 있습니다. 커스텀 스킴은 ‘앱 링크’, ‘URI 스킴’으로 불리기도 합니다.

 

커스텀 스킴은 앱 스킴, 호스트, 패스(Path), 파라미터로 구성되어 있습니다. 앱 스킴은 이동하고 싶은 앱을 특정하고, 패스는 앱에서 들어가고 싶은 페이지를 특정합니다. 예를 들어, 토스앱의 스킴은 supertoss예요. 그럼 토스앱의 결제 페이지(pay)로 이동하는 링크는 supertoss://toss/pay가 되겠죠.

 

그러나 커스텀 스킴에는 서로 다른 앱에서 같은 스킴을 사용할 수 있다는 문제가 있습니다. 앱이 스킴을 등록할 때 이미 있는 스킴인지 아닌지를 확인할 수가 없거든요. Android에서는 같은 스킴을 가진 앱이 두 개가 있다면 “어떤 앱을 열래?”라고 묻는 선택 UI가 뜨지만, iOS에서는 이런 문제를 해결할 방법이 없습니다.

 

2. App Link와 Universal Link

그래서 커스텀 스킴의 한계를 보완하기 위해 2015년도에 iOS에서 Universal Link, Android에서 App Link를 출시했습니다. Android, iOS에서 가장 추천하는 딥링크 방식입니다.

 

커스텀 스킴과 달리 App Link, Universal Link는 표준 웹링크 형태입니다. 예를 들어 내 서비스가 웹에서 www.my-service.com에 운영되고 있다면, 이 링크를 그대로 앱의 딥링크로 사용하는 거죠. 사용자의 폰에 앱이 없다면 폰 브라우저에서 웹페이지가 그대로 열립니다.

 

그러나 App Link와 Universal Link는 사용자의 액션으로만 작동합니다. 스크립트로 클릭을 유발하면 앱으로 이동하지 않고, 그대로 웹 브라우저에서 링크가 열립니다. 그래서 여전히 커스텀 스킴 URL와 함께 사용되고 있습니다.

 

3. Intent 스킴

Android 환경에서는 커스텀 스킴, App Link와 사용되는 또 다른 딥링크가 있습니다. 바로 Intent 스킴이죠. App Link가 개발되기 전에 Android 웹뷰에서 사용됐습니다. iOS에서는 Intent 스킴을 사용하면 오류가 납니다.

intent:
   HOST/URI-path // Optional host
   #Intent;
      package=\\[string\\];
      action=\\[string\\];
      category=\\[string\\];
      component=\\[string\\];
      scheme=\\[string\\];
   end;

 

Intent 스킴은 위와 같이 상당히 복잡한 형태를 가지고 있습니다. 앱의 스킴, 패키지 등 많은 정보를 담고 있죠. 복잡하지만 개발자 입장에서는 Intent 스킴에서 제공하는 정보를 가공해서 다양한 방식으로 앱을 열 수 있습니다. 예를 들어 앱이 설치되지 않았거나 invalid한 상태인 것을 확인하고 앱 패키지 정보로 플레이 스토어를 열 수 있어요.

 

아래 표는 지금까지 알아본 딥링크 유형을 정리하고 있습니다. 더 자세한 내용은 포스트 맨 아래에 참고하면 좋을 자료에서 확인하세요.

 

웹뷰에서 앱으로 이동하기

지금까지 다양한 딥링크 유형을 알아봤는데요. 이제부터 국내 카드앱·은행앱을 Android, iOS 웹뷰에서 각각 열어보겠습니다.

 

Android 웹뷰에서 딥링크 열기

먼저 AndroidManifest.xml 파일에 카드앱·은행앱 패키지를 등록할게요. 패키지를 등록하지 않으면 앱이 설치되어 있어도 스토어로 이동하고, Google 정책을 위반합니다.

 

// AndroidManifest.xml

<queries>
  <package android:name="com.kakao.talk" /> <!-- 카카오톡 -->
  <package android:name="com.shcard.smartpay" /> <!-- 신한페이판 -->
  <package android:name="com.shinhancard.smartshinhan" /> <!-- 신한페이판-공동인증서 -->
  <package android:name="com.hyundaicard.appcard" /> <!-- 현대카드 -->
  <package android:name="com.lumensoft.touchenappfree" /> <!-- 현대카드-공동인증서 -->
  <package android:name="kr.co.samsungcard.mpocket" /> <!-- 삼성카드 -->
  <package android:name="nh.smart.nhallonepay" /> <!-- 올원페이 -->
  <package android:name="com.kbcard.cxh.appcard" /> <!-- KB Pay -->
  <package android:name="com.kbstar.liivbank" /> <!-- Liiv(KB국민은행) -->
  <package android:name="com.kbstar.reboot" /> <!-- Liiv Reboot(KB국민은행) -->
  <package android:name="kvp.jjy.MispAndroid320" /> <!-- ISP/페이북 -->
  <package android:name="com.lcacApp" /> <!-- 롯데카드 -->
  <package android:name="com.hanaskcard.paycla" /> <!-- 하나카드 -->
  <package android:name="kr.co.hanamembers.hmscustomer" /> <!-- 하나멤버스 -->
  <package android:name="kr.co.citibank.citimobile" /> <!-- 씨티모바일 -->
  <package android:name="com.wooricard.wpay" /> <!-- 우리페이 -->
  <package android:name="com.wooricard.smartapp" /> <!-- 우리카드 -->
  <package android:name="com.wooribank.smart.npib" /> <!-- 우리WON뱅킹 -->
  <package android:name="viva.republica.toss" /> <!-- 토스뱅크 -->
  <package android:name="com.nhnent.payapp" /> <!-- PAYCO -->
  <package android:name="com.ssg.serviceapp.android.egiftcertificate" /> <!-- SSGPAY -->
  <package android:name="com.kakao.talk" /> <!-- 카카오페이 -->
  <package android:name="com.nhn.android.search" /> <!-- 네이버페이 -->
  <package android:name="com.lotte.lpay" /> <!-- L.pay -->
  <package android:name="com.lottemembers.android" /> <!-- L.POINT -->
  <package android:name="com.samsung.android.spay" /> <!-- 삼성페이 -->
  <package android:name="com.samsung.android.spaylite" /> <!-- 삼성페이 -->
  <package android:name="com.lge.lgpay" /> <!-- 엘지페이 -->
  <package android:name="com.lguplus.paynow" /> <!-- 페이나우 -->
</queries>

 

Android에서 토스페이먼츠 결제창WebViewClient로 엽니다. WebViewClient를 사용하면 웹뷰에서 일어나는 요청, 상태, 에러를 콜백으로 편리하게 처리할 수 있습니다.

 

웹뷰에서 결제창을 띄우고 카드를 선택하면 카드앱의 딥링크가 열리죠. 웹뷰에서 URL을 열 때는 shouldOverrideUrlLoading()를 사용합니다. 이 메서드는 웹뷰에서 URL을 어떻게 열지 제어합니다. 메서드에서 true를 반환하면 URL은 개발자가 정의한 대로 열리고 웹뷰에서는 아무것도 하지 않습니다. false를 반환하면 현재 웹뷰에서 URL을 엽니다.

 

그럼 shouldOverrideUrlLoading() 안에 있는 코드를 살펴보겠습니다.

 

먼저 딥링크를 파싱해서 URI 객체로 만듭니다. 그리고 스킴을 확인합니다.

 

  • Intent 스킴이라면 startSchemeIntent()함수를 호출합니다. 코드 하단에 정의된 startSchemeIntent() 함수를 보세요. Intent 스킴 링크를 열어보고 앱이 설치 안 되어 있다면, Intent 스킴에 포함된 앱 패키지 정보로 스토어를 엽니다.
  • Intent 스킴이 아니라면 커스텀 링크 또는 App Link이겠죠. 해당 딥링크로 Activity를 시작합니다. 링크에 오타가 있거나 문제가 있어서 정상적으로 Activity가 실행되지 않는다면, false를 반환해서 웹뷰에서 링크를 엽니다. 여기에 스토어로 이동하는 로직도 추가할 수도 있습니다.

 

fun initViews() {
	WEB_VIEW.webViewClient = object : WebViewClient() {
      override fun shouldOverrideUrlLoading(
          view: WebView?,
          request: WebResourceRequest?
      ): Boolean {
          return shouldOverrideUrlLoading(view, request)
      }
  }
}

fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean
    url?.let {
        if (!URLUtil.isNetworkUrl(url) && !URLUtil.isJavaScriptUrl(url)) {
			// 딥링크로 URI 객체 만들기
            val uri = try {
                Uri.parse(url)
            } catch (e: Exception) {
                return false
            }

            return when (uri.scheme) {
                "intent" -> {
                    startSchemeIntent(it) // Intent 스킴인 경우
                }
                else -> {
                    return try {
                        startActivity(Intent(Intent.ACTION_VIEW, uri)) // 다른 딥링크 스킴이면 실행
                        true
                    } catch (e: java.lang.Exception) {
                        false
                    }
                }
            }
        } else {
            return false
        }
    } ?: return false

/*Intent 스킴을 처리하는 함수*/
fun startSchemeIntent(url: String): Boolean {
    val schemeIntent: Intent = try {
        Intent.parseUri(url, Intent.URI_INTENT_SCHEME) // Intent 스킴을 파싱
    } catch (e: URISyntaxException) {
        return false
    }
    try {
        startActivity(schemeIntent) // 앱으로 이동
        return true
    } catch (e: ActivityNotFoundException) { // 앱이 설치 안 되어 있는 경우
        val packageName = schemeIntent.getPackage()

        if (!packageName.isNullOrBlank()) {
            startActivity(
                Intent(
                    Intent.ACTION_VIEW,
                    Uri.parse("market://details?id=$packageName") // 스토어로 이동
                )
            )
            return true
        }
    }
    return false
}

 

iOS 웹뷰에서 딥링크 열기

먼저 Info.plistLSApplicationQueriesSchemes 를 추가하고 카드사, 은행의 앱 스킴을 배열에 넣어 주세요. 설정하지 않으면, 앱이 열리지 않고 콘솔 쪽에 canOpenURL : failed for URL 에러가 발생합니다.

 

//Info.plist
<key>LSApplicationQueriesSchemes</key>
    <array>
        <string>kb-acp</string>
        <string>liivbank</string>
        <string>newliiv</string>
        <string>kbbank</string>
        <string>nhappcardansimclick</string>
        <string>nhallonepayansimclick</string>
        <string>nonghyupcardansimclick</string>
        <string>lottesmartpay</string>
        <string>lotteappcard</string>
        <string>mpocket.online.ansimclick</string>
        <string>ansimclickscard</string>
        <string>tswansimclick</string>
        <string>ansimclickipcollect</string>
        <string>vguardstart</string>
        <string>samsungpay</string>
        <string>scardcertiapp</string>
        <string>shinhan-sr-ansimclick</string>
        <string>smshinhanansimclick</string>
        <string>com.wooricard.wcard</string>
        <string>newsmartpib</string>
        <string>citispay</string>
        <string>citicardappkr</string>
        <string>citimobileapp</string>
        <string>cloudpay</string>
        <string>hanawalletmembers</string>
        <string>hdcardappcardansimclick</string>
        <string>smhyundaiansimclick</string>
        <string>shinsegaeeasypayment</string>
        <string>payco</string>
        <string>lpayapp</string>
        <string>ispmobile</string>
        <string>tauthlink</string>
        <string>ktauthexternalcall</string>
        <string>upluscorporation</string>
    </array>

 

WKWebView 클래스를 사용해서 토스페이먼츠 결제창을 열겠습니다. 웹뷰에서 다른 URL로 이동하는 것을 제어하려면 WKNavigationDelgate을 사용하세요. WKNavigationDelegate을 conform하고 있는 클래스에서 아래와 같이 webView()를 정의하세요.

 

webView()를 더 자세히 보겠습니다. decidePolicyFor 파라미터에 들어가는 WKNavigationAction을 딥링크로 설정합니다. 함수 안에서 URL 스킴의 유형을 확인할게요.

 

  • URL 스킴이 http 혹은 https 이 아니라 커스팀 스킴이면, 링크를 열어보세요. 정상적으로 링크가 열린다면 성공이고 앱이 이동합니다. 안 열린다면 사용자 폰에 앱이 설치 안 되어 있어요. 사용자를 수동으로 앱 스토어로 이동시킬 수는 있지만, 이 방법은 유지 보수가 어렵습니다. 앱 스토어 아이디가 바뀔 수 있기 때문이죠. 2023년 3월 기준으로 국내 카드·은행 앱의 앱 스토어 아이디는 아래 코드에서 확인할 수 있습니다.
  • URL 스킴이 http 혹은 https이라면 Universal Link이겠죠. 웹뷰에서 바로 열면 됩니다. 사용자 폰에 앱이 설치되어 있지 있으면 바로 이동하고, 설치되어 있지 않으면 보통 각 카드·은행 앱에서 사용자를 스토어로 이동시킵니다.

 

func webView(
    _ webView: WKWebView,
    decidePolicyFor navigationAction: WKNavigationAction,
    decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
) {
    if let url = navigationAction.request.url, // Navigation Action을 딥링크로 설정
    url.scheme != "http" && url.scheme != "https" {
        UIApplication.shared.open(url, options: [:], completionHandler:{ (success) in
            if !(success){
                /* 앱이 설치되어 있지 않을 때
                   카드/은행 앱의 scheme과  앱토어 주소를 알고 있는 경우,
                   직접 앱스토어로 안내한다. */
				if let scheme = url.scheme,
					let appStoreURLString = Constant.appStoreURL[scheme],
					let appStoreURL = URL(string: appStoreURLString) {
						UIApplication.shared.open(appStoreURL)
					} else {
						// 스킴을 실행에 실패했거나, 앱을 안내할 수 없습니다.
                    }
            }
        })
        decisionHandler(.cancel) // 웹뷰에서 링크로 이동하지 않음
    } else {
        decisionHandler(.allow) // 웹뷰에서 링크로 이동
    }
}

 

// 카드/은행 앱 앱스토어 URL 관리 목록
enum Constant {
  static let appStoreURL: [String: String] = [
    "supertoss': "<https://apps.apple.com/app/id839333328>",
    "ispmobile': "<https://apps.apple.com/app/id369125087>",
    "kb-acp': "<https://apps.apple.com/app/id695436326>",
    "liivbank': "<https://apps.apple.com/app/id1126232922>",
    "mpocket.online.ansimclick': "<https://apps.apple.com/app/id535125356>",
    "lottesmartpay': "<https://apps.apple.com/app/id668497947>",
    "lotteappcard': "<https://apps.apple.com/app/id688047200>",
    "lpayapp': "<https://apps.apple.com/app/id1036098908>",
    "lmslpay': "<https://apps.apple.com/app/id473250588>",
    "cloudpay': "<https://apps.apple.com/app/id847268987>",
    "hanawalletmembers': "<https://apps.apple.com/app/id1038288833>",
    "hdcardappcardansimclick': "<https://apps.apple.com/app/id702653088>",
    "shinhan-sr-ansimclick': "<https://apps.apple.com/app/id572462317>",
    "wooripay': "<https://apps.apple.com/app/id1201113419>",
    "com.wooricard.wcard': "<https://apps.apple.com/app/id1499598869>",
    "newsmartpib': "<https://apps.apple.com/app/id1470181651>",
    "nhallonepayansimclick': "<https://apps.apple.com/app/id1177889176>",
    "citimobileapp': "<https://apps.apple.com/app/id1179759666>",
    "shinsegaeeasypayment': "<https://apps.apple.com/app/id666237916>",
    "naversearchthirdlogin': "<https://apps.apple.com/app/id393499958>",
    "payco': "<https://apps.apple.com/app/id924292102>",
    "kakaotalk': "<https://apps.apple.com/app/id362057947>",
    "kftc-bankpay': "<https://apps.apple.com/app/id398456030>"
  ]
}

 

참고하면 좋을 자료

 

<원문>

Android, iOS 웹뷰에서 딥링크 열기

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
토스페이먼츠 기술 블로그
78
명 알림 받는 중

작가 홈

토스페이먼츠 기술 블로그
78
명 알림 받는 중
토스페이먼츠 기술 블로그입니다. 개발자들이 만든, 개발자들을 위한 PG사 토스페이먼츠는 PG를 연동하는 개발자 경험을 수호하기 위해 직관적인 API, 편리한 개발자센터, 읽기 쉬운 문서를 만들고 있습니다.

좋아요

댓글

스크랩

공유

공유

지금 회원가입하고,
요즘IT가 PICK한 뉴스레터를 받아보세요!

회원가입하기
요즘IT의 멤버가 되어주세요! 요즘IT의 멤버가 되어주세요!
요즘IT의 멤버가 되어주세요!
모든 콘텐츠를 편하게 보고 스크랩해요.
모든 콘텐츠를 편하게 보고 스크랩 하기
매주 PICK한 콘텐츠를 뉴스레터로 받아요.
매주 PICK한 콘텐츠를 뉴스레터로 받기
로그인하고 무료로 사용하기