본문으로 건너뛰기

안드로이드 가입 및 인증

외부 브라우저를 통한 Apple OAuth 로그인 처리입니다. Android에서 Apple 로그인을 지원합니다.

동작 방식
  1. PlayNANOO 서버에서 Apple OAuth URL 획득
  2. 외부 브라우저에서 Apple 계정 로그인
  3. Deep Link로 token 수신
  4. 토큰으로 계정 등록(SocialSignIn) 또는 회원 전환(SocialChange) API 호출

Unreal 구현

// iOSSignin.h
#pragma once

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

DECLARE_DELEGATE_OneParam(FOnAppleSigninSuccess, const FString&);
DECLARE_DELEGATE_TwoParams(FOnAppleSigninError, const FString&, const FString&);

/**
* Apple Sign In (Android/iOS)
* 외부 브라우저를 통한 Apple OAuth 로그인 처리
*/
UCLASS()
class YOURPROJECT_API UiOSSignin : public UObject
{
GENERATED_BODY()

private:
// Apple URL 응답 구조체
struct FAppleUrlResponse
{
FString ErrorCode;
FString Message;
FString Url;

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("ErrorCode"), ErrorCode);
JsonObject->TryGetStringField(TEXT("Message"), Message);
JsonObject->TryGetStringField(TEXT("Url"), Url);
return true;
}
return false;
}
};

FOnAppleSigninSuccess OnSuccessCallback;
FOnAppleSigninError OnErrorCallback;

public:
/**
* Apple 로그인 시작
*/
UFUNCTION(BlueprintCallable, Category = "PlayNANOO|Account")
void Sign_in_with_Apple(FOnAppleSigninSuccess OnSuccess, FOnAppleSigninError OnError);

private:
void GetAppleUrlAndOpen();
void OnDeepLink(const FString& URL);
void SendTokenToServer(const FString& Token);
FString ExtractParam(const FString& URL, const FString& Key);
};

// iOSSignin.cpp
#include "iOSSignin.h"
#include "PlayNANOOHelper.h"
#include "HttpModule.h"
#include "Interfaces/IHttpRequest.h"
#include "Interfaces/IHttpResponse.h"
#include "Kismet/GameplayStatics.h"

void UiOSSignin::Sign_in_with_Apple(FOnAppleSigninSuccess OnSuccess, FOnAppleSigninError OnError)
{
OnSuccessCallback = OnSuccess;
OnErrorCallback = OnError;

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

GetAppleUrlAndOpen();
}

void UiOSSignin::GetAppleUrlAndOpen()
{
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
Request->SetVerb(TEXT("GET"));
Request->SetURL(TEXT("https://service-account.playnanoo.com/api/apple/url"));

// 공통 헤더 설정 (로그인 API이므로 인증 토큰 불필요)
FPlayNANOOHelper::SetCommonHeaders(Request, false);

Request->OnProcessRequestComplete().BindLambda(
[this](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess)
{
if (bSuccess && Response.IsValid())
{
FAppleUrlResponse Result;
if (Result.FromJson(Response->GetContentAsString()))
{
if (!Result.Url.IsEmpty())
{
FPlatformProcess::LaunchURL(*Result.Url, nullptr, nullptr);
}
else
{
OnErrorCallback.ExecuteIfBound(Result.ErrorCode, Result.Message);
}
}
}
else
{
OnErrorCallback.ExecuteIfBound(TEXT("HTTP_ERROR"), TEXT("Failed to get Apple URL"));
}
}
);

Request->ProcessRequest();
}

void UiOSSignin::OnDeepLink(const FString& URL)
{
if (URL.Contains(TEXT("type=apple")))
{
FString Status = ExtractParam(URL, TEXT("status"));
FString Token = ExtractParam(URL, TEXT("token"));

if (Status == TEXT("success") && !Token.IsEmpty())
{
SendTokenToServer(Token);
}
else
{
OnErrorCallback.ExecuteIfBound(TEXT("APPLE_AUTH_FAILED"), TEXT("Apple authentication failed"));
}

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

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

OnSuccessCallback.ExecuteIfBound(Token);
}

FString UiOSSignin::ExtractParam(const FString& URL, const FString& Key)
{
FString Search = Key + TEXT("=");
if (URL.Contains(Search))
{
int32 StartIndex = URL.Find(Search) + Search.Len();
int32 EndIndex = URL.Find(TEXT("&"), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartIndex);
if (EndIndex == INDEX_NONE)
{
EndIndex = URL.Len();
}
return URL.Mid(StartIndex, EndIndex - StartIndex);
}
return FString();
}

사용 방법

1. Apple 로그인 시작

void UYourClass::StartAppleLogin()
{
UiOSSignin* AppleSignin = NewObject<UiOSSignin>();

AppleSignin->Sign_in_with_Apple(
FOnAppleSigninSuccess::CreateLambda([](const FString& Token)
{
UE_LOG(LogTemp, Log, TEXT("Apple 로그인 성공! Token: %s"), *Token);

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

2. 토큰으로 계정 연동

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

신규 로그인 (SocialSignIn)

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

회원 전환 (SocialChange)

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