Sign in with Google 환경 설정 (v1 호환)
- 구글 로그인 웹 인증 방식 입니다. google v1 방식 과 호환 가능 합니다.
- 구글 계정 활용을 위하여 환경설정을 합니다.
- 플레이나누 SDK 5.0.0.6 이상 버전을 권장합니다. (브라우저 캐시 문제 해결)
- 최소 요구 버전: 5.0.0.3
구글 사용자 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 정보를 확인 합니다.
Unity 딥링크 등록 절차 Android
기존 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 합니다. 외부 브라우저 사용 시에도 기능은 정상 동작하며, 딥링크를 통해 앱으로 복귀합니다.
Google은 보안 정책에 따라 WebView(내장 브라우저)에서의 OAuth 인증을 차단합니다.
NAVER, LINE, KakaoTalk 등 앱의 내장 브라우저에서 Google 로그인 시 403 disallowed_useragent 오류가 발생합니다.
SDK의 브라우저 선택 동작:
- Chrome이 설치되어 있으면 Chrome Custom Tabs 또는 Chrome 브라우저를 우선 사용합니다.
- Chrome이 설치되어 있지 않거나 비활성화된 경우, 시스템 기본 브라우저로 전환됩니다.
- 기본 브라우저가 WebView 기반 앱(NAVER 등)으로 설정되어 있으면 403 오류가 발생합니다.
사용자 안내 가이드:
- Google 로그인에는 Chrome, Samsung Internet, Firefox 등 독립 실행 브라우저가 필요합니다.
- 기기에 Chrome이 설치되어 있지 않은 경우 Google Play에서 설치해야 합니다.
- Chrome이 설치되어 있으나 오류가 발생하는 경우, 기기 설정에서 Chrome이 비활성화되어 있지 않은지 확인합니다.
Deprecated: 기존 mygame scheme 방식
아래 방식은 더 이상 사용하지 마십시오.
모든 앱이 동일한 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>