다계정 로그인 관리
다계정 로그인 관리 란 로그인 혹은 로그아웃 시 계정 정보를 저장하여 이후 재로그인 시 저장된 계정 정보를 불러와 로그인할 수 있는 기능으로, 이전에 인증된 계정 정보를 활용하여 보다 빠르고 편리한 로그인 경험을 제공합니다.
동작 플로우 및 제약 사항¶
다계정 로그인 관리 기능을 구현하기에 앞서, 동작 플로우와 제약 사항을 설명합니다.
아래의 각 기능별 동작 플로우는 다계정 로그인 관리 구현에 참고하기 위한 예시 시나리오로, 개발사 환경에 따라 플로우 순서나 계정 정보 저장 방식은 해당 시나리오와 다르게 구현할 수 있습니다.
로그인 상태에서 계정 정보 저장¶
유저가 명시적 로그인 완료 후 로그인한 상태에서 계정 정보를 저장하는 기능의 시나리오입니다.
이 시나리오의 저장된 계정 정보를 활용하여 다계정 로그인 관리의 로그인 혹은 삭제을 구현할 수 있습니다.
※ 계정 정보 저장 및 처리 방식은 게임 클라이언트 구현 상황에 따라 다를 수 있습니다.
로그아웃 시 계정 정보 저장¶
로그인한 유저가 로그아웃 시 계정 정보를 저장하는 기능의 시나리오입니다.
이 시나리오의 저장된 계정 정보를 활용하여 다계정 로그인 관리의 로그인 혹은 삭제을 구현할 수 있습니다.
※ 계정 정보 저장 및 처리 방식은 게임 클라이언트 구현 상황에 따라 다를 수 있습니다.
저장된 계정 정보를 활용한 재로그인 혹은 계정 삭제¶
저장된 계정 정보을 활용하여 재로그인하거나 조회한 리스트에서 특정 계정 정보 삭제하는 플로우로, 실제적인 다계정 로그인 관리 기능을 구현하기 위한 시나리오입니다.
해당 플로우 중 게임에서 구현한 PlayerID 리스트 UI에서 리스트 목록의 특정 계정을 선택하여 로그인하는 단계에서는 현재 유저의 로그인 여부에 따라 처리되는 동작이 상이합니다.
※ 게임 클라이언트에서 구현하는 PlayerID 리스트의 UI 구성 방식은 게임 개발 환경에 따라 다를 수 있습니다.
동작 제약 사항¶
다계정 로그인 관리 구현에 앞서 고려해야 하는 동작 제약 사항은 아래와 같습니다.
- 계정 정보 저장은 로그인 상태에서만 가능합니다.
- 저장된 계정 정보를 활용하여 PlayerID 리스트 조회 혹은 저장된 계정 정보 삭제하기는 로그인 상태와 관계없이 가능합니다.
- PlayerID 리스트 조회 후 저장된 계정으로 재로그인은 로그아웃된 상태에서만 가능합니다.
구현 가이드¶
SDK로 다계정 로그인 관리 기능을 구현하는 방법을 안내합니다.
아래와 같은 순서로 각 단계마다 SDK에서 제공하는 API를 호출하여 단계 별 기능을 구현헙니다.
- 로그인 완료 후, 현재 로그인된 계정 정보 저장
- 저장된 계정으로 로그인 또는 저장된 계정 정보 삭제를 위해 저장된 계정 정보(PlayerID 리스트) 조회하기
- 유저가 다계정 로그인 관리 클릭 시 조회한 계정 정보가 PlayerID 리스트 UI로 노출되도록 구현
- PlayerID 리스트 UI에서 저장된 계정(PlayerID)으로 로그인하기 혹은 저장된 계정(PlayerID) 삭제하기 구현
1. 현재 로그인된 계정 정보 저장¶
현재 로그인된 계정 정보를 저장합니다. 저장된 계정 정보를 활용하면 이후 재로그인 시 다계정 로그인 관리 기능으로 보다 빠르게 로그인할 수 있습니다.
게임 클라이언트에서는 로그인된 계정 저장 구현 시, 아래와 같이 로그인된 계정 저장(새로운 계정 추가) 버튼 외 로그아웃 버튼 UI 요소를 구성하고 유저의 계정 저장 유무에 따라 로그인된 계정 저장 API 를 호출하도록 구현할 수 있습니다. 자세한 구현 방식은 위 로그아웃 시 계정 정보 저장 플로우를 참고하세요.
로그인된 계정 저장 API를 호출하는 예제 코드는 아래와 같습니다.
2. 저장된 계정 정보(PlayerID 리스트) 조회하기¶
SDK와 인증 서버에 저장된 계정 정보(PlayerID 리스트를 )조회합니다. 게임 클라이언트에서는 조회한 PlayerID 리스트를 활용하여 별도의 PlayerID 리스트 UI를 구현할 수 있습니다.
저장된 계정 정보(PlayerID 리스트) 조회하기 API를 호출하는 예제 코드는 아래와 같습니다.
AuthV4.getStoredPlayerIdList(new AuthV4.AuthV4PlayerIdListListener() {
@Override
public void onAuthV4GetPlayerIdList(ResultAPI result, ArrayList<Long> playerIdList) {
if (result.isSuccess()) {
if(playerIdList != null) {
for(Long playerId : playerIdList) {
// API 호출 성공
// playerId : 플레이어 아이디
}
}
}
}
});
3. 저장된 계정(PlayerID)으로 로그인하기¶
로그아웃 상태에서 다계정 로그인 관리 기능을 통해 저장된 계정(PlayerID)로 빠르게 로그인합니다.
저장된 계정으로 로그인하기 구현 순서는 아래와 같습니다.
- 유저가 로그아웃 후 재로그인 시, 로그인 화면에서 다계정 로그인 관리 클릭 → 저장된 계정 정보(PlayerID 리스트) 조회하기 API 호출
- 앞 단계에서 구현한 PlayerID 리스트 UI 노출
그림 2. 다계정 로그인 관리 를 통한 저장된 계정으로 로그인하기 구현 예시 -
유저가 PlayerID 리스트 UI에서 특정 계정을 선택 및 로그인 → '저장된 계정 정보(PlayerID) 로 로그인하는 API 호출
'저장된 PlayerID로 로그인하는 API'를 호출하는 예제 코드는 아래와 같습니다.AuthV4.signInWithStoredPlayerId(playerId, useAutoSignIn, (ResultAPI result, AuthV4.PlayerInfo playerInfo)=>{ if (result.isSuccess()) { // 인증 성공 // playerInfo : 인증된 사용자 정보 // 이메일 정보 조회 예시 foreach (KeyValuePair<AuthV4.ProviderType, AuthV4.ProviderInfo> entry in playerInfo.providerInfoData) { AuthV4.ProviderInfo providerInfo = entry.Value; if(providerInfo.providerEmail != null && providerInfo.providerEmail != "") { string email = providerInfo.providerEmail; break; } } } else if (result.needExit()) { // TODO: 앱 종료 기능을 구현하세요 // 예) Application.Quit(); } });
FHiveAuthV4::SignInWithStoredPlayerId(playerId, useAutoSignIn, FHiveAuthV4OnSignInDelegate::CreateLambda([this](const FHiveResultAPI& Result, const FHivePlayerInfo& PlayerInfo) { if (Result.IsSuccess()) { // 인증 성공 (PlayerInfo: 인증된 사용자 정보) // 이메일 정보 조회 예시 for (const auto& ProviderInfoEntry : PlayerInfo.ProviderInfoData) { FHiveProviderInfo ProviderInfo = ProviderInfoEntry.Value; FString Email = ProviderInfo.ProviderEmail; } } else if (Result.NeedExit()) { // TODO: 앱 종료 기능을 구현하세요 // 예) UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false); } }));
AuthV4::signInWithStoredPlayerId(playerId, useAutoSignIn, [=](ResultAPI const & result, PlayerInfo const & playerInfo) { if (result.isSuccess()) { // 인증 성공 // playerInfo: 인증된 사용자 정보 // 이메일 정보 조회 예시 for(auto it = playerInfo.providerInfoData.begin(); it != playerInfo.providerInfoData.end(); ++it) { hive::ProviderInfo providerInfo = it->second; if(!providerInfo.providerEmail.empty()) { std::string email = providerInfo.providerEmail; break; } } } else if (result.needExit()) { // TODO: 앱 종료 기능을 구현하세요 // Cocos2d-x 엔진 사용자 // 예) exit(0); // Unreal 엔진 사용자 // 예) UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, false); } });
```java AuthV4.signInWithStoredPlayerId(playerId, useAutoSignInState, object : AuthV4.AuthV4SignInListener { override fun onAuthV4SignIn(result: ResultAPI, playerInfo: AuthV4.PlayerInfo?) { if (result.isSuccess) { // 인증 성공 // playerInfo: 인증된 사용자 정보
// 이메일 정보 조회 예시 playerInfo?.let { for ((key, value) in it.providerInfoData) { var providerInfo: AuthV4.ProviderInfo = value if(providerInfo.providerEmail.isNotEmpty()) { val email = providerInfo.providerEmail break } } } } else if (result.needExit()) { // TODO: 앱 종료 기능을 구현하세요 // 예) exitProcess(0) } } }) ```
AuthV4.signInWithStoredPlayerId(playerId, useAutoSignInState, new AuthV4.AuthV4SignInListener() { @Override public void onAuthV4SignIn(ResultAPI result, AuthV4.PlayerInfo playerInfo) { if (result.isSuccess()) { // 인증 성공 // playerInfo: 인증된 사용자 정보 // 이메일 정보 조회 예시 if(playerInfo != null) { for (Map.Entry<AuthV4.ProviderType, AuthV4.ProviderInfo> entry : playerInfo.getProviderInfoData().entrySet()) { AuthV4.ProviderInfo providerInfo = entry.getValue(); if (providerInfo.getProviderEmail() != "") { String email = providerInfo.getProviderEmail(); break; } } } } else if (result.needExit()) { // TODO: 앱 종료 기능을 구현하세요 // 예) System.exit(0); } } });
AuthV4Interface.signInWithStoredPlayerId(playerId, useAutoSignIn: useAutoSignInState) { (result, playerInfo) in if result.isSuccess() { // 인증 성공 // playerInfo: 인증된 사용자 정보 // 이메일 정보 조회 예시 if let playerInfo = playerInfo { // providerEmail이 존재하는 providerInfo 탐색 (현재 로그인 진행된 provider) for key in playerInfo.providerInfoData.keys { if let providerInfo = playerInfo.providerInfoData[key], providerInfo.providerEmail.count > 0 { // providerEmail != "" email = providerInfo.providerEmail break } } } } else if result.needExit() { // TODO: 앱 종료 기능을 구현하세요 // 예) exit(0) } }
[HIVEAuthV4 signInWithStoredPlayerId: playerId useAutoSignIn: useAutoSignIn handler:^(HIVEResultAPI *result, HIVEPlayerInfo *playerInfo) { if([result isSuccess]){ // 인증 성공 // playerInfo: 인증된 사용자 정보 // 이메일 정보 조회 예시 if(playerInfo != nil) { // providerEmail이 존재하는 providerInfo 탐색 (현재 로그인 진행된 provider) for (NSString* key in playerInfo.providerInfoData.allKeys) { HIVEProviderInfo* providerInfo = playerInfo.providerInfoData[key]; if (providerInfo != nil && providerInfo.providerEmail.length > 0) { // providerEmail != "" email = providerInfo.providerEmail; break; } } } } else if ([result needExit]) { // TODO: 앱 종료 기능을 구현하세요 // 예) exit(0); } }];
4.저장된 계정(PlayerID) 삭제하기¶
로그아웃 상태에서 다계정 로그인 관리 기능을 통해 저장된 계정(PlayerID)을 삭제합니다.
노출된 PlayerID 리스트 UI에서 유저는 특정 계정을 선택하여 로그인하는 대신, 저장된 계정(PlayerID)을 삭제할 수 있습니다.
저장된 계정(PlayerID)을 삭제하는 API를 호출하는 예제 코드는 아래와 같습니다.
에러 코드¶
다계정 로그인 관리 기능 구현 중 SDK에서 전송하는 에러 코드에 대한 설명입니다.
Error Code | Message | Description |
NEED_INITIALIZE | AuthV4NotInitialized | SDK Setup이 되어 있지 않은 경우 (AuthV4.setup) |
IN_PROGRESS | AuthV4InProgress | API 응답을 받기전에 다시 호출할 경우 |
INVALID_SESSION | AuthV4SessionNotExist | 요청한 PlayerID가 리스트에 존재하지 않을 경우 |
INVALID_SESSION | AuthV4SessionProcessingFail | 세션 저장/삭제 실패 |
구현 유의 사항¶
다계정 로그인 관리 기능 구현 시, 아래의 사항을 유의하세요.
-
API 연속 호출 제한
인터페이스 호출 후 응답을 받기 전에 API를 반복 호출할 경우, 정상적인 응답을 받지 못할 수 있습니다. 반드시 응답을 받은 후 다음 API를 호출하세요.
-
로그인 세션 저장 방식 주의
로그인 세션은 로그인된 계정 기준이 아닌 디바이스 기준으로 저장됩니다. 구현 방식에 따라 동일 디바이스를 사용하는 다른 사용자에게 로그인 정보가 노출될 수 있으므로, 보안이 필요한 서비스에서는 로그인 세션 저장 기능을 주의하여 사용하세요.
-
COPPA 적용(ageGateU13 = true) 시 다계정 로그인 관리 기능 사용 금지
다계정 로그인 관리 기능은 계정 정보를 저장하고 이를 통해 빠르게 로그인할 수 있는 기능이지만, COPPA 적용 대상자에 한해서는 계정 정보를 저장하거나 세션 기반 로그인을 제공되지 않도록 다계정 로그인 관리 기능을 제한하세요.
SDK에서는 자동으로 다계정 로그인 관리 기능 사용을 제한하지 않으므로, 게임 내에서 별도 대응이 필요합니다. 또한 COPPA는 만 13세 미만 사용자에 대한 개인정보 수집, 사용, 공유를 제한하고 있으므로, 세션 정보를 포함한 관련 데이터의 저장을 지양하도록 권장합니다.