본문으로 건너뛰기

Sign in with Google

OAuth 2.0 기반의 구글 로그인 웹 인증 방식입니다. iOS, 안드로이드 통합 사용 가능합니다.

동작 방식

Android:

  1. 외부 브라우저(또는 Chrome Custom Tabs)에서 구글 계정 선택
  2. Deep Link로 id_token 수신
  3. 토큰으로 계정 등록(SocialSignIn) 또는 회원 전환(SocialChange) API 호출

iOS:

  1. ASWebAuthenticationSession으로 앱 내 인증 세션 표시
  2. Deep Link 또는 네이티브 콜백으로 id_token 수신
  3. 토큰으로 계정 등록(SocialSignIn) 또는 회원 전환(SocialChange) API 호출

Unreal 구현

// GoogleSignin.h
#pragma once

#include "CoreMinimal.h"
#include "Http.h"
#include "Json.h"
#include "JsonUtilities.h"
#include "GoogleSignin.generated.h"

DECLARE_DELEGATE_OneParam(FOnGoogleSigninSuccess, const FString&);
DECLARE_DELEGATE_TwoParams(FOnGoogleSigninError, const FString&, const FString&);

UCLASS()
class YOURPROJECT_API UGoogleSignin : public UObject
{
GENERATED_BODY()

private:
static const FString HOST_PLAYNANOO_OAUTH2REDIRECT;
static const FString OAUTH2REDIRECT_SCOPE;

FString ClientId;
FOnGoogleSigninSuccess OnSuccessCallback;
FOnGoogleSigninError OnErrorCallback;

public:
/**
* Google 로그인 시작
*/
UFUNCTION(BlueprintCallable, Category = "PlayNANOO|Account")
void Sign_in_with_Google(const FString& InClientId, FOnGoogleSigninSuccess OnSuccess, FOnGoogleSigninError OnError);

private:
void OnDeepLink(const FString& URL);
FString ExtractIdToken(const FString& URL);
void OpenURLInExternalBrowser(const FString& URL);
void SendTokenToServer(const FString& Token);
};

// GoogleSignin.cpp
#include "GoogleSignin.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/Guid.h"

const FString UGoogleSignin::HOST_PLAYNANOO_OAUTH2REDIRECT = TEXT("https://www.playnanoo.com/oauth2redirect.html");
const FString UGoogleSignin::OAUTH2REDIRECT_SCOPE = TEXT("openid email profile");

void UGoogleSignin::Sign_in_with_Google(const FString& InClientId, FOnGoogleSigninSuccess OnSuccess, FOnGoogleSigninError OnError)
{
ClientId = InClientId;
OnSuccessCallback = OnSuccess;
OnErrorCallback = OnError;

// Deep Link 리스너 등록
FCoreDelegates::ApplicationReceivedDeepLinkDelegate.AddUObject(this, &UGoogleSignin::OnDeepLink);

FString Nonce = FGuid::NewGuid().ToString(EGuidFormats::Digits);

FString AuthUrl = FString::Printf(TEXT("https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&redirect_uri=%s&response_type=id_token&scope=%s&nonce=%s&prompt=select_account&login_hint="),
*ClientId,
*FGenericPlatformHttp::UrlEncode(HOST_PLAYNANOO_OAUTH2REDIRECT),
*FGenericPlatformHttp::UrlEncode(OAUTH2REDIRECT_SCOPE),
*Nonce
);

// 외부 브라우저에서 열기 (403 disallowed_useragent 방지)
OpenURLInExternalBrowser(AuthUrl);
}

void UGoogleSignin::OnDeepLink(const FString& URL)
{
FString Token = ExtractIdToken(URL);
if (!Token.IsEmpty())
{
SendTokenToServer(Token);

// Deep Link 리스너 해제
FCoreDelegates::ApplicationReceivedDeepLinkDelegate.RemoveAll(this);
}
}

FString UGoogleSignin::ExtractIdToken(const FString& URL)
{
// query parameter(?) 또는 fragment(#)에서 id_token 추출
FString ParamString;

if (URL.Contains(TEXT("?")))
{
int32 Index;
URL.FindChar('?', Index);
ParamString = URL.RightChop(Index + 1);
}
else if (URL.Contains(TEXT("#")))
{
int32 Index;
URL.FindChar('#', Index);
ParamString = URL.RightChop(Index + 1);
}

if (ParamString.IsEmpty())
{
return FString();
}

TArray<FString> Parts;
ParamString.ParseIntoArray(Parts, TEXT("&"), true);

for (const FString& Part : Parts)
{
if (Part.StartsWith(TEXT("id_token=")))
{
return Part.RightChop(9); // "id_token=" 길이
}
}

return FString();
}

void UGoogleSignin::OpenURLInExternalBrowser(const FString& URL)
{
#if PLATFORM_ANDROID
// Android 전용 처리
if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
{
jstring URLString = Env->NewStringUTF(TCHAR_TO_UTF8(*URL));
FJavaWrapper::CallVoidMethod(Env, FJavaWrapper::GameActivityThis,
FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_LaunchURL", "(Ljava/lang/String;)V", false),
URLString);
Env->DeleteLocalRef(URLString);
}
#else
// 기본 처리 (iOS 등)
FPlatformProcess::LaunchURL(*URL, nullptr, nullptr);
#endif
}

void UGoogleSignin::SendTokenToServer(const FString& Token)
{
// token 값으로 SocialChange 또는 SocialSignIn 진행
// SocialChange(Token, TEXT("GOOGLE"));
// or
// SocialSignIn(Token, TEXT("GOOGLE"));

OnSuccessCallback.ExecuteIfBound(Token);
}

OAuth 2.0 파라미터

파라미터설명
client_idGoogle Cloud Console에서 발급OAuth 웹 클라이언트 ID
redirect_urihttps://www.playnanoo.com/oauth2redirect.html인증 후 리다이렉트 URL
response_typeid_tokenOpenID Connect ID 토큰 요청
scopeopenid email profile요청할 사용자 정보 범위
nonceFGuid::NewGuid()재생 공격 방지용 랜덤 문자열
promptselect_account계정 선택 강제 표시
환경 설정 필수

Sign in with Google을 사용하기 전에 구글 환경 설정 문서를 참조하여 다음 설정을 완료해야 합니다:

  • Google Cloud Console에서 OAuth 2.0 클라이언트 ID 발급
  • Android/iOS Deep Link 설정
  • 리다이렉트 URI 등록

사용 방법

1. 구글 로그인 시작

void UYourClass::StartGoogleSignIn()
{
UGoogleSignin* GoogleSignin = NewObject<UGoogleSignin>();

GoogleSignin->Sign_in_with_Google(
TEXT("Your Google Client Id"),
FOnGoogleSigninSuccess::CreateLambda([](const FString& Token)
{
UE_LOG(LogTemp, Log, TEXT("Google 로그인 성공! Token: %s"), *Token);

// 토큰으로 PlayNANOO API 호출
// SocialSignIn(Token, TEXT("GOOGLE"));
}),
FOnGoogleSigninError::CreateLambda([](const FString& ErrorCode, const FString& Message)
{
UE_LOG(LogTemp, Error, TEXT("Google 로그인 실패: [%s] %s"), *ErrorCode, *Message);
})
);
}

2. 토큰으로 계정 연동

인증 완료 후 SendTokenToServer 메소드에서 받은 id_token으로 PlayNANOO API를 호출합니다:

신규 로그인 (SocialSignIn)

void UYourClass::SocialSignIn(const FString& Token, const FString& AccountType)
{
// 구글 계정으로 처음 로그인
// AccountType: "GOOGLE"
}

회원 전환 (SocialChange)

void UYourClass::SocialChange(const FString& Token, const FString& AccountType)
{
// 비회원에서 구글 회원으로 전환
// AccountType: "GOOGLE"
}
참조

플랫폼별 콜백 처리

Android

  • DeepLink 방식으로 콜백을 받습니다.
  • FCoreDelegates::ApplicationReceivedDeepLinkDelegate에 콜백을 등록합니다.
  • Chrome Custom Tabs 라이브러리가 포함된 경우 앱 내 오버레이 형태로 브라우저가 열립니다.
  • 라이브러리가 없으면 외부 Chrome 브라우저로 열리며, 인증 완료 후 딥링크로 앱 복귀합니다.
// Deep Link 리스너 등록
FCoreDelegates::ApplicationReceivedDeepLinkDelegate.AddUObject(this, &UGoogleSignin::OnDeepLink);

iOS

  • ASWebAuthenticationSession을 사용하여 앱 내에서 인증 세션이 열립니다.
  • Deep Link를 통해 콜백을 받습니다.
  • FCoreDelegates::ApplicationReceivedDeepLinkDelegate로 동일하게 처리 가능합니다.
Chrome Custom Tabs 설정

Android에서 앱 내 오버레이 형태로 브라우저를 띄우려면 androidx.browser:browser:1.5.0 라이브러리가 필요합니다. 자세한 설정 방법은 구글 환경 설정 문서를 참조하세요.