친구 초대 미션

친구 초대 미션은 [Client Admin]에서 프로모션 등록 시 설정할 수 있는 게임 미션입니다. 친구 초대 코드를 공유하여 신규 유저를 초대하면 초대한 유저와 초대받은 유저가 모두 리워드를 받을 수 있습니다. 언어별 설정 방법이 다르므로 개발 언어를 먼저 확인해 주세요!


Android

런처 Activity 내 선언된 Javascript InterfacesharedInviteLink 케이스를 추가합니다. 해당 코드 실행 시 OS 공유 시트를 노출 시키게 됩니다.

Interface function
Description

sharedInviteLink()

친구 초대 링크 공유 시 호출되는 함수

  • parameters

    • inviteCode - 발급된 초대코드

    • infoMessage - 안내 메세지

    • link - 초대 링크

// 블리피 Launcher Web <-> App 인터페이스 규격
class ExampleWebAppInterface(private val mContext: Context) {
    ...
    @JavascriptInterface
    fun sharedInviteLink(inviteCode: String, infoMessage: String, link: String) {
        // 초대 미션 공유
        val msg = "초대코드: $inviteCode\n$infoMessage\n$link"

        // Android Sharesheet를 통해 텍스트 콘텐츠를 공유합니다.
        val sendIntent: Intent = Intent().apply {
            action = Intent.ACTION_SEND
            putExtra(Intent.EXTRA_TEXT, msg)
            type = "text/plain"
        }
        
        val shareIntent = Intent.createChooser(sendIntent, null)
        mContext.startActivity(shareIntent)
    }
    ...
}

iOS

1. 링크 공유 시트 노출을 위한 구조체 생성

친구 초대 데이터를 전달 받고 iOS 앱 안에서 공유 시트를 노출 시키는 구조체를 생성합니다.

  • 공유 시트 노출을 위해 UIViewControllerRepresentable 사용

// ShareSheet.swift
import SwiftUI
import UIKit

// postMessage로 넘어오는 문자열을 JSON 형태로 파싱하기 위해 선언
// inviteCode - 발급된 초대코드
// infoMessage - Client Admin에 입력된 안내 메세지
// link - Client Admin에 입력된 초대 링크
struct InviteLink: Codable {
    var inviteCode: String
    var infoMessage: String
    var link: String
}

// 공유하기 시트
struct ShareSheet: UIViewControllerRepresentable {
    var items: [Any] // 공유할 항목

    func makeUIViewController(context: Context) -> UIActivityViewController {
        let controller = UIActivityViewController(activityItems: items, applicationActivities: nil)
        return controller
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
        // 업데이트가 필요할 경우 여기에 로직 추가
    }
}

2. WebView 구조체에서 Script Message 핸들링 추가

  • WKWebViewuserContentController 에서 친구 초대 링크 공유 관련 이벤트 처리 로직을 추가합니다.

  • 런처는 postMessage 문자열 데이터 형태로 전달하기 때문에 JSON 형태로 디코딩 과정이 필요합니다.

  • 디코딩된 데이터를 부모 contentView의 onSharedInviteLink 콜백 함수에 전달합니다.

// LauncherWebView.swift
import WebKit
import SwiftUI

struct LauncherWebView: UIViewRepresentable {
    ...
    // 초대 링크 처리할 콜백 함수
    let onSharedInviteLink: (String, String, String) -> Void
    
    class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
        ...
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            // 런처 친구초대 링크 공유 이벤트 수신 시 처리
            if message.name == "sharedInviteLink" {
                // 메시지의 body는 JSON.stringify 문자열이라 디코딩 필요
                if let jsonString = message.body as? String,
                   let jsonData = jsonString.data(using: .utf8) {
                    do {
                        // JSON 문자열을 ShareSheet 클래스 내 InviteLink 구조체로 디코딩
                        let inviteData = try JSONDecoder().decode(InviteLink.self, from: jsonData)
                        // 부모의 onSharedInviteLink 메서드 호출
                        parent.onSharedInviteLink(inviteData.inviteCode, inviteData.infoMessage, inviteData.link)
                    } catch {
                        print("Failed to decode JSON: (error.localizedDescription)")
                    }
                } else {
                    print("Failed to convert message body to String or Data")
                }
            }
        }
        ...
    }
    
    ...
    func makeUIView(context: Context) -> WKWebView {
        ...
        // javascript에서 전달하는 이벤트 등록
        contentController.add(context.coordinator, name: "sharedInviteLink")
        ...
    }
    ...
    
    static func dismantleUIView(_ uiView: WKWebView, coordinator: Self.Coordinator) {
        uiView.configuration.userContentController.removeScriptMessageHandler(forName: "sharedInviteLink")
    }
}

3. WebView가 포함된 ContentView 내에서 ShareSheet 클래스 호출

WebView 에 실행된 런처에서 친구 초대 링크 공유에 대한 이벤트가 발생하면 ContentView에 선언된 onSharedInviteLink 콜백 함수가 호출 됩니다.

  • shareItems state 값에 공유할 내용을 설정합니다.

  • isShowingShareSheet state 값 변경을 통해 ShareSheet 노출 여부를 결정합니다.

// LauncherContentView.swift
import SwiftUI
import WebKit

struct LauncherContentView: View {
    ...
    // ShareSheet를 표시할 때 사용할 상태 변수
    @State private var isShowingShareSheet = false
    @State private var shareItems: [Any] = []
    
    var body: some View {
        LauncherWebView(
            url: URL(string: "런처 URL"),
            // 친구 초대 링크 공유 이벤트 핸들링 처리
            onSharedInviteLink: { inviteCode, infoMessage, link in
                // 공유할 항목 설정
                shareItems = ["\(infoMessage)\n\n초대코드 : \(inviteCode)\n\n\(link)"]
                // ShareSheet를 표시하도록 상태를 변경
                isShowingShareSheet = true
            },
        )
        .navigationBarBackButtonHidden(true)
        // ShareSheet를 표시하는 시트 추가
        .sheet(isPresented: $isShowingShareSheet) {
            ShareSheet(items: shareItems)
        }
    }
    ...
    
}

Flutter

🤝 OS 공유 시트 사용 시

1. 링크 공유 패키지 설치

아래 패키지를 설치하면 링크 공유 기능을 수행할 수 있습니다.

  • 사용 패키지: share_plus

    • 해당 패키지는 플랫폼의 공유 대화상자를 통해 콘텐츠 공유를 가능하게 합니다.

flutter pub add share_plus

2. javascript handler 추가

런처를 사용하는 Screen 내 Javascript HandlersharedInviteLink 타입을 추가합니다. handlerName 값은 BlpLauncher 로 설정해야 합니다.

Type
Description

sharedInviteLink

친구 초대 링크 공유 시 호출

  • inviteCode - 발급된 초대코드

  • infoMessage - 안내 메세지

  • link - 초대 링크

// lib/screens/bleepy_screen.dart
InAppWebView(
    key: webViewKey,
    initialUrlRequest:
        URLRequest(url: WebUri(widget.launcherUrl)),
    initialSettings: options,
    onWebViewCreated: (controller) {
        webViewController = controller;
        // 자바스크립트 핸들러 추가
        controller.addJavaScriptHandler(handlerName: 'BlpLauncher', callback: (message) {
            final data = jsonDecode(message[0]);
            final key = data['type'];
            switch (key) {
                ...
                // 추가
                case "sharedInviteLink":
                    sharedInviteLink(data["inviteCode"], data["infoMessage"], data["link"]);
                    break;
                default:
                    break;
            }
        });
    }
)

share_plus 패키지를 통해서 친구 초대 링크를 공유하는 기능을 수행합니다.

// lib/screens/bleepy_screen.dart
import 'package:share_plus/share_plus.dart';
// 친구초대 링크 공유하기
void sharedInviteLink(String infoMessage, String inviteCode, String link) async {
    try {
        // 메세지 예시
        const msg = "초대코드: $inviteCode\n$infoMessage\n$link"
        await Share.share(msg);
    } catch (error: any) {
        // Share API error
    }
}

🤝 카카오톡 공유 기능 사용 시

Flutter에서 카카오톡 공유 기능을 위해 추가적인 조치가 필요합니다. 카카오톡 실행 URL 오픈 시 필요한 아래 패키지를 설치해주세요.

flutter pub add url_launcher

flutter_inappwebviewshouldOverrideUrlLoading 에 아래 로직을 추가합니다.

  • 기존 앱 존재 시 : 앱 실행

  • 미설치 시 : 스토어 이동

...
import 'dart:io';
import 'package:url_launcher/url_launcher.dart';

InAppWebView(
  ...
  shouldOverrideUrlLoading: (controller, navigationAction) async {
    var uri = navigationAction.request.url!;
  
    if(Platform.isIOS) {
      // iOS - kakao
      if (uri.toString().startsWith("kakaolink://")) {
        if (await canLaunchUrl(uri)) {
          // KakaoTalk is installed, launch it
          await launchUrl(uri, mode: LaunchMode.externalApplication);
        } else {
          // KakaoTalk is not installed, redirect to App Store
          const appStoreUrl = "https://apps.apple.com/app/id362057947";
          await launchUrl(Uri.parse(appStoreUrl), mode: LaunchMode.externalApplication);
        }
  
        return NavigationActionPolicy.CANCEL;
      }
    }
  
    if(Platform.isAndroid) {
      // AOS - kakao
      if (uri.toString().startsWith("intent:")) {
        final fallbackUrl = Uri.tryParse(uri.queryParameters['browser_fallback_url'] ?? '');
  
        if (fallbackUrl != null && await canLaunchUrl(fallbackUrl)) {
          await launchUrl(fallbackUrl, mode: LaunchMode.externalApplication);
        } else {
          final kakaolinkUrl = uri.toString().replaceFirst("intent:", "");
  
          try {
            await launchUrl(Uri.parse(kakaolinkUrl), mode: LaunchMode.externalApplication);
          } catch (e) {
            print("Failed to launch KakaoLink URL: $e");
  
            // KakaoTalk is not installed, redirect to Google Play Store
            const appStoreUrl = "https://play.google.com/store/apps/details?id=com.kakao.talk";
            await launchUrl(Uri.parse(appStoreUrl), mode: LaunchMode.externalApplication);
          }
        }
        return NavigationActionPolicy.CANCEL;
      }
    }
  
    return NavigationActionPolicy.ALLOW;
  },
  ...
)

iOS 환경의 경우 Info.plist 파일 내 kakaolink 값을 추가해주세요.

// ios/Runner/Info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>kakaolink</string>
</array>

React Native

🤝 OS 공유 시트 사용 시

기본 제공되는 Share API 를 통해 친구 초대 링크를 공유하는 기능을 수행합니다. WebViewonMessage 함수 내에 해당 케이스를 추가합니다.

Type
Description

sharedInviteLink

친구 초대 링크 공유 시 호출되는 함수

  • inviteCode - 발급된 초대코드

  • infoMessage - 안내 메세지

  • link - 초대 링크

// src/screens/launcher/Launcher.tsx
import {WebView, WebViewMessageEvent} from 'react-native-webview';
export default function Launcher() {
    // WebView에서 보내온 메세지 처리
    const onMessage = (e: WebViewMessageEvent) => {
        const {type, inviteCode, infoMessage, link}
            = JSON.parse(e.nativeEvent.data);
        switch (type) {
            ...
            // 추가
            case 'sharedInviteLink':
                sharedInviteLink(inviteCode, infoMessage, link);
                break;
            }
        };
        return (
            <View>
                <WebView
                    ...
                    onMessage={onMessage}
                    ...
                />
            </View>
        );
    }
}

Share API 를 통해서 친구 초대 링크를 공유하는 기능을 수행합니다.

// src/screens/launcher/Launcher.tsx
import { Share } from 'react-native';
// 친구초대 링크 공유하기
const sharedInviteLink = async (inviteCode: string, infoMessage: string, link: string) => {
    try {
        // 메세지 예시
        const msg = '{infoMessage}\n초대코드 : {inviteCode}\n\n{link}'
        await Share.share({
            message: msg,
        });
    } catch (error: any) {
        // Share API error
    }
};

🤝 카카오톡 공유 기능 사용 시

React Native 앱으로 개발된 경우, 카카오톡 공유 기능을 위해 추가적인 조치가 필요합니다. 올바른 URL 스키마 처리를 위해 아래 패키지를 설치해주세요.

npm install react-native-url-polyfill

패키지 설치가 완료되면 WebView 관련 설정을 추가합니다.

  • originWhiteList

  • domStorageEnabled

  • onShouldStartLoadWithRequest

handleShouldStartLoadWithRequest 메서드 내에서 AOS, iOS 플랫폼 별 처리를 진행합니다. 앱이 설치된 경우는 앱을 실행하고, 앱 미설치 시 스토어로 이동하게 됩니다.

import 'react-native-url-polyfill/auto';
import {Alert, Linking, Platform} from 'react-native';

export default function Launcher() {
  ...
  const handleShouldStartLoadWithRequest = (event: any): boolean => {
    const url = event.url;

    // 1. Android intent scheme process
    if (Platform.OS === 'android' && url.startsWith('intent:')) {
      const handleAndroidIntent = async () => {
        try {
          const kakaoUrl = url.replace('intent:', '');
          const fallbackUrl = new URLSearchParams(kakaoUrl).get(
            'browser_fallback_url',
          );
          const canOpen = await Linking.canOpenURL(kakaoUrl);

          if (canOpen) {
            // run KakaoTalk
            await Linking.openURL(kakaoUrl);
          } else if (fallbackUrl) {
            // move to fallback URL
            await Linking.openURL(fallbackUrl);
          } else {
            // move to Google Play Store
            const playStoreUrl =
              'https://play.google.com/store/apps/details?id=com.kakao.talk';
            await Linking.openURL(playStoreUrl);
          }
        } catch (error) {
          console.error('Error handling intent URL:', error);
        }
      };

      handleAndroidIntent();
      return false; // WebView load stop
    }
  
    // 2. IOS kakaolink process
    if (Platform.OS === 'ios' && url.startsWith('kakaolink://')) {
      const handleIosKakaoLink = async () => {
        try {
          const canOpen = await Linking.canOpenURL(url);
          if (canOpen) {
            await Linking.openURL(url);
          } else {
            const appStoreUrl = 'https://apps.apple.com/app/id362057947';
            await Linking.openURL(appStoreUrl);
          }
        } catch (error) {
          console.error('Error handling Kakao URL on iOS:', error);
        }
      };
      handleIosKakaoLink();
      return false; // WebView load stop
    }

    return true; // WebView load allow
  };
  ...

  return (
    <View>
      <WebView
        ref={webviewRef}
        source={{
          uri: route.params.url,
        }}
        originWhitelist={['kakaolink', 'intent', 'http', 'https']}
        javaScriptEnabled={true}
        domStorageEnabled={true}
        cacheEnabled={false}
        // 카카오톡 공유하기 처리
        onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
      />
    </View>
  );
}

Android 추가 처리

AndroidManifest.xml 파일 내 수정이 필요합니다.

  • <queries></queries> 선언을 통해 kakaolink 외부 intents를 명시합니다.

  • <application><activity> 태그 내 android:exported 속성 값을 true로 설정합니다.

// android/app/src/main/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <!-- Declare queries for external intents -->
    <queries>
      <!-- KakaoTalk-specific intent -->
      <intent>
          <action android:name="android.intent.action.VIEW" />
          <data android:scheme="kakaolink" />
      </intent>
    </queries>

    <application
      ...
      android:exported="true">
      <activity
        ...
        android:exported="true">
        ...
      </activity>
    </application>
</manifest>

```

iOS

Info.plist 파일 내 kakaolink URL 스키마 추가가 필요합니다.

// ios/{projectName}/Info.plist
...
<!-- KakaoTalk URL Scheme Support -->
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>kakaolink</string>
</array>
...

Web

Web 도메인의 서비스인 경우 블리피 런처에서 기본적으로 Web Share API사용하게 됩니다.

친구 초대 미션 사용 시 임베디드 코드 내 유저의 회원가입 시점 추가 전달이 필요합니다.

Type
Description

signUpAt

신규 유저 판단을 위한 회원가입 일시를 전달해야 합니다.

  • timestamp 형식

  • ex) 1717143687

<!-- {signUpAt} 영역에 로그인된 회원의 회원가입 일시 전달 (timestamp) -->
<div style="height: 100%; max-width: 960px">
  <iframe
    src="https://web-launcher.bleepy.io?userKey={userKey}&secretKey={secretKey}&signUpAt={signUpAt}"
    style="overflow: hidden; width: 100%; height: 100%"
    allowfullscreen
    sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-top-navigation"
    id="bleepy-iframe"
    allow="web-share;clipboard-read; clipboard-write"
  />
</div>

개발에 대한 추가 설명이 더 필요하신가요?

"[Client Admin] 로그인 → 오른쪽 하단 채널톡 위젯" 클릭 후 개발 카테고리에 문의 남겨주시면 기술 개발팀에서 확인 후 연락드리겠습니다.

Last updated