블로그로 돌아가기

Epic Online Services Unreal Blueprint Login 실패 및 Achievement 오류 해결 방법

게시일 2026년 7월 9일
Epic Online Services Unreal Blueprint Login 실패 및 Achievement 오류 해결 방법

핵심 요약

Unreal Engine에서 Epic Online Services(EOS) 연동 시 발생하는 Blueprint 로그인 실패와 achievement 오류의 원인 및 해결 방법을 다룹니다. 특히 스토어프론트 수준의 EAID와 백엔드 게임 서비스용 PUID의 구분, 그리고 asynchronous 실행 흐름 제어의 중요성을 중점적으로 설명합니다. 이를 해결하기 위한 C++ 구현 예시와 DefaultEngine.ini 파일 설정 템플릿을 제공하여 개발자가 즉시 적용할 수 있도록 돕습니다. 더 나아가, 크로스 플랫폼 백엔드 서비스인 horizOn을 통해 플랫폼 연동 오버헤드를 줄이는 대안도 제시합니다.

Unreal Engine 게임의 shipping build를 컴파일하고, Epic Games Launcher에서 실행한 후 커스텀 login 버튼을 클릭합니다. 아무 반응이 없거나 게임 클라이언트가 멈추고, 로그에는 원인을 알 수 없는 authentication 실패 메시지가 가득 찹니다. 설상가상으로 achievement 해제 node는 아무런 에러 없이 실행되었음에도 Epic Games Store overlay의 플레이어 프로필에는 아무것도 나타나지 않습니다. 이러한 오류는 스토어프론트 API를 연동할 때 매우 흔하게 발생합니다. 다행히도 asynchronous execution flow를 올바르게 수정하고, 서브시스템이 user identities를 매핑하는 방식을 이해하면 이를 해결할 수 있습니다.

EOS Authentication Pipeline 이해하기: EAID vs. PUID

Unreal Engine의 Online Subsystem (OSS)을 통해 Epic Online Services (EOS)를 연동하려면 2단계 ID 시스템을 거쳐야 합니다. Epic Account ID (EAID)는 Auth Interface에 의해 관리됩니다. 이는 스토어프론트 수준에서 사용자의 신원을 인증하며, social overlay, friends lists, Epic Games Store 라이브러리 검사 등의 기능을 활성화합니다.

반면, Product User ID (PUID)는 Connect Interface에 의해 관리됩니다. PUID는 사용자를 backend 게임 서비스에 연결하는 게임 전용 식별자입니다. 이 서비스에는 matchmaking, stats, cloud saves, achievements 등이 포함됩니다.

EAID와 PUID 불일치로 인해 Achievements가 작동하지 않는 이유

가장 자주 발생하는 실패 사례 중 하나는 개발자가 Auth Interface를 통한 인증(EAID 생성)에는 성공했지만, 서브시스템이 Connect Interface 로그인에 실패하는 경우입니다. achievements는 PUID를 사용하는 Connect Interface에 의해 관리되기 때문에, PUID가 확인되지 않은 상태에서 achievement 진행 상황을 기록하려고 하면 EOS_InvalidUser 에러가 발생하며 실패하게 됩니다.

스토어프론트 wrapper 없이 실행되는 게임의 경우, 유저는 보통 중앙 집중식 유저 테이블에서 대조되는 credentials를 사용해 로그인합니다. 하지만 Epic Games Store와 같은 현대적인 스토어프론트 에코시스템에서는 identity가 federated(연합)되어 있습니다. 게임이 실행되면 launcher로부터 cryptographic ticket을 받게 됩니다. 게임은 이 ticket을 스토어프론트의 authentication servers로 전달하고, SDK는 플랫폼의 모든 게임에서 유저의 패스포트 역할을 하는 Epic Account ID를 반환합니다.

그러나 개인정보 보호 및 크로스플레이 보안을 위해, Epic은 achievements 같은 게임 전용 기능이 Epic Account ID를 직접 읽는 것을 허용하지 않습니다. 대신 게임은 Connect Interface를 초기화하여 Product User ID를 받아오기 위한 2차 교환을 수행해야 합니다. 이러한 분리 덕분에 게임이 해킹당하더라도 플레이어의 글로벌 Epic Account credentials는 침해되지 않습니다. PUID가 없으면 게임은 실질적으로 인증되지 않은 게스트 상태로 작동하여 EGS 기능에 접근할 수 없게 됩니다. 만약 Epic Developer Portal에서 credentials config나 client policy에 올바른 권한이 설정되어 있지 않다면 이 연결은 실패합니다. identity 인터페이스는 Epic 계정이 유효하므로 로그인 성공을 보고하지만, PUID가 링크되지 않았기 때문에 게임 서비스 레이어는 achievement 데이터를 기록할 수 없습니다.

잘못된 Blueprint Login 시퀀스의 구조

많은 개발자들이 Blueprint execution pin을 선형적으로 연결하여 로그인 문제를 겪습니다. Login node의 execution pin을 Query AchievementsWrite Achievement Progress node에 직접 드래그하여 연결하면 항상 실패합니다.

authentication 요청은 Epic 백엔드로 전송되어야 하므로 asynchronous하게 작동합니다. 엔진은 Epic 서버가 유저 credentials를 반환하기 훨씬 전인 바로 다음 프레임에 Blueprint 그래프를 계속 실행합니다. 이 단계에서는 플레이어의 Unique Net ID가 여전히 null 상태입니다.

"누락된 Node"의 불일치: Async 실행에서의 Race Conditions

login node 바로 다음에 achievement node를 호출하면, 캐싱된 유저 상태가 비어 있기 때문에 서브시스템은 디버그 창에 나타나지 않는 silent error를 발생시킵니다. 올바른 워크플로우를 위해서는 서브시스템의 asynchronous delegates에 커스텀 이벤트를 바인딩해야 합니다.

또한, 개발자들은 업적을 기록하기 전에 먼저 achievements를 캐싱해야 하는 핵심 단계를 놓치는 경우가 많습니다. 서버에 현재 schema를 먼저 쿼리하지 않고는 achievement 진행 상황을 수정할 수 없습니다. 사전 Query Achievements 호출 없이 Write Achievements를 호출하면, 로컬 캐시된 achievement 맵 크기가 0이기 때문에 로그 콘솔에 silent failure 또는 EOS_InvalidUser 에러가 발생합니다.

올바른 Asynchronous Blueprint 흐름

이 문제를 해결하려면 login 트리거와 achievement 업데이트를 분리해야 합니다. 먼저, login node를 호출하고 올바른 authentication credentials를 지정합니다.

둘째, identity 인터페이스의 On Login Complete delegate에 이벤트를 바인딩합니다. 이 이벤트가 실행되고 성공 상태가 반환되면, 유저의 Unique Net ID를 가져옵니다.

셋째, 이 ID를 Query Achievements node에 전달합니다. 마지막으로 On Query Achievements Complete delegate에 이벤트를 바인딩하고, 해당 콜백 내에서만 Write Achievement Progress를 호출해야 합니다.

C++로 견고한 EOS Login & Achievement Controller 구현하기

이러한 asynchronous 작업을 깔끔하게 처리하기 위해, C++로 authentication 루프를 구현하는 것을 적극 권장합니다. C++을 사용하면 IOnlineSubsystem 및 해당 인터페이스에 직접 액세스할 수 있습니다.

다음은 프로덕션 환경에 바로 적용할 수 있는 C++ 기반의 login 및 achievement 핸들러 구현체입니다:

#include "OnlineSubsystem.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Interfaces/OnlineAchievementsInterface.h"
#include "OnlineSubsystemUtils.h"

void UEOSLoginHandler::LoginToEpicGames(APlayerController* PlayerController)

{
    if (!PlayerController) return;

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    if (!Subsystem)
    {
        UE_LOG(LogTemp, Warning, TEXT("EOS Subsystem not found. Make sure the plugin is enabled."));
        return;
    }

    IOnlineIdentityPtr Identity = Subsystem->GetIdentityInterface();
    if (Identity.IsValid())
    {
        FOnlineAccountCredentials Credentials;
        Credentials.Type = TEXT("ExchangeCode");
        Credentials.Id = TEXT("");
        Credentials.Token = TEXT(""); // The launcher passes this via command line arguments

        Identity->AddOnLoginCompleteDelegate_Handle(0, FOnLoginCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnLoginComplete));
        Identity->Login(0, Credentials);
    }
}

void UEOSLoginHandler::OnLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
    if (!bWasSuccessful)
    { 
        UE_LOG(LogTemp, Error, TEXT("Epic Online Services Login Failed. Error: %s"), *Error);
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("EOS Login Successful. ID resolved: %s"), *UserId.ToString());

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    { 
        Achievements->QueryAchievements(UserId, FOnQueryAchievementsCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnQueryAchievementsComplete));
    }
}

void UEOSLoginHandler::OnQueryAchievementsComplete(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (!bWasSuccessful)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to query achievements from Epic Developer Portal."));
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("Achievements queried successfully. Now writing progress."));

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    {
        FOnlineAchievementsWriteRef WriteObject = MakeShared<FOnlineAchievementsWrite>();
        WriteObject->SetFloatStat(TEXT("ACH_FIRST_BLOOD"), 100.0f);

        Achievements->WriteAchievements(PlayerId, WriteObject, FOnAchievementsWrittenDelegate::CreateUObject(this, &UEOSLoginHandler::OnAchievementsWritten));
    }
}

void UEOSLoginHandler::OnAchievementsWritten(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (bWasSuccessful)
    {
        UE_LOG(LogTemp, Log, TEXT("Achievement successfully unlocked on Epic Games Store!"));
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to write achievement progress. Check sandbox mappings."));
    }
}

이 컨트롤러는 순차적인 callbacks를 활용합니다. identity 인터페이스가 Epic Account ID와 Product User ID를 모두 성공적으로 해결한 후에만 achievement 인터페이스에 접근하도록 보장합니다.

EOS 사용을 위한 DefaultEngine.ini 파일 설정하기

Config/DefaultEngine.ini 파일의 설정이 잘못되면 Blueprint login node가 초기화에 실패합니다. Online Subsystem EOS 플러그인이 게임 클라이언트에 바인딩되려면 특정 파라미터들이 필요합니다.

서브시스템이 제대로 초기화되도록 하려면 다음 설정 템플릿을 사용하세요:

[OnlineSubsystem]
DefaultPlatformService=EOS

[OnlineSubsystemEOS]
bEnabled=true
bUseEpicConnect=true
ProductId=your_product_id_here
SandboxId=your_sandbox_id_here
DeploymentId=your_deployment_id_here
ClientId=your_client_id_here
ClientSecret=your_client_secret_here

[OnlineSubsystemEOS.Auth]
bUseEpicAccount=true

DefaultEngine.ini 파일 설정은 컴파일된 게임 바이너리와 Epic Developer Portal 간의 다리 역할을 합니다. [OnlineSubsystemEOS] 아래의 파라미터들은 제품 설정과 정확히 일치해야 합니다.

흔히 혼동하는 부분 중 하나는 client credentials와 sandbox ID의 차이점입니다. Client ID와 Client Secret은 게임 클라이언트의 credentials를 나타내며, 이를 통해 EOS 백엔드와 통신할 수 있게 해줍니다. Sandbox ID는 클라이언트가 접근하는 특정 환경(Development, Staging, Live 등)을 나타냅니다.

만약 게임 클라이언트가 Client Credentials 정책과 일치하지 않는 Sandbox ID로 인증을 시도하면, 인증 요청이 거부됩니다. 또한 bUseEpicConnect 플래그는 true로 설정해야 Online Subsystem이 배후에서 EAID-PUID 매핑을 자동으로 처리합니다.

Overlay Focus 불일치 및 플랫폼 연동 관리하기

Unreal의 EOS 플러그인은 알림 표시, 친구 초대 처리, OAuth 웹 인증 수행 등을 위해 Epic Games Overlay에 크게 의존합니다. 게임 클라이언트가 AccountPortal credential 타입을 사용해 로그인을 트리거하면, 운영 체제는 브라우저 창을 실행하거나 overlay를 엽니다.

이 리디렉션은 게임 창에서 포커스를 뺏어갑니다. 입력 모드가 포커스 해제를 처리하도록 설정되어 있지 않으면 게임이 멈추거나 키가 계속 눌려 있는 현상이 발생할 수 있습니다.

overlay로 인한 입력 문제는 PC 개발자들에게 흔한 골칫거리입니다. 예를 들어 overlay 인젝션은 raw input 드라이버와 충돌하여 키보드/마우스 상태를 고정시킬 수 있습니다. 이러한 버그가 플레이어 경험을 해치는 것을 방지하려면, The Marathon Input Issues Fix: Architecting PC Games to Survive Overlay Conflicts 분석을 확인해 보시기 바랍니다.

또한 테스트 중 발생하는 초기화 실패는 로컬 설정의 socket connections나 timeout thresholds가 잘못 구성되어 발생하는 경우가 많습니다. 세션 시작 시 드라이버 타임아웃 문제를 해결하려는 경우, Uefn Session Launch Timeout Nightmares: Diagnosing Unreal Engine Network Drivers 가이드를 확인해 보세요.

크로스 플랫폼 스토어프론트 Backend 단순화하기

스토어프론트 서브시스템을 수동으로 구성하고, C++ delegate 체인을 작성하고, 플랫폼별 overlay 포커스 문제를 해결하는 것은 시간이 매우 많이 걸리는 작업입니다. Steam, EGS, 그리고 콘솔과 같은 다중 스토어프론트를 지원하려고 하면 개발 오버헤드는 배로 증가합니다.

이를 직접 구현하려면 load balancers 구축, database sharding, SSL cert 관리 등을 설정해야 하며, 이는 쉽게 4~6주의 작업 시간이 소요될 수 있습니다.

horizOn을 사용하면 이러한 스토어프론트 API들을 추상화한 단일화된 크로스 플랫폼 백엔드를 얻을 수 있습니다. 이중 식별자 토큰(EAID/PUID), Epic sandboxes, 또는 Steamworks 초기화 코드를 직접 다룰 필요 없이 유저 프로필, achievements, matchmaking, leaderboards를 처리하는 단일 API를 사용하면 됩니다.

horizOn은 브리지 역할을 하여 유저가 실행한 스토어프론트에 관계없이 achievements와 플레이어 진행 상황을 자동으로 동기화하므로, 개발자는 스토어프론트 boilerplate 대신 핵심 게임 루프에 집중할 수 있습니다. 즉, 플레이어는 어떤 플랫폼이나 런처를 사용하는지에 상관없이 스토어프론트 전용 SDK 코드를 한 줄도 작성하지 않고도 업적을 달성하고, cloud saves를 사용하고, 매치를 검색할 수 있게 됩니다.

Epic Online Services 연동을 위한 현대적인 Best Practices

  1. Asynchronous Nodes를 절대로 직접 연결하지 마세요: stats나 achievements를 기록하는 것과 같은 다운스트림 작업을 실행하기 전에 항상 OnLoginCompleteOnQueryAchievementsComplete delegates가 실행될 때까지 기다리세요.

  2. 개발 중에는 Verbose Logging을 활성화하세요: 숨겨진 EOS 경고를 추적하려면 DefaultEngine.ini 파일의 [Core.Log] 아래에 다음 줄을 추가하세요:

    [Core.Log]
    LogOnline=VeryVerbose
    LogOnlineGame=VeryVerbose
    LogEOSSDK=VeryVerbose
    
  3. Dev Portal에서 Achievements를 EOS에 매핑하세요: Epic Games Store(EGS)에서 생성된 achievements가 Epic Online Services(EOS) 백엔드에 명시적으로 매핑되어 있는지 확인하세요. 링크되어 있지 않으면 EOS SDK가 achievement ID를 인식하지 못합니다.

  4. Shipping Builds에서는 ExchangeCode를 사용하세요: 로컬 개발 단계에서는 Dev Auth Tool을 사용하는 Developer login 타입이 편리하지만, Epic Games Launcher에서 실행되는 shipping builds에서는 credentials 타입을 ExchangeCode로 전환해야 합니다. 런처에서 제공하는 ExchangeCode auth 타입을 사용하면 웹 브라우저 OAuth 리디렉션 과정이 생략되어, 수동 사용자 개입이 필요한 8~12초의 로그인 시퀀스 시간이 500ms 미만의 자동 handshake로 단축됩니다.

스토어프론트 연동 간소화

Unreal Engine에서 Epic Online Services login 및 achievement 에러를 해결하는 핵심은 async 파이프라인을 이해하고 설정 프로필을 완벽하게 맞추는 데 있습니다. identity와 게임 서비스 인터페이스 간의 전환 과정을 잘 관리하면 개발자는 silent error를 제거하고 플레이어에게 원활한 경험을 선사할 수 있습니다.

multiplayer 백엔드를 확장할 준비가 되셨나요? horizOn을 무료로 체험해 보거나 API docs를 확인해 보세요.


출처: Epic Games Store (Online Services AND Achievements)

이 대시보드는 다음에 의해 애정을 담아 만들어졌습니다 Projectmakers

© 2026 projectmakers.de

unknown-v1.100.2 / unknown-v--