Skip to content

Instantly share code, notes, and snippets.

@ValuepotionSDK
Last active November 22, 2016 05:12
Show Gist options
  • Save ValuepotionSDK/f3316d05d0182fe420b77cb7747da0bf to your computer and use it in GitHub Desktop.
Save ValuepotionSDK/f3316d05d0182fe420b77cb7747da0bf to your computer and use it in GitHub Desktop.
[밸류포션] 하이브리드 앱을 위한 밸류포션 SDK 연동 안내

개요

하이브리드 앱에서 Android/iOS 네이티브 영역과 웹뷰 내의 영역 모두에서 밸류포션의 기능을 사용하는 경우를 위한 가이드입니다.

하이브리드 앱에 이 방법으로 연동해야 하는 이유

하이브리드 앱을 위해 밸류포션 SDK 를 연동할 때, 이 안내 문서가 없다면 자연스레 아래의 세 가지 방법 중 하나를 택하게 될 것입니다.

  1. 밸류포션 대시보드에서 앱을 1개만 생성하고, 하나의 clientId 를 Android/iOS 네이티브 SDK 와 웹용 SDK 양쪽에 공통적으로 사용한다
  2. 밸류포션 대시보드에서 앱을 2개 생성해서, 하나의 clientId 는 Android/iOS 네이티브 SDK 를 초기화하는데 사용하고, 다른 하나의 clientId 는 웹용 SDK 를 초기화하는데 사용한다
  3. 밸류포션 대시보드에서 앱을 1개만 생성하고, 앱 내에서 Android/iOS 네이티브 SDK 는 사용하지 않고, 웹뷰 내에서 웹용 SDK 만 사용한다
  • 1 의 방법을 사용했을 때는, Android/iOS SDK 과 웹용 SDK 가 유저를 식별하는데 사용하는 방법이 다른 관계로 네이티브 영역의 지표와 웹뷰 내의 지표가 무의미하게 섞여 버립니다.
  • 2 는 1 의 문제를 해결하였지만, 결국엔 대시보드에서 두 앱의 지표를 모두 확인해야 하는 번거로움이 있습니다.
  • 3 은 광고 클릭이 발생했는데 외부 브라우저를 띄워주지 못하고 웹뷰 내에서 페이지 전환이 발생하는 문제가 있습니다.

그래서 이 문서에서 안내하는 방법을 사용하면 웹뷰 내에서 발생하는 이벤트들이 브릿지를 통해 Android/iOS 네이티브 SDK 에 전달되며, 결국 Android/iOS 네이티브 SDK 를 통해서만 모든 이벤트가 밸류포션 서버에 저장되기 때문에 네이티브 영역과 웹뷰 영역의 이벤트가 의미있게 통합되어 지표를 확인하실 수 있고, 광고도 문제 없이 동작합니다.

앱 관리

하이브리드 앱의 경우 밸류포션에서 단 1개의 앱만 생성하신 후에, 그 Client Id, Secret Key 를 Android/iOS 네이티브 SDK 를 연동하시는데 사용하시면 됩니다.

웹뷰 쪽의 스크립트를 위해 앱(clientId)을 별도로 생성하실 필요가 없습니다.

연동 방법

Android/iOS 네이티브 SDK 의 기본 연동을 해 놓으신 후에 아래 추가 코드를 넣어주셔야 합니다.

SDK 버전

  • Android SDK 1.1.4 혹은 그보다 높은 버전
  • iOS SDK 1.1.28 혹은 그보다 높은 버전의 framework library
  • 웹 SDK v11 혹은 그보다 높은 버전 (vp-latest)

Step 1. 네이티브 코드

Android 네이티브

import com.valuepotion.sdk.HybridBridge;

....

public void initWebView() {
  webView = (WebView) findViewById(R.id.webview);
  webView.getSettings().setJavaScriptEnabled(true);
  webView.setWebViewClient(new HybridBridge(activity, webView).getWebViewClient());
  webView.loadUrl("...");
}

위와 같이 웹뷰 객체를 얻어온 후에 HybridBridge 객체를 생성하여 웹뷰에 세팅을 해줍니다. 이는 웹뷰에 페이지를 로드하기 전에 한번 선행되어야 합니다.

만약에 이미 웹뷰에 setWebViewClient 메소드를 통해 직접 세팅해 놓은 WebViewClient 가 있다면 아래와 같이 합니다.

public void initWebView() {
  webView = (WebView) findViewById(R.id.webview);
  webView.getSettings().setJavaScriptEnabled(true);
  
	final HybridBridge hybridBridge = new HybridBridge(activity, webView);
	webView.setWebViewClient(new WebViewClient() {
		@Override
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			if (hybridBridge.shouldOverrideUrlLoading(url)) {
				return true;
			}
			return super.shouldOverrideUrlLoading(view, url);
		}
	});

  webView.loadUrl("...");
}

iOS 네이티브 (UIWebView 를 사용하는 경우)

// Objective-C
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    // bridge method 에서 해당 request 를 처리했다면, NO 를 리턴합니다.
    // 따라서 NO 를 리턴했을 때 이후 처리 또는 페이지 이동을 중지합니다.
    if ([[ValuePotion bridge] webView:webView shouldStartLoadWithRequest:request] == NO) {
        return NO;
    }

    // ...
    
    return YES;
}

iOS 네이티브 (WKWebView 를 사용하는 경우)

// Swift
func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
    // bridge method 에서 해당 request 를 처리했다면, false 를 리턴합니다.
    // 따라서 false 를 리턴했을 때 이후 처리 또는 페이지 이동을 중지합니다.
    if (!ValuePotion.bridge().webView(webView, decidePolicyForNavigationAction: navigationAction)) {
        decisionHandler(.Cancel)
    }

    // ...
    
    decisionHandler(.Allow)
}

Step 2. 웹

VP.init({
  hybrid: true,
  noAd: function (iframeId) {
      ...
  },
  adDisplayed: function (iframeId) {
      ...
  },
  ...
});

원래 웹용 SDK 에서 VP.init() 함수를 호출할 때 clientId 를 넣도록 안내하고 있으나, 하이브리드 앱인 경우는 clientId 를 넣지 않고, 대신 hybrid: true 를 넣어야 합니다.

SDK 사용하기

위와 같이 연동이 다 되었으면, Android/iOS SDK 와 웹 SDK 둘다 기존의 방법 그대로 사용하시면 정상적으로 작동합니다.

웹뷰 내에서 도메인 없는 웹 페이지를 로드하는 경우

앱에서 html 을 직접 생성해서 웹뷰에 로드 시키는 방법을 사용하고 계시다면 아래의 두 가지 경우 중 하나로 예상됩니다.

  1. 웹뷰를 배너 사이즈만하게 하고, 그 웹뷰 안에 다른 컨텐츠는 없이 밸류포션의 광고만 노출시키려는 경우
  2. 앱의 웹뷰에 원래부터 앱의 컨텐츠를 html 로 구성해서 제공해오고 있었는데, 그 웹뷰 안에 밸류포션의 광고를 노출시키려는 경우

1. 웹뷰를 배너 사이즈만하게 하고, 그 웹뷰 안에 다른 컨텐츠는 없이 밸류포션의 광고만 노출시키려는 경우

밸류포션의 스크립트와 광고를 노출하는 코드가 담긴 웹 페이지를 만드시고, 웹서버에 올려서 http://yourservice.com/ad.html 등과 같이 실제로 존재하는 url 을 만들어주세요.

그리고 웹뷰에는 html string 을 로드하거나, local file 을 localhost:// 등으로 로드하지 말고, 저 실제 웹 URL 을 로드해주세요.

광고가 노출되는 영역이 실제로 존재하는 웹페이지가 아닌 경우에 광고를 내려주지 않는 정책을 가진 DSP 들이 있어서, 보다 좋은 광고 효율을 위해 꼭 필요한 작업입니다.

2. 앱의 웹뷰에 원래부터 앱의 컨텐츠를 html 로 구성해서 제공해오고 있었는데, 그 웹뷰 안에 밸류포션의 광고를 노출시키려는 경우

바로 위의 경우와 비슷합니다. 실제 컨텐츠는 html string 이 웹뷰에 바로 로드되었더라도,

광고 노출 만큼은 iframe 안에 실제 URL 을 로드해서 그 영역에서 작동하도록 해야 합니다.

그리고 그에 따른 부가적인 작업들이 필요합니다.

1. 기초 작업

앱의 웹뷰에서 로딩되는 file://....html 파일을 (A) 라고 지칭하겠습니다.

기존엔 (A) 에서 바로 광고를 노출했지만, 광고 노출을 위한 중간 페이지인 (B) 를 새로 만들어주세요.

(B) 는 예를 들면 http://yourservice.com/ad.html 과 같은 URL 을 가질거고, 내용은 대략

<html>
<head>
    <meta name="viewport" content="width=device-width, user-scalable=no">
</head>
<body style="margin:0;padding:0;">
<div id="vp_wrapper"></div>
<p>hello</p>
<script type="text/javascript" src="https://campaigns-assets-vp.daumcdn.net/websdk/vp-latest.min.js"></script>
<script type="text/javascript">
    VP.init({clientId: "your_own_client_id"});
    var opts = {
        callback: function (filled) {
            console.log("ad callback : " + filled);
        }
    };
    VP.ad("vp_wrapper", "test_placement", 320, 100, opts);
</script>
</body>
</html>

위와 같을 겁니다. 위의 placement, width, height 는 제가 임의로 넣은 것이므로 원래 사용하시는 값으로 수정해서 사용해주세요.

2. 광고 영역을 iframe 으로..

이제 (A) 에서 광고를 노출하기 위해 만들어 놓았던 div 에 iframe 을 넣고, 그 iframe 이 (B) 의 url 을 가리키도록 해주세요.

그러면 (B) 에서 밸류포션의 광고 혹은 외부 광고가 정상적으로 노출되게 됩니다.

(이 때 (B) 의 url 에 사용되는 도메인을 저희에게 알려주셔야 저희가 미리 외부 광고 제공업체에 해당 도메인을 등록해서 광고가 노출되도록 설정할 수 있습니다)

3. 광고 callback 정상화 시키기

원래 VP.ad() 함수에 callback 을 넣으면 filled 라는 파라메터를 통해 성공 혹은 실패 여부를 알 수 있습니다.

하지만 위의 2단계까지만 진행하면 이 callback 이 작동하지 않게 됩니다.

이유는, (B) 아래쪽에 있는 iframe 들에서 광고 노출 여부에 관한 정보를 postMessage 함수를 통해 위쪽으로 올려보낼 때

가장 top frame 으로 올려보내고 있는데,

정상적인 케이스라면 가장 top frame 이 (B) 이기 때문에 잘 받아서 처리를 하겠지만,

지금은 그보다 더 위의 단계인 (A) 가 생겨버려서 (A) 로 그 메시지가 올라가버리고 유실되는 상황입니다.

그래서 해야 할 일은, (A) 에서 받은 메시지를 다시 (B) 로 되돌려주는 작업을 해주셔야 합니다.

(A) 에 다음과 같은 스크립트를 넣어주세요.

window.addEventListener('message', function (msg) {
  var iframe = document.querySelector("?????");
  iframe.contentWindow.postMessage(msg.data, "*");
});

위 스크립트의 document.querySelector("?????") 에서 적절한 selector 구문을 넣어주셔서 (B) 를 로딩하는 iframe 객체를 얻을 수 있게 해주세요.

여기까지 하시면 VP.ad() 의 callback 이 정상적으로 작동합니다.

4. 마지막 콜백 연동

VP.ad() 의 callback 은 정상적으로 작동하도록 고쳐놨지만, 정작 광고가 없는 경우에 (A) 에서 iframe 을 붙히면서 잡아놨던 영역은 여전히 하얗게 텅비게 될텐데요.

그걸 해결하기 위해서 (B) 에서 VP.ad() 를 호출하고 그 callback 에서 no ad 일 때는 (A) 에게 'iframe 을 없애' 라는 메시지를 전달해야 합니다.

var opts = {
    callback: function (filled) {
        console.log("ad callback : " + filled);
        if (!filled) {
            window.top.postMessage({
                action: "vp_ad_callback",
                filled: filled
            }, "*");
        }
    }
};
VP.ad("vp_wrapper", "test_placement", 320, 100, opts);

위와 같이 (B) 의 callback 에서 postMessage 를 통해 가장 상위 프레임에 메시지를 전달하도록 합니다.

그리고 가장 상위 프레임인 (A) 에서는

window.addEventListener('message', function (msg) {
  if (typeof(msg.data) == 'object' && msg.data.action == "vp_ad_callback") {
    if (!msg.data.filled) {
      var iframe = document.querySelector("?????");
      iframe.parentElement.removeChild(iframe);
    }
  } else {
    var iframe = document.querySelector("?????");
    iframe.contentWindow.postMessage(msg.data, "*");
  }
});

위와 같이 message 핸들링 부분을 수정해서 filled 변수 값에 따라 no ad 일 때 (A) 에서 광고 영역 자체를 숨길 수 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment