> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Version

버전 체크 기능




애플리케이션 버전이나 추가 에셋의 버전, 이용약관에 동의한 버전 등을 판정하는 기능을 제공합니다.

버전 체크를 통과했을 때, 새로운 임시 GS2 클라이언트ID/시크릿을 발급할 수 있습니다.

이 기능을 이용함으로써, 앱에 내장된 GS2의 클라이언트ID/시크릿은 로그인 및 버전 체크를 수행하는 API만 호출할 수 있는 권한만 가지도록 하고, 버전 체크를 통과한 후에 실제로 게임을 플레이하기에 충분한 권한을 가진 클라이언트ID/시크릿을 받을 수 있도록 할 수 있습니다.

```mermaid
graph TD
  Boot["앱 실행"] --> Login["GS2-Account 로 로그인"]
  Login -- "최소 권한의<br/>클라이언트ID/시크릿" --> Check["GS2-Version<br/>CheckVersion"]
  Check -- "OK" --> Token["새로운 ProjectToken 취득<br/>(원래 권한)"]
  Check -- "Warning" --> Notify["플레이어에게 업데이트 안내"]
  Check -- "Error" --> Force["강제 버전 업"]
  Notify --> Token
  Token --> Game["게임 본편"]
```

## 버전 모델

네임스페이스에는 최대 10개의 버전 모델을 선언할 수 있습니다. 버전 체크에는 여러 항목을 설정할 수 있으며, 모든 버전 체크를 통과한 경우에만 체크를 통과할 수 있습니다.

버전 모델에는 두 종류가 있으며, "애플리케이션이 전송한 버전을 기반으로 버전을 판정"하거나 "로그인 중인 사용자가 과거에 동의한 약관의 버전을 기반으로 버전을 판정"할 수 있습니다.<br>
전자를 "패시브 버전 체크", 후자를 "액티브 버전 체크"라고 부릅니다.

| 마스터 항목 | 설명 |
| --- | --- |
| `name` | 버전 모델명(체크 시 식별자) |
| `scope` | `passive`(패시브) / `active`(액티브) |
| `type` | `simple`(단일 임계값) / `schedule`(시각에 따라 임계값 전환) |
| `currentVersion` | 액티브 버전 체크에서 최초 참여 시 자동으로 승인된 것으로 처리하는 버전 |
| `warningVersion` | 경고를 표시하는 버전 임계값(이 버전 이하에서 `warnings`를 반환) |
| `errorVersion` | 오류로 처리하는 버전 임계값(이 버전 이하에서 `errors`를 반환) |
| `scheduleVersions` | `type: schedule`일 때 시각별 임계값을 정의 |
| `needSignature` | 버전 정보에 서명을 요구할지 여부 |
| `signatureKeyId` | 서명 검증에 사용하는 GS2-Key의 키ID |
| `approveRequirement` | 액티브 버전 체크 시 승인 요구 사항(`required` / `optional`) |

## 버전 번호 형식

{major}.{minor}.{micro}<br>
형식의 버전 번호를 이용할 수 있으며, 각 항목에는 정수값을 지정할 수 있습니다.
버전 값의 비교는 `major` → `minor` → `micro` 순으로 이루어지며, 사전순이 아니라 숫자로 비교됩니다.

## 패시브 버전 체크

게임 실행 바이너리나, 게임이 다운로드한 에셋별 버전 체크에 이용합니다.

마스터 데이터에서는 버전 모델별로 "경고를 발생시키는 버전 임계값", "오류로 처리하는 버전 임계값"을 설정할 수 있습니다.<br>
클라이언트는 `CheckVersion` API에 현재 앱의 빌드 번호나 다운로드 완료된 에셋의 버전을 전송하고, 서버 측에서 임계값을 판정한 결과를 받습니다.

판정 결과는 다음 중 하나가 됩니다.

| 결과 | 조건 | 예상되는 동작 |
| --- | --- | --- |
| 통과 | 모든 항목이 `warningVersion`을 상회 | 게임 본편으로 진행 |
| 경고 (`warnings`) | 어느 하나가 `warningVersion` 이하이면서 `errorVersion` 초과 | 플레이어에게 업데이트를 안내하면서 계속 진행 가능 |
| 오류 (`errors`) | 어느 하나가 `errorVersion` 이하 | 강제 버전 업 |

## 액티브 버전 체크

EULA·개인정보처리방침·특정 지역의 약관 개정과 같이 "플레이어가 어느 버전에 동의했는지"를 판정하는 용도로 이용합니다.

`AcceptVersion` API를 호출함으로써 사용자가 임의의 버전을 승인 상태로 만들 수 있습니다.
클라이언트에서 전송되는 버전이 아니라 서버 측에 기록된 승인 버전이 임계값 판정의 대상이 되므로, 위장을 통해 약관 동의를 회피할 수 없습니다.

### 스케줄에 의한 임계값 전환

`type: schedule`을 선택하면 `scheduleVersions`에 GS2-Schedule의 이벤트ID와 해당 이벤트 기간 중의 버전을 설정할 수 있습니다.
예를 들어 "12월 1일부터는 이용약관 v2를 필수로 한다"와 같은 운영을, 배포 없이 예약할 수 있습니다.

## 서명 검증

`needSignature`를 활성화하면 `CheckVersion` 시 서명이 포함된 버전 정보를 요구할 수 있습니다.
서명은 GS2-Key에서 발급한 키(`signatureKeyId`)로 검증되므로, 변조된 버전 정보를 배제할 수 있습니다.

## 스크립트 트리거

네임스페이스에 `checkVersionTriggerScriptId`·`acceptVersionScript`를 설정하면 버전 체크 시나 버전 승인 시에 커스텀 스크립트를 실행할 수 있습니다.

설정할 수 있는 주요 이벤트 트리거와 스크립트 설정명은 다음과 같습니다.

- `checkVersionTriggerScriptId`: 버전 체크 처리 시 호출되는 스크립트입니다. 동적인 판정 로직을 추가할 수 있습니다.
- `acceptVersionScript`: 버전 승인 시 호출되는 스크립트입니다.

## 트랜잭션 액션

GS2-Version에서는 다음과 같은 트랜잭션 액션을 제공합니다.

### 입수 액션

| 액션 | 용도 |
| --- | --- |
| `Gs2Version:AcceptByUserId` | 지정한 버전을 승인 상태로 만듭니다(액티브 버전 체크용). |

"지정한 버전의 승인"을 입수 액션으로 이용함으로써, 특정 아이템을 입수했을 때나 미션을 달성했을 때 등에 자동으로 특정 약관이나 버전을 승인 완료 상태로 만드는 처리가 가능해집니다.

## 마스터 데이터 운용

마스터 데이터를 등록함으로써 마이크로서비스에서 이용 가능한 데이터와 동작을 설정할 수 있습니다.

마스터 데이터의 종류에는 다음이 있습니다.

- `VersionModel`: 버전 모델 정의

다음은 마스터 데이터 JSON의 예시입니다.

```json
{
  "version": "2019-08-19",
  "versionModels": [
    {
      "name": "app",
      "metadata": "앱 본체",
      "scope": "passive",
      "type": "simple",
      "warningVersion": { "major": 1, "minor": 2, "micro": 0 },
      "errorVersion":   { "major": 1, "minor": 0, "micro": 0 }
    },
    {
      "name": "eula",
      "metadata": "이용약관",
      "scope": "active",
      "type": "simple",
      "currentVersion": { "major": 1, "minor": 0, "micro": 0 },
      "warningVersion": { "major": 1, "minor": 0, "micro": 0 },
      "errorVersion":   { "major": 1, "minor": 0, "micro": 0 },
      "approveRequirement": "required"
    }
  ]
}
```

마스터 데이터 등록은 관리 콘솔에서 등록하는 방법 외에도, GitHub에서 데이터를 반영하거나 GS2-Deploy를 사용하여 CI에서 등록하는 워크플로우를 구성할 수 있습니다.

## 구현 예제

### 버전 체크 실행

`CheckVersion`의 결과에는 경고에 해당하는 항목(`Warnings`)·오류에 해당하는 항목(`Errors`)·성공 시 발급되는 `ProjectToken`이 포함됩니다.
`ProjectToken`을 받은 경우에는 이를 사용하여 재인증을 수행함으로써, 실제 게임 플레이에 필요한 권한을 가진 세션을 취득할 수 있습니다.



**Unity**
```csharp

    var result = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Checker(
    ).CheckVersionAsync(
        targetVersions: new [] {
            new Gs2.Unity.Gs2Version.Model.EzTargetVersion
            {
                VersionName = "app",
                Version = new Gs2.Unity.Gs2Version.Model.EzVersion
                {
                    Major = 1,
                    Minor = 2,
                    Micro = 3,
                },
            },
            new Gs2.Unity.Gs2Version.Model.EzTargetVersion
            {
                VersionName = "asset",
                Version = new Gs2.Unity.Gs2Version.Model.EzVersion
                {
                    Major = 1,
                    Minor = 2,
                    Micro = 3,
                },
            },
        }
    );
    var projectToken = result.ProjectToken;
    var warnings = result.Warnings;
    var errors = result.Errors;

    if (errors != null && errors.Count > 0) {
        // 강제 버전 업
    } else if (warnings != null && warnings.Count > 0) {
        // 업데이트 권장 다이얼로그를 표시하면서 계속 진행
    }
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Checker(
    )->CheckVersion(
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Version::Model::FTargetVersion>>>();
            v->Add({'versionName': 'app', 'version': {'major': 1, 'minor': 2, 'micro': 3}});
            v->Add({'versionName': 'asset', 'version': {'major': 1, 'minor': 2, 'micro': 3}});
            return v;
        }() // targetVersions
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto ProjectToken = Result->ProjectToken;
    const auto Warnings = Result->Warnings;
    const auto Errors = Result->Errors;
```
**Godot**
```gdscript

var domain = ez.version.namespace_(
        "namespace-0001"
    ).me(game_session).checker(
    )

var async_result = await domain.check_version(
    [
        Gs2VersionEzTargetVersion.new()
            .with_version_name("app")
            .with_version(
            Gs2VersionEzVersion.new()
                .with_major(1)
                .with_minor(2)
                .with_micro(3)
            ),
        Gs2VersionEzTargetVersion.new()
            .with_version_name("asset")
            .with_version(
            Gs2VersionEzVersion.new()
                .with_major(1)
                .with_minor(2)
                .with_micro(3)
            ),
    ] # target_versions
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 액티브 버전 체크에 동의

이용약관 동의 버튼을 눌렀을 때 등에 `AcceptAsync`를 호출합니다.
동의한 버전은 `AcceptVersion` 모델에 사용자별로 기록되며, 이후의 `CheckVersion`에서 참조됩니다.



**Unity**
```csharp

    var result = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).AcceptVersion(
        versionName: "eula"
    ).AcceptAsync(
    );
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->AcceptVersion(
        "eula" // versionName
    )->Accept(
    );
    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();
```
**Godot**
```gdscript

var domain = ez.version.namespace_(
        "namespace-0001"
    ).me(game_session).accept_version(
        "eula"
    )

var async_result = await domain.accept(
    (Gs2VersionVersion.new()
        .with_major(2)
        .with_minor(2)
        .with_micro(2)) # version
)
if async_result.error != null:
    if async_result.error.type == "AcceptVersionInvalidException":
        # 승인 프로세스 도중에 서버 버전이 갱신된 결과 오류가 발생했습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 버전 모델 취득

UI에 "현재 약관 버전", "경고 대상 임계값"을 표시하기 위해 버전 모델 자체를 취득할 수 있습니다.



**Unity**
```csharp

    var item = await gs2.Version.Namespace(
        namespaceName: "namespace-0001"
    ).VersionModel(
        versionName: "eula"
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Version->Namespace(
        "namespace-0001" // namespaceName
    )->VersionModel(
        "eula" // versionName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Item = Future->GetTask().Result();
```
**Godot**
```gdscript

var domain = ez.version.namespace_(
        "namespace-0001"
    ).version_model(
        "version-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


## 보다 실전적인 정보

### 버전 업데이트 운영 절차

버전 업데이트 시, 플레이 중인 모든 플레이어를 한 번에 내보내고 최신 버전에서만 플레이할 수 있도록 하고 싶을 때가 있습니다.<br>
GS2-Version에서는 신규 로그인을 막을 수는 있지만, 이미 로그인된 플레이어는 버전 체크 후 임시로 발급된 클라이언트ID/클라이언트시크릿의 유효기간이 만료될 때까지 계속 접근할 수 있습니다.

그래서 모든 플레이어의 GS2-Gateway가 제공하는 알림용 상시 접속 세션을 끊고, 게임에서 세션 끊김을 핸들링하면 버전 체크 후 재접속 처리를 수행하도록 합니다.<br>
버전 체크에 실패한 경우에는 그대로 버전 업 시퀀스로 진입합니다.

이렇게 하면 버전을 업데이트하여 플레이 중인 모든 플레이어에게도 버전 체크를 강제할 수 있습니다.

### 임시 클라이언트ID 활용

`ProjectToken`을 조합하면, 실행 시 앱에 내장되어 있는 클라이언트ID/시크릿에는 "로그인과 버전 체크만 가능"한 최소한의 권한만 부여해 두고, 버전 체크 통과 후에 얻는 `ProjectToken`을 통해 게임 플레이에 필요한 강력한 권한을 행사하는 2단계 인증 구성을 취할 수 있습니다.
이를 통해 앱의 리버스 엔지니어링으로 클라이언트ID/시크릿이 유출되더라도, 피해를 버전 체크 돌파 수준으로 한정할 수 있습니다.

## 상세 레퍼런스

[GS2-Version API 레퍼런스](../../api_reference/version)



