본문으로 건너뛰기

Sign in with Google 환경 설정 (v1 호환)

  • 구글 로그인 웹 인증 방식 입니다. google v1 방식 과 호환 가능 합니다.
  • 구글 계정 활용을 위하여 환경설정을 합니다.

구글 사용자 OAuth 클라이언트 ID 등록

  • Google Cloud Platform > 사용자 인증 정보 > 사용자 인증 정보 만들기 > OAuth 클라이언트 ID 선택합니다.
  • 어플리케이션 유형을 Wep application 로 선택 합니다.
  • 승인된 리디렉션 URI : 다음 2개 URL을 모두 추가 합니다.
    • https://www.playnanoo.com/oauth2redirect.html (기존 버전 호환용)
    • https://www.playnanoo.com/oauth2redirect.html?v=2 (신규 버전)
  • 추가 된 OAuth 클라이언트 ID의 클라이언트 ID 정보를 확인 합니다.

구글 사용자 OAuth 클라이언트 ID 등록 (iOS)

  • Google Cloud Platform > 사용자 인증 정보 > 사용자 인증 정보 만들기 > OAuth 클라이언트 ID 선택합니다.
  • 어플리케이션 유형을 iOS 로 선택 후 기타 정보를 입력 후 완료 합니다.
  • 추가 된 OAuth 클라이언트 ID 선택 후 PLIST 정보를 다운로드 합니다.
  • 다운받은 PLIST 파일명을 GoogleService-Info.plist 로 변경합니다.

구글 게임 SDK 등록

  • 구글 게임 Unity Package 를 다운로드 합니다.
  • 구글 게임 Unity Package 를 프로젝트에 Import 합니다.
  • 구글 게임은 iOS는 필수이며, 안드로이드는 선택 입니다.

구글 게임 유니티 패키지 다운로드

구글 인증 SDK 등록

  • 구글 인증 Unity Package 를 다운로드 합니다.
  • 구글 인증 Unity Package 를 프로젝트에 Import 합니다.
  • 구글 인증 Unity Package Import 시 충돌 및 에러방지를 위하여 일부 파일을 제거 합니다.

구글 인증 유니티 패키지 다운로드

xCode 설정 (iOS)

  • xCode 프로젝트는 CocoaPods 프로젝트로 실행합니다.
  • Google Cloud Platform 에서 다운로드 한 GoogleService-Info.plist 을 프로젝트에 추가 합니다.

  • CocoaPods 의 GoogleSignIn 버전 정보를 변경합니다. 일부 버전이 구글 인증 SDK와 충돌이 발생 될 수 있습니다.
  • pod 정보 변경 후 pod install 을 실행하여 해당 서비스의 설치를 완료 합니다.

  • Google Cloud Platform 의 iOS URL 스키마 정보를 URLType 의 URL Schemes에 등록합니다.
  • URL Schemes 정보는 GameService-Info.plist 의 REVERSED_CLIENT_ID 값입니다.

Unity 딥링크 등록 절차 Android

딥링크 scheme 변경 안내

기존 mygame 공유 scheme은 deprecated 되었습니다. 동일 scheme을 사용하는 앱이 여러 개 설치된 경우, 인증 완료 후 잘못된 앱으로 복귀하는 문제가 발생합니다. SDK가 PlayNANOO 콘솔에 등록된 Game ID를 scheme으로 자동 사용하므로, 별도의 scheme 설정이 필요 없습니다.

Game ID는 PlayNANOO 콘솔 > 게임&앱 설정 페이지의 "게임&앱 ID" 항목에서 확인할 수 있습니다.

  • Assets/Editor/ 폴더 안에 AndroidDeepLinkPostProcessor.cs 파일을 생성 하고 아래 내용을 추가 합니다.
  • SDK가 PlayNANOO Settings에 등록된 Game ID를 딥링크 scheme으로 자동 사용합니다.
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Android;
using System.IO;
using System.Xml;

public class AndroidDeepLinkPostProcessor : IPostGenerateGradleAndroidProject
{
public int callbackOrder => 999;

public void OnPostGenerateGradleAndroidProject(string path)
{
string manifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");

if (!File.Exists(manifestPath))
{
UnityEngine.Debug.LogError("[DeepLink] AndroidManifest.xml not found!");
return;
}

// PlayNANOOSettings에서 game_id 로드
PlayNANOOSettings settings = AssetDatabase.LoadAssetAtPath<PlayNANOOSettings>(
"Assets/PlayNANOO/Resources/PlayNANOOSettings.asset");

if (settings == null || string.IsNullOrEmpty(settings.gameID))
{
UnityEngine.Debug.LogError("[DeepLink] PlayNANOOSettings not found or gameID is empty!");
return;
}

string gameId = settings.gameID;

XmlDocument manifest = new();
manifest.Load(manifestPath);

XmlNamespaceManager nsmgr = new(manifest.NameTable);
nsmgr.AddNamespace("android", "http://schemas.android.com/apk/res/android");

AddDeepLinkIntentFilter(manifest, nsmgr, gameId);
AddInternetPermission(manifest, nsmgr);
AddBrowserQueries(manifest, nsmgr);

manifest.Save(manifestPath);
UnityEngine.Debug.Log($"[DeepLink] AndroidManifest 설정 완료 (scheme: {gameId})");
}

private void AddDeepLinkIntentFilter(XmlDocument manifest, XmlNamespaceManager nsmgr, string gameId)
{
XmlNode applicationNode = manifest.SelectSingleNode("/manifest/application", nsmgr);
XmlNode activityNode = applicationNode?.SelectSingleNode(
"activity[@android:name='com.unity3d.player.UnityPlayerActivity']", nsmgr);
activityNode ??= applicationNode?.SelectSingleNode(
"activity[contains(@android:name, 'UnityPlayerActivity')]", nsmgr);

if (activityNode == null)
{
UnityEngine.Debug.LogError("[DeepLink] UnityPlayerActivity not found!");
return;
}

// 이미 game_id scheme 딥링크가 있는지 확인
XmlNodeList intentFilters = activityNode.SelectNodes("intent-filter", nsmgr);
foreach (XmlNode filter in intentFilters)
{
XmlNode dataNode = filter.SelectSingleNode(
$"data[@android:scheme='{gameId}']", nsmgr);
if (dataNode != null)
{
UnityEngine.Debug.Log($"[DeepLink] 딥링크 이미 등록됨 (scheme: {gameId})");
return;
}
}

// 딥링크 Intent Filter 생성
XmlElement intentFilter = manifest.CreateElement("intent-filter");

XmlElement action = manifest.CreateElement("action");
action.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.intent.action.VIEW");
intentFilter.AppendChild(action);

XmlElement categoryDefault = manifest.CreateElement("category");
categoryDefault.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.intent.category.DEFAULT");
intentFilter.AppendChild(categoryDefault);

XmlElement categoryBrowsable = manifest.CreateElement("category");
categoryBrowsable.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.intent.category.BROWSABLE");
intentFilter.AppendChild(categoryBrowsable);

XmlElement data = manifest.CreateElement("data");
data.SetAttribute("scheme", "http://schemas.android.com/apk/res/android", gameId);
intentFilter.AppendChild(data);

activityNode.AppendChild(intentFilter);
UnityEngine.Debug.Log($"[DeepLink] 딥링크 Intent Filter 추가 완료 (scheme: {gameId})");
}

private void AddInternetPermission(XmlDocument manifest, XmlNamespaceManager nsmgr)
{
XmlNode existingPermission = manifest.SelectSingleNode(
"/manifest/uses-permission[@android:name='android.permission.INTERNET']", nsmgr);
if (existingPermission != null) return;

XmlElement permission = manifest.CreateElement("uses-permission");
permission.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.permission.INTERNET");

XmlNode manifestNode = manifest.SelectSingleNode("/manifest");
manifestNode?.AppendChild(permission);
}

private void AddBrowserQueries(XmlDocument manifest, XmlNamespaceManager nsmgr)
{
XmlNode existingQueries = manifest.SelectSingleNode("/manifest/queries", nsmgr);
if (existingQueries != null) return;

XmlElement queries = manifest.CreateElement("queries");
XmlElement intent = manifest.CreateElement("intent");

XmlElement action = manifest.CreateElement("action");
action.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.intent.action.VIEW");
intent.AppendChild(action);

XmlElement category = manifest.CreateElement("category");
category.SetAttribute("name", "http://schemas.android.com/apk/res/android",
"android.intent.category.BROWSABLE");
intent.AppendChild(category);

XmlElement data = manifest.CreateElement("data");
data.SetAttribute("scheme", "http://schemas.android.com/apk/res/android", "https");
intent.AppendChild(data);

queries.AppendChild(intent);

XmlNode manifestNode = manifest.SelectSingleNode("/manifest");
manifestNode?.AppendChild(queries);
}
}
#endif
빌드 결과 예시

PlayNANOO 콘솔에 등록된 Game ID가 yourgameid인 경우, 빌드 시 AndroidManifest.xml에 아래와 같이 등록됩니다.

<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:scheme="yourgameid" />
</intent-filter>

Unity 딥링크 등록 절차 iOS

  • Info.plist 안에 아래 내용 추가.
  • CFBundleURLSchemes 값을 PlayNANOO 콘솔에 등록된 Game ID로 설정 합니다.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>{your_game_id}</string>
</array>
<key>CFBundleURLName</key>
<string>com.mycompany.{your_game_id}</string>
</dict>
</array>
설정 예시

PlayNANOO 콘솔에 등록된 Game ID가 yourgameid인 경우:

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>yourgameid</string>
</array>
<key>CFBundleURLName</key>
<string>com.mycompany.yourgameid</string>
</dict>
</array>

Android Chrome Custom Tabs 설정 (선택사항)

Android에서 앱 내 오버레이 형태로 브라우저를 띄우려면 Chrome Custom Tabs 라이브러리가 필요합니다. 라이브러리가 없으면 자동으로 외부 브라우저로 열립니다.

mainTemplate.gradle 설정

  • Assets/Plugins/Android/mainTemplate.gradle 파일에 아래 의존성을 추가합니다.
dependencies {
implementation 'androidx.browser:browser:1.5.0'
// ... 기존 의존성들
}

Custom Build 사용 시

  • Project Settings > Player > Android > Publishing Settings에서 "Custom Main Gradle Template"을 체크합니다.
  • 생성된 mainTemplate.gradle 파일에 위 의존성을 추가합니다.

ProGuard 설정

  • Assets/Plugins/Android/proguard-user.txt 파일에 아래 규칙을 추가합니다.
  • ProGuard/R8이 Chrome Custom Tabs 클래스를 제거하지 않도록 합니다.
-keep class androidx.browser.customtabs.** { *; }
정보

Chrome Custom Tabs 라이브러리가 포함되지 않은 경우, SDK는 자동으로 외부 Chrome 브라우저로 fallback 합니다. 외부 브라우저 사용 시에도 기능은 정상 동작하며, 딥링크를 통해 앱으로 복귀합니다.


Deprecated: 기존 mygame scheme 방식

Deprecated (SDK 5.x 이전 버전)

아래 방식은 더 이상 사용하지 마십시오. 모든 앱이 동일한 mygame:// scheme을 공유하여, 동일 기기에 PlayNANOO SDK를 사용하는 앱이 2개 이상 설치된 경우 인증 완료 후 잘못된 앱으로 복귀하는 문제가 발생합니다.

신규 방식에서는 SDK가 PlayNANOO 콘솔에 등록된 Game ID를 scheme으로 자동 사용하여 앱별 고유 식별이 가능합니다.

기존 방식 (참고용)

Android (기존)

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Android;
using System.IO;

public class AndroidDeepLinkPostProcessor : IPostGenerateGradleAndroidProject
{
public int callbackOrder => 999;

public void OnPostGenerateGradleAndroidProject(string path)
{
string manifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");

if (!File.Exists(manifestPath))
{
UnityEngine.Debug.LogError("[DeepLink] AndroidManifest.xml not found!");
return;
}

string manifest = File.ReadAllText(manifestPath);

if (!manifest.Contains("mygame://oauth2redirect"))
{
string intentFilter = @"
<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:scheme=""mygame"" android:host=""oauth2redirect"" />
</intent-filter>";

manifest = manifest.Replace(
"</activity>",
intentFilter + "\n</activity>"
);

File.WriteAllText(manifestPath, manifest);
UnityEngine.Debug.Log("[DeepLink] AndroidManifest 딥링크 등록 완료");
}
else
{
UnityEngine.Debug.Log("[DeepLink] AndroidManifest 이미 등록됨");
}
}
}
#endif

iOS (기존)

<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>mygame</string>
</array>
<key>CFBundleURLName</key>
<string>com.mycompany.mygame</string>
</dict>
</array>