모바일 게임에서 푸시 통지 서비스는 게임 유저의 리텐션(잔존율) 관리에 중요한 수단입니다. Hive는 다양한 경로로 유저에게 알림을 보낼 수 있도록 푸시 서비스를 제공하는데, 모바일 게임의 리텐션을 올리기 위한 마케팅용 광고 푸시를 제공할 뿐만 아니라 Google의 FCM, Apple의 APNS을 지원합니다. 언어별 시간대 설정 역시 가능해 글로벌 서비스에 대응하고 있습니다. 푸시 정책은 KISA 및 Google Play Store 마켓 가이드라인을 준수하고 있습니다.
푸시 모듈 변경 사항 (1.8.0+)
- 정보통신망법의 규제 강화에 따라 푸시를 타입별로 분리하여, 유저가 원하는 정보를 푸시로 발송 받을 수 있는 선택의 권한을 제공해야 한다.
- 게임 내 알림(푸시) 설정 화면을 통해 선택된 알림 on, off 정보를 푸시 모듈 서버에 저장한다.
- 저장된 정보 값을 토대로 백오피스에서 발송되는 푸시의 경우 유저의 설정 정보에 따라 푸시를 발송 또는 미 발송 처리를 진행한다.
- 푸시 종류는 게임 알림, 공지 알림, 야간 알림으로 구분하며 이를 모두 포함하는 모든 알림은 OS에 따라 구분하여 적용한다. (iOS의 경우, 푸시 설정 화면에 모든 알림은 구성하지 않는다.)
- 야간 알림의 정책은 한국에만 해당하므로 게임 언어가 타 국가 언어로 플레이 할 경우 제외한다.
- 각 게임 스튜디오에서 가이드를 참조하여 UI 및 인터랙션을 구성해야 한다.
푸시 v4 (1.16.4+)
- 푸시 토큰에 계정 정보와 게임 서버 정보를 수집하여 계정 기반 푸시와 게임 서버 별 푸시 발송이 가능하다.
- 푸시를 발송하려면 먼저 각 푸시 발송 서비스로부터 토큰 값을 수집한다. 기존에는 단말(DID) 기준 형태로, 하나의 단말에 하나의 토큰이 대응되었고 각 단말 별로 ‘공지 알림/야간 알림'(notice/night) 설정이 저장되었다.
새로운 푸시 v4 시스템(Push v4)을 활용하면 계정 기반(VID) 수집이 가능하고, 각 유저별 ‘공지 알림/야간 알림’ 설정을 신규 SDK에 저장한다. 토큰은 가장 마지막에 접속한 단말의 토큰만 유효하다. -
기존에 수집된 데이터 혹은 계정 기반 지원이 어려운 경우 단말기(DID) 기반 푸시 발송이 지원된다. (하위 호환)
Hive 플랫폼의 변경사항
푸시 클라이언트
- 게임 클라이언트에서 푸시 설정 정보를 푸시 모듈에 요청 시 결과값을 리턴 해주는 API 추가
- 게임 푸시 설정 화면 내 변경된 모든 알림, 공지 알림, 야간 알림에 대한 정보 값을 받아 저장하는 API 추가
푸시 서버
- 푸시 클라이언트에 저장된 정보 값을 푸시 서버로 받아 저장하는 API 추가
- 푸시 서버에 저장된 값을 기반으로 푸시 발송 프로세스를 변경
푸시 설정
유저는 앱 설치 후 초기 실행시 약관 동의를 받게 되고 마케팅용 광고 푸시를 받겠다는 동의 절차를 수행하게 됩니다. 하지만 푸시 알림 서비스를 원하지 않는 유저를 위해서 게임 설정 창에서 푸시 알림을 켜고 끌 수 있는 기능을 제공해야 합니다.
푸시 알림 종류
푸시 알림에 대한 수신여부를 모든 알림, 게임 알림, 공지 알림, (공지)야간 알림 등으로 구분하여 설정할 수 있어야 합니다.
모든 알림 |
|
게임 알림 |
|
공지 알림 |
|
(공지)야간 알림 |
|
푸시 설정 정책
1. 여러분의 게임 내에 유저가 푸시 알림을 활성화하거나 비활성화할 수 있는 메뉴를 제공하세요.
- 푸시 설정 메뉴 구성
- 게임언어가 한국어일 경우 푸시 알림 설정 메뉴의 예시. (Android와 iOS를 포함하는 모든 단말에서 반드시 야간 알림 설정을 노출해야 합니다.)
- 게임언어가 한국어가 아닐 경우 푸시 알림 설정 메뉴의 예시. (Android와 iOS를 포함하는 모든 단말에서 야간 알림 설정을 노출하지 않습니다.)
푸시 알림 설정 변경 시에는 토스트 팝업을 2초 동안 표시해야 합니다.
- 푸시 알림 팝업을 노출하는 경우
- 유저가 접속한 지역이 한국일 경우
- 모든 알림, 공지 알림, 야간 알림 변경 발생 시(게임 알림의 경우 토스트 팝업을 띄우지 않습니다.)
- 푸시 알림 팝업 문구
- 모든 알림 동의 활성화: [회사명] 2016.05.01 알림 수신에 동의하셨습니다.
- 공지 알림 동의 비활성화: [회사명] 2016.05.01 알림 수신에 거부하셨습니다.
- 야간 알림 동의 활성화: [회사명] 2016.05.01 야간 알림 수신에 동의하셨습니다.
- 토스트 팝업 예시
- 푸시 설정 안내 문구를 제공하세요.
푸시 설정 메뉴에서 수신을 활성화했는데도 푸시가 수신되지 않으면 단말기 설정 메뉴에서 알림 설정을 확인하라는 안내를 제공해 주세요. 단말기에서 여러분 게임 앱이 발생하는 알림을 차단하면 푸시 알림을 제대로 받을 수 없습니다.
2. Hive 콘솔을 통해 발송하는 공지의 설정은 Hive API를 적용하세요.
- Hive 콘솔을 통해 발송하는 공지 설정의 종류는 공지 알림과 (공지)야간 알림 2가지가 있습니다.
- 두 알림 옵션은 Hive 서버에 설정된 값을 조회해서 반영해야 합니다.
- 두 알림 옵션에 변경이 발생 시 Hive 서버에 변경 내용을 전달해야 합니다.
- 자세한 내용은 Push Description를 참고하세요.
3. 야간 알림 정책을 적용하세요.
야간 알림은 공지 알림에 속한 알림입니다. 야간 알림 설정 메뉴를 구현할 때 다음 정책을 적용하세요.
- 공지 알림을 비활성화하면 야간 알림도 자동으로 비활성화해야 합니다.
- 공지 알림이 비활성화일 때 야간 알림을 활성화할 수 없어야 합니다.
- 공지 알림을 활성화해도 야간 알림을 자동으로 활성화하지 않아야 합니다.
Provisional Authorization
Hive SDK v1.17.0부터 iOS 12에 추가된 Provisional Authorization 기능을 지원합니다.
Hive SDK 약관 동의 이후 노출되던 기존의 푸시 전송 동의 팝업이 더 이상 노출되지 않기 때문에 앱을 실행한 유저들은 모든 푸시 메시지를 기본으로 수신합니다. 이 때 푸시 수신 여부는 유저가 받은 푸시 메시지나 앱 설정에서 선택할 수 있습니다. 유저가 수동으로 푸시 전송에 동의하기 전까지는 푸시를 수신해도 알림음이 울리지 않으며, 팝업 또한 노출되지 않습니다.
iOS 12 미만 버전을 사용하는 기기에서는 기존과 동일하게 초기화 이후 푸시 전송 동의 팝업이 노출됩니다.
명시적 푸시 권한 요청
iOS
Hive SDK v1.19.1부터 게임에서 필요한 경우 유저에게 명시적으로 푸시 전송 동의 팝업을 노출할 수 있는 API가 제공됩니다. 유저가 푸시 전송 동의 여부를 설정하지 않은 상태라면 Push 클래스의 RequestPermission()
메서드를 호출하여 유저에게 푸시 전송 동의 팝업을 노출할 수 있습니다. Provisional Authorization을 활용하여 Hive SDK 약관 동의에 대한 묵시적 권한 동의를 받은 경우라도 푸시 전송 동의 팝업이 노출되며, 푸시 전송에 동의한 유저에게는 명시적으로 푸시 메시지가 노출됩니다. 단, C2SModuleSocial_Initialize API를 호출하여 적어도 한 번은 초기화를 완료한 후에 적용할 수 있습니다.
Android
Hive SDK v1.20.2.2부터 targetSDK 33 이상으로 빌드한 앱이 Android 13 이상 단말에서 실행될 때, 게임에서 필요한 경우 유저에게 명시적으로 푸시 권한 팝업을 노출할 수 있는 C2SModulePush_RequestPermission API가 제공됩니다. 단, C2SModuleSocial_Initialize API를 호출하여 적어도 한 번은 초기화를 완료한 이후에만 적용할 수 있습니다. C2SModulePush_RequestPermission API 호출 시점은 초기화 직후 또는 초기화 이후 게임에서 알림 권한 요청이 필요한 시점에 호출하시기 바랍니다.
다음은 푸시 권한 요청을 위한 예제 코드입니다.
1 2 3 4 5 6 7 8 9 |
C2SModule.Parameter arg = new C2SModule.Parameter (); C2SModule.Error error; if (!C2SModule.Push.RequestPermission (arg, out error)) { // Error 처리 } // 별도의 콜백 없음 |
1 2 3 4 5 6 7 8 9 10 |
Json::Value arg; C2SModule_Cocos2dx::C2SModuleError error; if (!C2SModulePush_RequestPermission(strJSON.c_str(), &error) == false) { // Error 처리 } // 별도의 콜백 없음 |
1 2 3 4 5 6 7 8 9 10 |
NSDictionary* arg = @{}; NSError* error; if (![C2SModulePush RequestPermission:nil error:&error]) { // Error 처리 } // 별도의 콜백 없음 |
Push Description
C2SModulePush_Description
유저가 광고성 푸시(공지 알림) 및 야간 푸시(야간 알림)에 대해 수신 여부를 설정할 수 있으며, 설정된 상태에 따라 푸시가 발송된다.
IOS에도 useNotice(공지 알림)와 useNight(야간알림)의 적용이 가능하다. (1.8.0+ )
argument name | type | desc | platform | 비고 |
---|---|---|---|---|
usePush | bool | (optional) 푸시의 사용 유무를 설정한다. (Local & GCM Push On/off 적용됨) |
android | |
sound | bool | (optional) 푸시 알림의 소리 on / off | android | 1.19.6- (1.19.6 미만만 지원) |
vibrate | bool | (optional) 푸시 알림의 진동 on / off | android | 1.19.6- |
operationGCM | bool | (optional) 앱이 동작중일때 GCM 푸시 알림 여부 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
android | |
operationRemote | bool | (optional) 앱이 동작중일때 Remote 푸시 알림 여부 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
iOS | |
operationLocal | bool | (optional) 앱이 동작중일때 로컬 푸시 알림 여부 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | |
useNotice | bool | (optional) 광고성 푸시 알림 여부 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | 1.8.0+ |
useNight | bool | (optional) (pm 9:00 ~ am 8:00) 야간 푸시 알림 여부 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | 1.8.0+ |
Result – Api.Push_Description
Local 푸시에 등록된 push id 리스트
argument name | type | desc | platform | 비고 |
---|---|---|---|---|
usePush | bool | 푸시의 사용 유무의 상태 | android | |
sound | bool | 푸시 알림의 소리 on / off 상태 | android | 1.19.6- (1.19.6 미만만 지원) |
vibrate | bool | 푸시 알림의 진동 on / off 상태 | android | 1.19.6- |
operationGCM | bool | 앱이 동작중일때 GCM 푸시 알림 여부 상태 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
android | |
operationRemote | bool | 앱이 동작중일때 Remote 푸시 알림 여부 상태 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
android | |
operationLocal | bool | 앱이 동작중일때 로컬 푸시 알림 여부 상태 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | |
useNotice | bool | 광고성 푸시 알림 여부 상태 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | 1.8.0+ |
useNight | bool | (pm 9:00 ~ am 8:00 Local Time) 야간 푸시 알림 여부 상태 키를 추가하지 않고 호출하면 현재 설정 상태 값을 받아온다. |
all | 1.8.0+ |
샘플 코드
public static bool C2SModule.Push.Description(C2SModule.Parameter arg, out C2SModule.Error error,
C2SModule.CompletionHandler completionHandler);
– 푸시의 변경된 상태 정보를 가져오거나, 서버에 전송한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
C2SModule.Parameter arg = new C2SModule.Parameter(); arg["usePush"] = true; arg["sound"] = true; arg["vibrate"] = true; arg["operationGCM"] = true; arg["operationLocal"] = true; /* 1.8.0 추가 "useNotice", "useNight" */ arg["useNotice"] = true; arg["useNight"] = true; if(!C2SModule.Push.Description(arg, out error)) { CommonModuleDelegate.Log(error); } /* 1.8.0 추가. 모든 알림, 공지 알림, 야간 알림 수신동의 설정 */ C2SModule.Parameter arg = new C2SModule.Parameter(); // 공지 알림 on 야간 알림 on arg["useNotice"] = true; arg["useNight"] = true; // 공지 알림 on 야간 알림 off arg["useNotice"] = true; arg["useNight"] = false; // 공지 알림 off 야간 알림 off arg["useNotice"] = false; arg["useNight"] = false; // Push 수신동의 설정 if(!C2SModule.Push.Description(arg, out error)) { CommonModuleDelegate.Log(error); } /* 1.8.0 추가. 수신동의 정보 가져오기*/ C2SModule.Parameter arg = new C2SModule.Parameter(); //수신동의 정보 가져오기 if(!C2SModule.Push.Description(arg, out error,delegate (C2SModule.Parameter reArg, C2SModule.Error userError) { // 공지 알림 스위치 on/off noticeSwitchOn = reArg["useNotice"] as bool; // 야간 알림 스위치 on/off nightSwitchOn = reArg["useNight"] as bool; })) { logController.showErrorLog (error); } |
– 푸시의 변경된 상태 정보를 가져오거나, 서버에 전송한다,
– 델리게이트에 C2SModuleApi_PushDescription 콜백이 내려온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
TSharedPtr arg = MakeShareable(new FJsonObject); /* 1.8.0 추가 "useNotice", "useNight" */ arg->SetBoolField(TEXT("useNotice"),true); arg->SetBoolField(TEXT("useNight"),false); /* Android C2S_PushDescription */ arg->SetBoolField(TEXT("usePush"),true); arg->SetBoolField(TEXT("sound"),true); arg->SetBoolField(TEXT("vibrate"),true); arg->SetBoolField(TEXT("operationGCM"),true); arg->SetBoolField(TEXT("operationLocal"),true); FString OutputString; TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&OutputString); FJsonSerializer::Serialize(arg.ToSharedRef(), Writer); FHiveSDKModule::Get().HiveSDKUEPush_Description(OutputString); /* 1.8.0 추가. 수신동의 정보 가져오기 */ FString empty; //수신동의 정보 가져오기 FHiveSDKModule::Get().HiveSDKUEPush_Description(empty); … //공통 델리게이트 콜백 void SampleProject::ResultDelegate(C2SModuleApi api, const char* json, C2SModuleErrorCode code, const char* message){ switch(api) { case C2SModuleApi_PushDescription: { TSharedPtr arg; TSharedRef< TJsonReader<> > reader = TJsonReaderFactory<>::Create( json ); if ( FJsonSerializer::Deserialize( reader, arg ) == false ){ return; } // 공지 알림 스위치 on/off noticeSwitchOnOff = arg->GetBoolField(TEXT("useNotice")); // 야간 알림 스위치 on/off nightSwitchOnOff = arg->GetBoolField(TEXT("useNight")); }break; } } |
+(BOOL)Description:(id)arg error:(NSError**)error completionHandler:(void(^)(NSDictionary *, NSError*))
completionHandler;
– 푸시의 변경된 상태 정보를 가져오거나, 서버에 전송한다,
-1.8.0이후로 IOS에서도 광고성 푸시와 야간 푸시의 동의 여부를 설정할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
NSError* error = nil; /* 1.8.0 추가 "useNotice", "useNight" */ NSDictionary* arg = @{@"useNotice":@true, @"useNight":@true}; [C2SModulePush Description:arg error:&error]; /* 1.8.0 추가. 공지 알림, 야간 알림 수신 동의 설정 */ NSError* error = nil; // 공지 알림 on 야간 알림 on NSDictionary* arg = @{@"useNotice":@true, @"useNight":@true}; // 공지 알림 on 야간 알림 off NSDictionary* arg = @{@"useNotice":@true, @"useNight":@false}; // 공지 알림 off 야간 알림 off NSDictionary* arg = @{@"useNotice":@false, @"useNight":@false}; // Push 수신동의 설정 [C2SModulePush Description:arg error:&error completionHandler:^(NSDictionary *resultArg, NSError* resultError){ // todo }]; /* 1.8.0 추가. 수신 동의 정보 가져오기 */ NSError* error = nil; NSDictionary* arg = [NSDictionary dictionary]; //수신동의 정보 가져오기 [C2SModulePush Description:arg error:&error completionHandler:^(NSDictionary *resultArg, NSError* resultError){ // 공지 알림 스위치 on/off [_noticeSwitch setOn:[resultArg[@"useNotice"] boolValue]]; // 야간 알림 스위치 on/off [_nightSwitch setOn:[resultArg[@"useNight"] boolValue]]; }]; |
C2SModule_Cocos2dx::C2SModuleCompletionHandler* completionHandler = NULL);
– 푸시의 변경된 상태 정보를 가져오거나, 서버에 전송한다,
– 델리게이트에 C2SModuleApi_PushDescription 콜백이 내려온다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
Json::Value arg; arg["usePush"] = true; arg["sound"] = true; arg["vibrate"] = true; arg["operationGCM"] = true; arg["operationLocal"] = true; /* 1.8.0 추가 "useNotice", "useNight" */ arg["useNotice"] = true; arg["useNight"] = true; Json::StyledWriter writer; std::string strJSON = writer.write(arg); C2SModule_Cocos2dx::C2SModuleError error; if( ::C2SModulePush_Description(strJSON.c_str(), &error) == false ) { CCLog( "====>>>> Push Description error (%s)", error.get()->GetMessage() ); } /* 1.8.0 추가. 모든 알림, 공지 알림, 야간 알림 수신동의 설정 */ Json::Value arg; // 공지 알림 on 야간 알림 on arg["useNotice"] = true; arg["useNight"] = true; // 공지 알림 on 야간 알림 off arg["useNotice"] = true; arg["useNight"] = false; // 공지 알림 off 야간 알림 off arg["useNotice"] = false; arg["useNight"] = false; Json::StyledWriter writer; std::string strJSON = writer.write(arg); C2SModule_Cocos2dx::C2SModuleError error; //Push 수신동의 설정 if(::C2SModulePush_Description(strJSON.c_str(), &error) == false ) { CCLog( "=> Push Description error (%s)", error.get()->GetMessage() ); } /* 1.8.0 추가. 수신동의 정보 가져오기 */ Json::Value arg; Json::StyledWriter writer; std::string strJSON = writer.write(arg); C2SModule_Cocos2dx::C2SModuleError error; //수신동의 정보 가져오기 if(::C2SModulePush_Description(strJSON.c_str(), &error) == false ){ CCLog( "=> Push Description error (%s)", error.get()->GetMessage() ); } … //공통 델리게이트 콜백 void SampleProject::ResultDelegate(C2SModuleApi api, const char* json, C2SModuleErrorCode code, const char* message){ case C2SModuleApi_PushDescription:{ Json::Value arg; Json::Reader reader; if( reader.parse(json, arg) == false ) { CCLog( "=> C2SModuleApi_PushDescription parse failed"); return; } // 공지 알림 스위치 on/off noticeSwitchOnOff = arg["useNotice"].asBool(); // 야간 알림 스위치 on/off nightSwitchOnOff = arg["useNight"].asBool(); }break; } |