Google Play 게임즈 서비스에 대한 서버 측 액세스 사용 설정

게임에서 백엔드 서버를 사용하는 경우 Google 로그인을 사용하여 플레이어를 인증하고 플레이어의 ID를 백엔드 서버에 안전하게 전달하는 것이 좋습니다. 이렇게 하면 기기를 거치는 동안 잠재적인 조작에 노출되지 않고 게임이 플레이어의 ID와 기타 데이터를 안전하게 가져올 수 있습니다.

이 시나리오에서는 게임에서 평소와 같이 Google Play 게임즈 서비스에 로그인하라는 메시지를 플레이어에게 표시합니다. 플레이어가 성공적으로 로그인하면 GoogleSignInAccount 객체에는 클라이언트가 서버에 전달하는 특수 일회용 코드(서버 인증 코드라고 함)가 포함됩니다. 그런 다음 서버에서 서버 인증 코드를 서버가 Google Play 게임즈 서비스 API를 호출하는 데 사용할 수 있는 OAuth 2.0 토큰으로 교환합니다.

게임에 로그인을 추가하는 방법을 자세히 알아보려면 Android 게임의 로그인을 참고하세요.

Google 로그인을 사용하여 플레이어를 인증하는 방법을 보여주는 자세한 코드 샘플은 GitHub의 clientserverskeleton 샘플을 참고하세요.

오프라인 액세스에는 다음 단계가 필요합니다.

  1. Google Play Console: 게임 서버의 사용자 인증 정보를 만듭니다. 사용자 인증 정보의 OAuth 클라이언트 유형은 '웹'입니다.
  2. Android 앱: 로그인의 일환으로 서버의 사용자 인증 정보 서버 인증 코드를 요청하여 서버에 전달합니다.
  3. 게임 서버: Google 인증 서비스를 사용하여 서버 인증 코드를 OAuth 액세스 토큰으로 교환한 후 이를 사용하여 Play 게임즈 서비스 REST API를 호출합니다.

시작하기 전에

Google 로그인을 게임에 통합하려면 먼저 Google Play 게임즈 서비스 설정에 설명된 대로 Google Play Console에서 게임을 추가해야 합니다.

게임용으로 연결된 서버 측 웹 애플리케이션 만들기

Google Play 게임즈 서비스는 웹 게임에 백엔드 지원을 제공하지 않습니다. 그러나 Android 게임 서버에는 백엔드 서버 지원을 제공합니다.

서버 측 앱에서 Google Play 게임즈 서비스용 REST API를 사용하려면 다음 단계를 따르세요.

  1. Google Play Console연결된 앱 섹션에서 게임과 연결된 웹 앱을 만듭니다. launch_url는 이 흐름에 사용되지 않으며 비워 둘 수 있습니다.
  2. 앱의 사용자 인증 정보 정보를 가져오려면 다음 단계를 따르세요.
    1. Google Play Console의 게임에서 게임 세부정보를 클릭합니다.
    2. API 콘솔 프로젝트 섹션까지 아래로 스크롤하고 API 콘솔 프로젝트의 링크를 클릭합니다.
    3. Google API 콘솔의 API 및 서비스 & gt; 사용자 인증 정보 화면에서 웹 앱의 client_secret.json 파일을 다운로드하여 서버가 액세스할 수 있는 위치에 저장합니다. 나중에 참조할 수 있도록 사용자 인증 정보의 클라이언트 ID를 기록합니다.
  3. 게임의 클라이언트 앱에서 요청을 수락할 준비가 되도록 서버 측 앱을 다시 시작합니다.

클라이언트에서 로그인 수행

GoogleSignInClient 클래스는 현재 로그인한 플레이어의 계정을 검색하고, 이전에 기기에서 기기의 계정으로 로그인하지 않은 경우 플레이어를 로그인시키는 기본 진입점입니다.

로그인 클라이언트를 만들려면 다음 단계를 따르세요.

  1. GoogleSignInOptions 객체를 통해 로그인 클라이언트를 만듭니다. GoogleSignInOptions.Builder에서 로그인을 구성하려면 GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN를 지정해야 합니다.
  2. 또한 서버의 클라이언트 ID를 매개변수로 사용하여 GoogleSignInOptions.Builder.requestServerAuthCode() 메서드를 호출하여 게임에 백엔드 서버의 인증 코드를 지정해야 합니다. 서버 인증 코드 가져오기에 설명된 대로 나중에 백엔드 서버의 액세스 토큰에 대한 인증 코드를 검색합니다.
  3. GoogleSignIn.getClient() 메서드를 호출하고 이전에 구성한 옵션을 전달합니다. 호출이 성공하면 Google Sign-In API가 GoogleSignInClient의 인스턴스를 반환합니다.
  4. GoogleSignInClient 인스턴스를 가져온 후에는 자동 로그인 실행에 설명된 대로 활동의 onResume()에서 자동으로 플레이어 로그인을 진행해야 합니다.

예를 들면 다음과 같습니다.

private static final int RC_SIGN_IN = 9001;
private GoogleSignInClient mGoogleSignInClient;

private void startSignInForAuthCode() {

  // Client ID for your backend server.
  String webClientId = getString(R.string.webclient_id);

  GoogleSignInOptions signInOption = new
      GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
      .requestServerAuthCode(webClientId)
      .build();

  GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption);
  Intent intent = signInClient.getSignInIntent();
  startActivityForResult(intent, RC_SIGN_IN);
}

서버 인증 코드 가져오기

게임에서 백엔드 서버의 액세스 토큰에 사용할 수 있는 서버 인증 코드를 검색하려면 Google 로그인에서 플레이어 로그인 성공 시 반환하는 GoogleSignInAccount 객체의 getServerAuthCode() 메서드를 호출합니다.

예를 들면 다음과 같습니다.


// Auth code to send to backend server.
private String mServerAuthCode;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  if (requestCode == RC_SIGN_IN) {
    GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
    if (result.isSuccess()) {
      mServerAuthCode = result.getSignInAccount().getServerAuthCode();
    } else {
      String message = result.getStatus().getStatusMessage();
      if (message == null || message.isEmpty()) {
        message = getString(R.string.signin_other_error);
      }
      new AlertDialog.Builder(this).setMessage(message)
          .setNeutralButton(android.R.string.ok, null).show();
    }
  }
}

서버 인증 코드를 서버의 액세스 토큰으로 교환

서버 인증 코드를 백엔드 서버로 보내 액세스 및 갱신 토큰으로 교환합니다. 액세스 토큰을 사용하여 플레이어를 대신하여 Google Play 게임즈 서비스 API를 호출하고 선택적으로 갱신 토큰을 저장하여 액세스 토큰이 만료되면 새 액세스 토큰을 획득합니다.

다음 코드 스니펫은 자바 프로그래밍 언어로 서버 측 코드를 구현하여 서버 인증 코드를 액세스 토큰으로 교환하는 방법을 보여줍니다. clientserverskeleton 샘플 앱을 사용합니다.

/**
 * Exchanges the authcode for an access token credential.  The credential
 * is the associated with the given player.
 *
 * @param authCode - the non-null authcode passed from the client.
 * @param player   - the player object which the given authcode is
 *                 associated with.
 * @return the HTTP response code indicating the outcome of the exchange.
 */
private int exchangeAuthCode(String authCode, Player player) {
try {

    // The client_secret.json file is downloaded from the Google API
    // console.  This is used to identify your web application.  The
    // contents of this file should not be shared.
    //
    File secretFile = new File("client_secret.json");

    // If we don't have the file, we can't access any APIs, so return
    // an error.
    if (!secretFile.exists()) {
        log("Secret file : " + secretFile
                .getAbsolutePath() + "  does not exist!");
        return HttpServletResponse.SC_FORBIDDEN;
    }

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(
            JacksonFactory.getDefaultInstance(), new
            FileReader(secretFile));

    // Extract the application id of the game from the client id.
    String applicationId = extractApplicationId(clientSecrets
            .getDetails().getClientId());

    GoogleTokenResponse tokenResponse =
            new GoogleAuthorizationCodeTokenRequest(
            HTTPTransport,
            JacksonFactory.getDefaultInstance(),
            "https://oauth2.googleapis.com/token",
            clientSecrets.getDetails().getClientId(),
            clientSecrets.getDetails().getClientSecret(),
            authCode,
            "")
            .execute();

    log("hasRefresh == " + (tokenResponse.getRefreshToken() != null));
    log("Exchanging authCode: " + authCode + " for token");
    Credential credential = new Credential
            .Builder(BearerToken.authorizationHeaderAccessMethod())
            .setJsonFactory(JacksonFactory.getDefaultInstance())
            .setTransport(HTTPTransport)
            .setTokenServerEncodedUrl("https://www.googleapis.com/oauth2/v4/token")
            .setClientAuthentication(new HttpExecuteInterceptor() {
                @Override
                public void intercept(HttpRequest request)
                        throws IOException {
                        }
            })
            .build()
            .setFromTokenResponse(tokenResponse);

    player.setCredential(credential);

    // Now that we have a credential, we can access the Games API.
    PlayGamesAPI api = new PlayGamesAPI(player, applicationId,
            HTTPTransport, JacksonFactory.getDefaultInstance());

    // Call the verify method, which checks that the access token has
    // access to the Games API, and that the player id used by the
    // client matches the playerId associated with the accessToken.
    boolean ok = api.verifyPlayer();

    // Call a Games API on the server.
    if (ok) {
        ok = api.updatePlayerInfo();
        if (ok) {
            // persist the player.
            savePlayer(api.getPlayer());
        }
    }

    return ok ? HttpServletResponse.SC_OK :
            HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

  } catch (IOException e) {
    e.printStackTrace();
  }
  return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}

로그인한 플레이어를 대신하여 백엔드 서버에서 Google API에 액세스하는 방법에 대해 자세히 알아보려면 서버 측 액세스 사용 설정을 참조하세요.

플레이어 로그아웃 처리

게임에서 플레이어를 로그아웃시키려면 GoogleSignInClient에서 signOut() 메서드를 호출합니다. 코드 스니펫의 예는 플레이어 로그아웃을 참고하세요.

서버에서 REST API 호출

사용 가능한 API 호출에 관한 자세한 내용은 Google Play 게임즈 서비스용 REST API를 참고하세요.

유용할 수 있는 REST API 호출의 예는 다음과 같습니다.

플레이어

  • 로그인한 플레이어의 ID와 프로필 데이터를 가져오고 싶은가요? 'me'를 ID로 사용하여 Players.get을 호출합니다.

친구

친구를 자세히 설명하는 친구 가이드를 검토하세요.

  • 플레이어의 친구 목록을 가져오고 싶으세요? 'friends_all'collection로 사용하여 Players.list를 호출합니다.
  • 친구 목록에 대한 액세스 권한이 있는지 확인하시겠습니까? me에 대해 Players.get을 호출하고 응답에서 profileSettings.friendsListVisibility 필드를 확인합니다.

업적

업적을 더 자세히 설명하는 업적 가이드를 검토합니다.

  • 현재 업적 목록을 확인하고 싶으신가요? AchievementDefinitions.list를 호출할 수 있습니다.
  • 이를 Achievements.list 호출과 결합하여 플레이어가 달성한 업적을 확인합니다.
  • 플레이어가 업적을 달성했나요? Achievements.unlock을 사용하여 달성
  • 플레이어가 일부 업적을 달성했나요? Achievements.increment를 사용하여 진행 상황을 보고하고 플레이어가 업적을 달성했는지 확인합니다.
  • 아직 프로덕션 단계에 있지 않은 게임을 디버깅하나요? Management API에서 Achievements.reset 또는 Achievements.resetAll을 호출하여 업적을 원래 상태로 재설정합니다.

리더보드

리더보드에 대해 자세히 설명하는 리더보드 가이드를 확인하세요.

  • 게임의 모든 스코어보드 목록을 원하나요? Leaderboards.list를 호출하세요.
  • 플레이어가 게임을 마쳤나요? 점수를 Scores.submit에 제출하고 새로운 최고점수인지 확인할 수 있습니다.
  • 리더보드를 표시하시겠습니까? Scores.list에서 데이터를 가져와 사용자에게 표시합니다.
  • Scores.listWindow를 사용하여 사용자의 최고점수에 근접한 다양한 점수를 확인하세요.
  • 특정 리더보드에서 플레이어의 점수에 관한 자세한 정보를 보려면 (예: 전체 플레이어 중 상위 12% 에 속한 경우) Scores.get을 호출하세요.
  • 게임을 디버깅하시나요? Management API에서 Scores.reset을 호출하여 특정 리더보드에서 해당 플레이어의 모든 점수를 재설정합니다.