GS2-Account
Game Server Services가 제공하는 계정 시스템은 “익명 계정"이라는 종류의 계정 시스템입니다. 일본에서는 널리 사용되는 방식이지만, 많은 지역에서는 낯설게 느껴지는 계정 시스템일 수 있습니다.
그러나 이 계정 시스템은 게임에 있어서 매우 이치에 맞는 계정 시스템입니다.
익명 계정이란 무엇인가요?
일반적으로 떠올리는 계정 관리는 로그인ID가 있고 비밀번호가 있는 방식일 것입니다. 익명 계정도 그 점은 다르지 않습니다.
그러나 로그인ID와 비밀번호가 모두 시스템에 의해 무작위로 결정된다는 것이 가장 큰 특징입니다.
시스템에서 발급한 로그인ID·비밀번호를 디바이스의 로컬 스토리지에 저장해 두고, 두 번째 이후에는 그 정보를 이용해 로그인함으로써 게임을 재개할 수 있습니다.
graph TD
Startup --> LoadSave{"디바이스 스토리지에서<br/>익명 계정을 불러오기"}
LoadSave -- Not Exists --> CreateAccount["익명 계정을 생성"]
CreateAccount --> SaveAccount["익명 계정을 저장"]
SaveAccount --> Login["로그인"]
LoadSave -- Exists --> Login
Login --> InGame익명 계정의 장점
- 간단한 등록: 사용자는 번거로운 등록 절차를 거치지 않고도 게임을 바로 시작할 수 있습니다. 특히 무료로 플레이할 수 있는 F2P 스타일의 게임에서는 많은 사용자가 게임을 시도해 보도록 하기 위해 등록의 수고를 줄이는 것이 중요합니다.
- 플레이어 정보 보호: 로그인ID나 비밀번호를 직접 설정할 필요가 없으므로 개인정보와 프라이버시가 보호됩니다. 또한 비밀번호 재설정이나 변경도 필요하지 않습니다.
- 이전의 용이성: 이전 정보로 각종 플랫폼의 ID 기반이나 SNS 계정 등을 이용할 수 있어, 서로 다른 디바이스나 앱 간의 이전을 원활하게 수행할 수 있습니다.
- 익명 플레이 경험: 플레이어는 익명으로 게임을 플레이할 수 있으므로, 실명이나 닉네임을 사용하지 않아도 되어 보다 자유로운 플레이 경험을 즐길 수 있습니다.
이전
디바이스의 로컬 스토리지만큼 신뢰성이 낮은 곳은 없습니다. 디바이스를 떨어뜨릴 수도 있고, 디바이스를 고장 낼 수도 있습니다. 그때 게임 데이터를 모두 잃어버리게 된다면, 그것은 절망적인 상황입니다.
게임 플레이어는 익명 계정으로 게임을 체험하고, 정말로 마음에 든다면 “이전 정보"를 등록할 수 있습니다. 이전 정보에는 각종 플랫포머의 ID 기반 정보를 이용해도 좋고, SNS 계정을 설정해도 좋습니다. 게임 퍼블리셔의 ID 기반도 좋은 선택일 것입니다.
이전 정보는 슬롯 번호 0~1024 범위에서 관리되며, 최대 1,025종류까지 등록할 수 있습니다. 용도에 따라 각 슬롯에 서로 다른 인증 수단을 연결함으로써 폭넓은 이전 시나리오를 구현할 수 있습니다.
이전 정보에는 각종 ID 기반에서 인증한 결과로 얻은 사용자ID 등을 기록합니다. 그리고 완전히 새로운 디바이스에서 이전을 실행합니다. 각종 ID 기반에 로그인하고, 얻어낸 사용자ID로 이전을 실행하면 과거에 이전 정보를 설정해 둔 익명 계정으로 로그인하기 위한 정보를 얻을 수 있습니다.
이 정보를 다시 디바이스의 로컬 스토리지에 저장하여 이후의 로그인에 이용합니다.
graph TD InGame -- 게임이 마음에 들었다 --> AddTakeOverSetting["이전 정보를 등록"] AddTakeOverSetting --> BrokenDevice["스마트폰이 고장 남"] NewDevice["새 스마트폰"] --> DoTakeOver["이전 정보를 입력하고<br/>이전을 실행"] DoTakeOver -- 익명 계정을 복원 --> SaveAccount["익명 계정을 저장"] SaveAccount --> Login["로그인"]
OpenID Connect 연동
이전 정보의 등록, 이전 처리의 실행에 OpenID Connect를 준수하는 인증 시스템을 사용하기 위한 지원이 마련되어 있습니다.
인증 서비스 등록
OpenID Connect 연동 기능을 사용하려면 마스터 데이터 설정이 필요합니다. 각 이전 슬롯마다 연동 대상을 지정할 수 있으며, 최대 1,025종류의 인증 수단에 대응할 수 있습니다. 설정 항목은 다음과 같습니다.
- 인증 서비스의 OpenID Connect Discovery 엔드포인트 URL
- 인증 서비스의 클라이언트ID
- 인증 서비스의 클라이언트 시크릿
- 인증 완료 후 이동할 URL
- 추가로 요구할 스코프 값
- 인증 결과로 취득할 추가 반환 값
Sign in with Apple은 클라이언트 시크릿을 동적으로 계산해야 하는데, 이 계산 기능도 갖추고 있습니다. 인증 서비스의 OpenID Connect Discovery 엔드포인트 URL에 Sign in with Apple의 URL을 지정한 경우에는 클라이언트 시크릿 대신
- Apple Developer의 팀ID
- Apple에서 발급한 비밀 키의 ID
- Apple에서 발급한 비밀 키의 페이로드(PEM)
를 등록할 수 있으며, 클라이언트 시크릿은 GS2가 필요에 따라 계산합니다.
인증 처리
인증 처리에 관한 지원도 제공하고 있습니다.
인증 서비스의 인증 페이지 URL 취득 API와, 인증 서비스에서 인증 후 콜백을 받기 위한 엔드포인트를 제공하고 있습니다.
인증 서비스의 콜백 URL에는 다음 형식을 따르는 값을 설정해 주세요.
https://account.{region}.gen2.gs2io.com/{ownerId}/{namespaceName}/type/{type}/callback
https://account.ap-northeast-1.gen2.gs2io.com/aAbBcCdD-project/namespace-0001/type/0/callback인증 콜백을 받으면 다음 URL로 이동합니다.
https://account.{region}.gen2.gs2io.com/{ownerId}/{namespaceName}/type/{type}/done?id_token={idToken}URL의 쿼리 문자열에 인증을 통해 얻은 ID 토큰이 전달됩니다.
Firebase Authentication과 같은 GS2 이외의 인증 기능 이용
ID 토큰만 얻을 수 있다면 그 수단이 반드시 위의 절차일 필요는 없습니다.
인증 서비스 이외로부터의 등록 거부
마스터 데이터에서 OpenID Connect 연동이 설정된 슬롯은 임의의 사용자 식별자와 비밀번호를 사용한 이전 정보 등록 및 이전 실행을 할 수 없게 됩니다.
구현 예제
익명 계정 생성
GS2-Account는 네임스페이스라는 계층을 가지며, 하나의 프로젝트 내에 여러 계정 풀을 가질 수 있습니다. 네임스페이스의 용도는 특별히 정해져 있지 않으며, 배포 지역별로 네임스페이스를 나누는 등 필요에 따라 활용할 수 있습니다.
var result = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).CreateAsync();
var item = await result.ModelAsync();
var userId = item.UserId;
var password = item.Password; const auto NamespaceName = "namespace-0001";
const auto Future = Gs2->Account->Namespace(
NamespaceName
)->Create();
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();
const auto Future2 = Result->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result2 = Future2->GetTask().Result();
const auto UserId = Result2->UserId;
const auto Password = Result2->Password;var domain = ez.account.namespace_(
"namespace-0001"
)
var async_result = await domain.create(
)
if async_result.error != null:
push_error(str(async_result.error))
return
var result = async_result.result익명 계정을 사용한 로그인
GS2-Account의 인증 처리는 일정 기간마다 다시 수행해야 합니다. 이러한 처리를 자동으로 수행해 주는 유틸리티 클래스인 Gs2AccountAuthenticator를 사용한 로그인 예시를 보여드립니다.
GS2의 API 클라이언트에 대해 Gs2AccountAuthenticator와 생성한 익명 계정의 사용자ID, 비밀번호를 지정함으로써 로그인 중인 세션 정보를 나타내는 GameSession 인스턴스를 얻을 수 있습니다. 이후, 로그인 중인 플레이어의 정보에 접근하려면 이 GameSession 인스턴스를 사용하게 됩니다.
var gameSession = await gs2.LoginAsync(
new Gs2AccountAuthenticator(
accountSetting: new AccountSetting {
accountNamespaceName = this.accountNamespaceName,
}
),
account.UserId,
account.Password
); const auto NamespaceName = "namespace-0001";
const auto KeyId = "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001";
const auto Future = Profile->Login(
MakeShareable<Gs2::UE5::Util::IAuthenticator>(
new Gs2::UE5::Util::FGs2AccountAuthenticator(
NamespaceName,
KeyId
)
),
UserId,
Password
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();var authenticator = Gs2AccountAuthenticator.new(
"namespace-0001",
"grn:gs2:{region}:{ownerId}:key:namespace-0001:key:key-0001"
)
var game_session = Gs2GameSession.new(
authenticator, connection, account.user_id, account.password
)
var async_result = await game_session.login()
if async_result.error != null:
push_error(str(async_result.error))
return인증 시 발생하는 특수 예외
인증 처리에서는 다음과 같은 특수 예외가 발생할 수 있습니다.
| 예외 타입 | 기반 타입 | 설명 |
|---|---|---|
PasswordIncorrectException | UnauthorizedException | 비밀번호가 일치하지 않음 |
BannedInfinityException | UnauthorizedException | 계정이 이용 정지되어 있음 |
게임 측에서는 이러한 예외를 적절히 처리하고, 사용자에게 오류 메시지를 표시하는 것을 권장합니다.
Gs2AccountAuthenticator / FGs2AccountAuthenticator를 통한 로그인에서 위 예외가 어떤 형태로 노출되는지는 SDK 래퍼의 구현에 따라 다르므로, 자세한 내용은 API 레퍼런스를 참조해 주세요.
이전 정보 등록
슬롯 번호에 서로 다른 값을 지정함으로써 하나의 계정에 대해 여러 개의 이전 설정을 유지할 수 있습니다. 슬롯 번호는 0~1024 범위로 지정할 수 있으며, 최대 1,025종류의 이전 방법을 등록할 수 있습니다.
예를 들어, 슬롯 번호:0에 이메일 주소·비밀번호를, 슬롯 번호:1에 소셜 미디어의 ID 정보를 저장하도록 하여 게임 플레이어가 원하는 이전 수단을 선택할 수 있도록 하는 운영이 가능합니다.
이전 설정의 비밀번호 처리
이전 정보에 등록하는 password는 서버 측에서 해시화되어 저장됩니다. 한 번 설정된 비밀번호는 API로 다시 취득할 수 없으며, 분실한 경우에는 새로운 이전 설정을 다시 발급해야 합니다. 이 구조를 통해 플레이어의 인증 정보를 안전하게 유지하면서 부정 이용을 방지합니다.
var result = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).TakeOver(
type: 0
).AddTakeOverSettingAsync(
userIdentifier: "user-0001@gs2.io",
password: "password-0001"
); const auto NamespaceName = "namespace-0001";
const auto Type = 0;
const auto UserIdentifier = "user-0001@gs2.io";
const auto Password = "password-0001";
const auto Future = Gs2->Account->Namespace(
NamespaceName
)->Me(
AccessToken
)->TakeOver(
Type
)->AddTakeOverSetting(
UserIdentifier,
Password
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
).me(game_session).take_over(0)
var async_result = await domain.add_take_over_setting(
"user-0001@gs2.io", "password-0001"
)
if async_result.error != null:
push_error(str(async_result.error))
return등록된 이전 정보 목록 조회
var items = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).TakeOversAsync(
).ToListAsync(); const auto NamespaceName = "namespace-0001";
const auto It = Gs2->Account->Namespace(
NamespaceName
)->Me(
AccessToken
)->TakeOvers();
TArray<Gs2::UE5::Account::Model::FEzTakeOverPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}var iterator = ez.account.namespace_(
"namespace-0001"
).me(
game_session
).take_overs(
)
var async_result = await iterator.load()
if async_result.error != null:
# 오류를 처리
push_error(str(async_result.error))
return
var items = async_result.result이전 실행
string userId;
string password;
try {
var result = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).DoTakeOverAsync(
type: 0,
userIdentifier: "user-0001@gs2.io",
password: "password-0001"
);
var item = await result.ModelAsync();
userId = item.UserId;
password = item.Password;
} catch(Gs2.Gs2Account.Exception.PasswordIncorrectException e) {
// Incorrect password specified.
} const auto NamespaceName = "namespace-0001";
const auto Type = 0;
const auto UserIdentifier = "user-0001@gs2.io";
const auto Password = "password-0001";
const auto Future = Gs2->Account->Namespace(
NamespaceName
)->DoTakeOver(
Type,
UserIdentifier,
Password
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
auto e = Future->GetTask().Error();
if (e->IsChildOf(Gs2::Account::Error::FPasswordIncorrectError::Class))
{
// Incorrect password specified.
}
return false;
}
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
)
var async_result = await domain.do_take_over(
null, # type
"user-0001@gs2.io", # user_identifier
"password-0001" # password
)
if async_result.error != null:
if async_result.error.type == "PasswordIncorrectException":
# 비밀번호 지정이 올바르지 않습니다
pass
push_error(str(async_result.error))
return
var result = async_result.resultOpenID Connect 인증 처리
Unity 샘플에서는 인앱 브라우저로 unity-webview를 사용하고 있습니다. Godot에서는 사용하는 WebView 플러그인의 URL 변경 시그널과 URL 표시 API로 바꿔서 읽어 주세요.
public static async UniTask<string> OpenAuthentication(
WebViewObject webView,
Gs2Domain gs2,
string namespaceName,
IGameSession gameSession,
int type
) {
string idToken = null;
webView.Init(
separated: true,
ld: url =>
{
if (new Uri(url).LocalPath.EndsWith("/done")) {
var codeField = new Uri(url).Query.Replace("?", "").Split("&").Select(v => new KeyValuePair<string,string>(v[..v.IndexOf("=", StringComparison.Ordinal)], v[(v.IndexOf("=", StringComparison.Ordinal)+1)..])).FirstOrDefault(v => v.Key == "id_token");
idToken = Uri.UnescapeDataString(codeField.Value);
webView.SetVisibility(false);
}
}
);
webView.LoadURL(
(await gs2.Account.Namespace(
namespaceName
).Me(
gameSession
).GetAuthorizationUrlAsync(
type
)).AuthorizationUrl
);
webView.SetInteractionEnabled(true);
webView.SetVisibility(true);
await UniTask.WaitWhile(() => idToken == null);
return idToken;
}func open_authentication(web_view, ez, namespace_name: String, type: int) -> String:
var state = {"id_token": ""}
web_view.url_changed.connect(func(url):
var url_text = str(url)
if not url_text.get_slice("?", 0).ends_with("/done"):
return
for field in url_text.get_slice("?", 1).split("&"):
var pair = field.split("=", true, 1)
if pair.size() == 2 and pair[0] == "id_token":
state.id_token = pair[1].uri_decode()
web_view.hide()
)
var async_result = await ez.account.namespace_(
namespace_name
).get_authorization_url(type)
if async_result.error != null:
push_error(str(async_result.error))
return ""
# 사용 중인 WebView 플러그인의 URL 표시 API로 교체합니다.
web_view.open_url(async_result.result.authorization_url)
while state.id_token.is_empty():
await Engine.get_main_loop().process_frame
return state.id_tokenOpenID Connect를 사용한 이전 정보 등록
var result = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).TakeOver(
type: 0
).AddTakeOverSettingOpenIdConnectAsync(
idToken: "id-token"
); const auto NamespaceName = "namespace-0001";
const auto Type = 0;
const auto Future = Gs2->Account->Namespace(
NamespaceName
)->Me(
AccessToken
)->TakeOver(
Type
)->AddTakeOverSettingOpenIdConnect(
"id-token"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
).me(game_session).take_over(
0
)
var async_result = await domain.add_take_over_setting_open_id_connect(
"0123456789" # id_token
)
if async_result.error != null:
push_error(str(async_result.error))
return
var result = async_result.resultOpenID Connect를 사용한 이전 실행
var result = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).DoTakeOverOpenIdConnectAsync(
type: 0,
idToken: "id-token"
);
var item = await result.ModelAsync();
var userId = item.UserId;
var password = item.Password; const auto NamespaceName = "namespace-0001";
const auto Type = 0;
const auto Future = Gs2->Account->Namespace(
NamespaceName
)->DoTakeOverOpenIdConnect(
Type,
"id-token"
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError())
{
return false;
}
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
)
var async_result = await domain.do_take_over_open_id_connect(
null, # type
"0123456789" # id_token
)
if async_result.error != null:
push_error(str(async_result.error))
return
var result = async_result.result기타 기능
플랫폼ID 관리
GS2-Account에서는 X나 Instagram 등 각종 플랫폼에서 사용되는 사용자ID를 계정에 연결하여 저장할 수 있습니다. 다른 플레이어는 이러한 ID로 검색하여 SNS상의 친구를 게임 내로 초대하는 등의 연동이 가능합니다. 플랫폼ID도 이전 정보와 마찬가지로 0~1024 슬롯 번호로 관리되며, 하나의 계정에 여러 개를 등록할 수 있습니다.
플랫폼ID 등록
var domain = gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PlatformId(
type: 0,
userIdentifier: "123456"
);
var result = await domain.AddPlatformIdSettingAsync(
);
var item = await result.ModelAsync(); const auto Domain = Gs2->Account->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->PlatformId(
0, // type
"123456" // userIdentifier
);
const auto Future = Domain->AddPlatformIdSetting(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
).me(game_session).platform_id(0)
var async_result = await domain.add_platform_id_setting("123456")
if async_result.error != null:
push_error(str(async_result.error))
return
var item = async_result.result플랫폼ID를 이용한 사용자 검색
var domain = gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PlatformId(
type: 0,
userIdentifier: "123456"
);
var result = await domain.FindPlatformUserAsync(
);
var item = await result.ModelAsync(); const auto Domain = Gs2->Account->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->PlatformId(
0, // type
"123456" // userIdentifier
);
const auto Future = Domain->FindPlatformUser(
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
// obtain changed values / result values
const auto Future2 = Future->GetTask().Result()->Model();
Future2->StartSynchronousTask();
if (Future2->GetTask().IsError()) return false;
const auto Result = Future2->GetTask().Result();var domain = ez.account.namespace_(
"namespace-0001"
).me(game_session).platform_id(0)
var async_result = await domain.find_platform_user("123456")
if async_result.error != null:
push_error(str(async_result.error))
return
var item = async_result.result등록된 플랫폼ID 목록
var items = await gs2.Account.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).PlatformIdsAsync(
).ToListAsync(); const auto It = Gs2->Account->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->PlatformIds();
TArray<Gs2::UE5::Account::Model::FEzPlatformIdPtr> Result;
for (auto Item : *It)
{
if (Item.IsError())
{
return false;
}
Result.Add(Item.Current());
}var iterator = ez.account.namespace_(
"namespace-0001"
).me(
game_session
).platform_ids(
)
var async_result = await iterator.load()
if async_result.error != null:
# 오류를 처리
push_error(str(async_result.error))
return
var items = async_result.result커스텀 스크립트 트리거
계정 생성이나 인증, 이전, BAN/해제 등의 처리 전후에 GS2-Script를 호출하는 이벤트 트리거를 설정할 수 있습니다. 게임 고유의 검증이나 감사를 수행할 때 활용할 수 있습니다. 트리거는 동기·비동기 실행 방식을 선택할 수 있으며, 비동기 처리에서는 GS2-Script나 Amazon EventBridge를 이용한 외부 연동도 가능합니다.
설정 가능한 주요 이벤트 트리거와 스크립트 설정명은 다음과 같습니다.
createAccountScript(완료 알림:createAccountDone): 계정 생성 전후authenticationScript(완료 알림:authenticationDone): 로그인 처리 전후createTakeOverScript(완료 알림:createTakeOverDone): 이전 정보 등록 전후doTakeOverScript(완료 알림:doTakeOverDone): 이전 실행 전후banScript(완료 알림:banDone): BAN 등록 전후unBanScript(완료 알림:unBanDone): BAN 해제 전후
비밀번호 자동 변경
네임스페이스 설정에서 이전 시 비밀번호를 자동으로 변경할 수 있습니다. 이 기능을 사용하면 이전 실행 시 이전 디바이스에서 로그인할 수 없게 만들 수 있습니다.
다만, 이 기능은 여러 디바이스에 의한 동시 로그인을 완전히 억제하는 것은 아닙니다. GS2-Gateway의 기능을 이용하면 동일 사용자가 동시에 로그인하는 것을 거부하는 기능이 있으며, 이쪽이 더 강력하게 동시 로그인에 대응할 수 있습니다.
사용자 데이터 익명화
Namespace 설정에서 differentUserIdForLoginAndDataRetention을 활성화하면, 로그인에 사용하는 사용자ID와 데이터 보관용 데이터 소유자ID가 각각 별도로 발급됩니다. 앱이 인증할 때는 기존과 마찬가지로 사용자ID를 이용하고, 서버 내의 사용자 데이터에는 데이터 소유자ID를 사용함으로써 개인정보와 게임 내 데이터를 분리할 수 있습니다. 이 기능을 이용하면 데이터 분석을 익명화된 사용자 데이터를 대상으로 수행할 수 있게 되어, 프라이버시 보호와 운영에 사용하는 데이터 수집을 양립할 수 있습니다.
마스터 데이터 관리
마스터 데이터를 등록함으로써 마이크로서비스에서 사용 가능한 데이터와 동작을 설정할 수 있습니다.
마스터 데이터의 종류에는 다음과 같은 것이 있습니다.
TakeOverTypeModel: 슬롯 번호별 이전 설정과 OpenID Connect 정보
다음은 OpenID Connect 연동을 설정하는 마스터 데이터의 JSON 예시입니다.
{
"version": "2024-07-30",
"takeOverTypeModels": [
{
"type": 0,
"metadata": "Google",
"openIdConnectSetting": {
"configurationPath": "https://accounts.google.com/.well-known/openid-configuration",
"clientId": "your-client-id",
"clientSecret": "your-client-secret"
}
}
]
}Sign in with Apple의 경우에는 clientSecret 대신 appleTeamId / appleKeyId / applePrivateKeyPem을 지정합니다. 자세한 내용은 인증 서비스 등록을 참조해 주세요.
마스터 데이터의 등록은 관리 콘솔에서 등록하는 것 외에도, GitHub에서 데이터를 반영하거나 GS2-Deploy를 사용해 CI에서 등록하는 등의 워크플로우를 구성할 수 있습니다.
플레이어별 시각 오프셋 설정
Game Server Services의 계정 정보에는 시각 오프셋을 부여할 수 있습니다. 이 오프셋에는 미래를 향해 플레이어가 몇 분 후의 상태로 동작할지를 설정할 수 있습니다.
이 기능을 이용하면 프로덕션 환경에서도 QA 담당자가 1시간 미래의 상태로 게임을 플레이하게 하는 것이 가능합니다. 이렇게 함으로써 이벤트 시작 시각이 되어도 이벤트가 열리지 않는 등의 문제를 미리 발견할 수 있습니다.
계정 이용 정지
게임 내에서 품행이 나쁜 플레이어를 이용 정지시킬 수 있습니다.
BAN 상태는 사유나 해제 예정 일시를 포함한 BanStatus로 여러 건 기록할 수 있으며, 계정에는 BAN 여부를 나타내는 플래그도 유지됩니다.
BAN 추가·해제 전후에는 GS2-Script를 호출하는 이벤트 트리거를 설정할 수 있어, 외부 시스템과의 연동을 수반하는 제어가 가능합니다.