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

# GS2-Datastore SDK for Game Engine API 레퍼런스

게임 엔진용 GS2-Datastore SDK의 모델 사양과 API 레퍼런스



## 모델

### EzDataObject

데이터 오브젝트<br>

데이터 오브젝트는 게임 플레이어가 업로드한 데이터입니다.<br>
데이터는 세대 관리되며, 30일분의 과거 데이터도 보관됩니다.<br>

데이터에는 접근 권한을 설정할 수 있습니다.<br>
스코프에는 3종류가 있으며,<br>
- 누구나 접근할 수 있는 `public`<br>
- 지정한 사용자 ID의 게임 플레이어만 접근할 수 있는 `protected`<br>
- 본인만 접근할 수 있는 `private`<br>

가 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| dataObjectId | string |  | ※ |  |  ~ 1024자 | 데이터 오브젝트 GRN<br>※ 서버가 자동으로 설정 |
| name | string |  | ✓ | UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| scope | 문자열 열거형<br>enum {<br>"public",<br>"protected",<br>"private"<br>}<br> |  |  | "private" |  | 파일 접근 권한<br>이 데이터 오브젝트에 접근할 수 있는 사용자를 제어합니다. `public`은 누구나 접근 가능, `protected`는 allowUserIds에 지정된 사용자 ID만 접근 가능, `private`은 오너 본인만 접근 가능합니다.public: 공개 / protected: 지정한 사용자에게만 공개 / private: 비공개 /  |
| allowUserIds | List&lt;string&gt; | {scope} == "protected" |  | [] | 0 ~ 100 items | 공개할 사용자 ID 목록<br>스코프가 `protected`로 설정되어 있는 경우, 이 데이터 오브젝트에 접근할 수 있는 사용자를 지정합니다. 이 목록에 포함된 사용자 ID를 가진 사용자만 읽기 접근이 허용됩니다.<br><br>※ scope이(가) "protected" 이면 활성화 |
| status | 문자열 열거형<br>enum {<br>"ACTIVE",<br>"UPLOADING",<br>"DELETED"<br>}<br> |  | ✓ |  |  | 상태<br>데이터 오브젝트의 현재 라이프사이클 상태입니다. `ACTIVE`는 데이터에 접근 가능함을 나타내고, `UPLOADING`은 새로운 버전이 업로드 중임을 나타내며, `DELETED`는 삭제 예정으로 표시된 상태임을 나타냅니다(실제 삭제는 30일 후에 이루어집니다).ACTIVE: 유효 / UPLOADING: 업로드 중 / DELETED: 삭제됨(삭제 처리로부터 30일 후에 실제로 삭제) /  |
| generation | string |  |  |  |  ~ 128자 | 데이터의 세대<br>업로드된 데이터의 현재 버전을 나타내는 식별자입니다. 데이터가 재업로드될 때마다 새로운 세대 ID가 할당됩니다. |
| createdAt | long |  | ※ | 현재 시각 |  | 생성일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정 |
| updatedAt | long |  | ※ | 현재 시각 |  | 최종 갱신일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정 |

**관련 메서드:**
deleteDataObject - 업로드한 파일 삭제
doneUpload - 파일 업로드 확정
listMyDataObjects - 플레이어가 업로드한 데이터 목록 조회
prepareDownload - 파일의 다운로드 URL 취득(데이터 오브젝트 ID로 지정)
prepareDownloadByUserIdAndDataObjectName - 다른 플레이어의 파일 다운로드 URL 취득(사용자 ID와 이름으로 지정)
prepareDownloadOwnData - 플레이어 자신의 파일 다운로드 URL 취득(이름으로 지정)
prepareReUpload - 기존 파일의 재업로드(덮어쓰기) 준비
prepareUpload - 새 파일 업로드 준비
restoreDataObject - 데이터 오브젝트의 관리 정보 복구
updateDataObject - 데이터 오브젝트의 접근 설정 변경


---

### EzDataObjectHistory

데이터 오브젝트 이력<br>

데이터 오브젝트의 업데이트 이력을 확인할 수 있습니다.<br>
데이터 오브젝트가 재업로드될 때마다 세대 ID와 파일 크기를 포함한 이력 레코드가 생성됩니다. 이력 데이터는 30일간 보관되며, 이전 버전으로의 롤백 및 감사가 가능합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| dataObjectHistoryId | string |  | ※ |  |  ~ 1024자 | 데이터 오브젝트 이력 GRN<br>※ 서버가 자동으로 설정 |
| generation | string |  | ✓ |  |  ~ 128자 | 세대 ID<br>업로드 시점의 데이터 오브젝트의 특정 버전을 나타내는 고유 식별자입니다. DataObject 의 generation 필드에 해당하며, PrepareDownloadByGeneration 에서 이 특정 버전을 다운로드할 때 사용할 수 있습니다. |
| contentLength | long |  | ✓ |  | 0 ~ 10485760 | 데이터 크기<br>이 세대의 업로드 데이터 크기(바이트 단위)입니다. 최대 파일 크기는 10 MB(10,485,760 바이트)입니다. |
| createdAt | long |  | ※ | 현재 시각 |  | 생성일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정 |

**관련 메서드:**
listDataObjectHistories - 데이터 오브젝트의 버전 이력 조회


---

## 메서드

### deleteDataObject

업로드한 파일 삭제<br>

지정한 데이터 오브젝트를 삭제 예정으로 표시합니다. 실제 파일은 30일 후에 삭제됩니다.<br>
플레이어가 더 이상 필요하지 않은 세이브 데이터나 업로드한 콘텐츠를 삭제할 때 사용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| InvalidStatusException | BadRequestException | DataObject가 조작 가능한 상태가 아닙니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.DeleteDataObjectAsync(
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Datastore.Exception.InvalidStatusException e) {
    // DataObject is not in operable state.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.DeleteDataObjectFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Datastore.Exception.InvalidStatusException)
        {
            // DataObject is not in operable state.
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->DeleteDataObject(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Datastore::Error::FInvalidStatusError::Class))
        {
            // DataObject is not in operable state.
        }
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).me(game_session).data_object(
        "dataObject-0001"
    )

var async_result = await domain.delete_data_object(
)
if async_result.error != null:
    if async_result.error is Gs2DatastoreInvalidStatusException:
        # DataObject가 조작 가능한 상태가 아닙니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### doneUpload

파일 업로드 확정<br>

PrepareUpload 또는 PrepareReUpload로 취득한 URL로의 파일 업로드가 완료된 후에 호출합니다.<br>
업로드를 확정하여 데이터 오브젝트를 다운로드 가능한 상태(상태 `ACTIVE`)로 만듭니다.<br>
이 호출을 하지 않으면 데이터 오브젝트는 `UPLOADING` 상태인 채로 남아 다운로드할 수 없습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| InvalidStatusException | BadRequestException | DataObject가 조작 가능한 상태가 아닙니다 |
| NotUploadedException | BadRequestException | DataObject가 업로드되지 않았습니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.DoneUploadAsync(
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Datastore.Exception.InvalidStatusException e) {
    // DataObject is not in operable state.
} catch(Gs2.Gs2Datastore.Exception.NotUploadedException e) {
    // DataObject is not uploaded.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.DoneUploadFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Datastore.Exception.InvalidStatusException)
        {
            // DataObject is not in operable state.
        }
        if (future.Error is Gs2.Gs2Datastore.Exception.NotUploadedException)
        {
            // DataObject is not uploaded.
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->DoneUpload(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Datastore::Error::FInvalidStatusError::Class))
        {
            // DataObject is not in operable state.
        }
        if (e->IsChildOf(Gs2::Datastore::Error::FNotUploadedError::Class))
        {
            // DataObject is not uploaded.
        }
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).me(game_session).data_object(
        "dataObject-0001"
    )

var async_result = await domain.done_upload(
)
if async_result.error != null:
    if async_result.error is Gs2DatastoreInvalidStatusException:
        # DataObject가 조작 가능한 상태가 아닙니다
        pass
    if async_result.error is Gs2DatastoreNotUploadedException:
        # DataObject가 업로드되지 않았습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### listMyDataObjects

플레이어가 업로드한 데이터 목록 조회<br>

플레이어가 업로드한 데이터 오브젝트(파일)의 목록을 조회합니다.<br>
상태별로 필터링할 수 있습니다: `ACTIVE`(다운로드 가능), `UPLOADING`(업로드 중), `DELETED`(삭제 예정).<br>
세이브 데이터 목록 화면을 표시하거나, 플레이어가 현재 저장하고 있는 파일을 확인할 때 사용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| status | 문자열 열거형<br>enum {<br>"ACTIVE",<br>"UPLOADING",<br>"DELETED"<br>}<br> |  | |  |  | 상태ACTIVE: 유효 / UPLOADING: 업로드 중 / DELETED: 삭제됨(삭제 처리로부터 30일 후 실제 삭제) /  |
| pageToken | string |  | |  |  ~ 1024자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzDataObject&gt;](#ezdataobject) | 데이터 오브젝트 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.DataObjectsAsync(
        status: "ACTIVE"
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var it = domain.DataObjects(
        status: "ACTIVE"
    );
    List<EzDataObject> items = new List<EzDataObject>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto It = Domain->DataObjects(
        "ACTIVE" // status
    );
    TArray<Gs2::UE5::Datastore::Model::FEzDataObjectPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeDataObjects(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeDataObjects(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeDataObjects(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeDataObjects(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeDataObjects(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeDataObjects(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### prepareDownload

파일의 다운로드 URL 취득(데이터 오브젝트 ID로 지정)<br>

지정한 데이터 오브젝트의 일시적인 다운로드 URL을 반환합니다.<br>
데이터 오브젝트는 ID(GRN)로 지정합니다. 접근 제어가 적용되어 파일의 오너 또는 허용 목록에 등록된 사용자만 다운로드할 수 있습니다.<br>
반환된 URL에 HTTP GET으로 접근하여 파일을 다운로드합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| dataObjectId | string |  | ✓|  |  ~ 1024자 | 데이터 오브젝트 GRN |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|
| fileUrl | string | 파일을 다운로드하기 위한 URL|
| contentLength | long | 파일 용량|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.PrepareDownloadAsync(
        dataObjectId: "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001"
    );
    var item = await result.ModelAsync();
    var fileUrl = result.FileUrl;
    var contentLength = result.ContentLength;

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.PrepareDownloadFuture(
        dataObjectId: "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001"
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;
    var fileUrl = future.Result.FileUrl;
    var contentLength = future.Result.ContentLength;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->PrepareDownload(
        "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001" // dataObjectId
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
    const auto FileUrl = Result->FileUrl;
    const auto ContentLength = Result->ContentLength;

```

**Godot**
```gdscript

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

var async_result = await domain.prepare_download(
    "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001" # data_object_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### prepareDownloadByUserIdAndDataObjectName

다른 플레이어의 파일 다운로드 URL 취득(사용자 ID와 이름으로 지정)<br>

다른 플레이어의 데이터를 사용자 ID와 데이터 오브젝트 이름을 지정하여 다운로드합니다.<br>
접근 제어가 적용됩니다. 데이터가 `public`이거나, 요청한 플레이어가 오너의 허용 목록에 포함되어 있어야 합니다.<br>
커스텀 레벨이나 리플레이 데이터 등, 플레이어끼리 데이터를 공유할 때 사용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| userId | string |  | ✓|  |  ~ 128자 | 사용자ID |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|
| fileUrl | string | 파일을 다운로드하기 위한 URL|
| contentLength | long | 파일 용량|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.PrepareDownloadByUserIdAndDataObjectNameAsync(
    );
    var item = await result.ModelAsync();
    var fileUrl = result.FileUrl;
    var contentLength = result.ContentLength;

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.PrepareDownloadByUserIdAndDataObjectNameFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;
    var fileUrl = future.Result.FileUrl;
    var contentLength = future.Result.ContentLength;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->User(
        "user-0001" // userId
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->PrepareDownloadByUserIdAndDataObjectName(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
    const auto FileUrl = Result->FileUrl;
    const auto ContentLength = Result->ContentLength;

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).user(
        "user-0001"
    ).data_object(
        "dataObject-0001"
    )

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

var result = async_result.result

```


---

### prepareDownloadOwnData

플레이어 자신의 파일 다운로드 URL 취득(이름으로 지정)<br>

ID 대신 데이터 오브젝트 이름을 지정하여 플레이어 자신의 데이터를 다운로드하기 위한 편리한 방법입니다.<br>
플레이어가 자신의 세이브 데이터나 업로드한 콘텐츠를 불러올 때 사용합니다.<br>
일시적인 다운로드 URL과 파일 크기가 반환됩니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|
| fileUrl | string | 파일을 다운로드하기 위한 URL|
| contentLength | long | 파일 용량|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.PrepareDownloadOwnDataAsync(
    );
    var item = await result.ModelAsync();
    var fileUrl = result.FileUrl;
    var contentLength = result.ContentLength;

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.PrepareDownloadOwnDataFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;
    var fileUrl = future.Result.FileUrl;
    var contentLength = future.Result.ContentLength;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->PrepareDownloadOwnData(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
    const auto FileUrl = Result->FileUrl;
    const auto ContentLength = Result->ContentLength;

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).me(game_session).data_object(
        "dataObject-0001"
    )

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

var result = async_result.result

```


---
### Download

데이터 다운로드

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | byte 배열 | 바이너리 데이터 |

#### 구현 예제



**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.DownloadAsync(
        dataObjectName: "dataObject-0001"
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.DownloadFuture();
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var result = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->Download();
    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 Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```


---

### DownloadByUserIdAndDataObjectName

사용자ID와 데이터 이름을 지정하여 데이터를 다운로드

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| userId | string |  | ✓|  |  ~ 128자 | 사용자ID |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | byte 배열 | 바이너리 데이터 |

#### 구현 예제



**Unity (UniTask)**
```csharp
var domain = gs2.Datastore.Namespace(
    namespaceName: "namespace-0001"
).User(
    userId: "user-0001"
).DataObject(
    dataObjectName: "dataObject-0001"
);
var result = await domain.DownloadByUserIdAndDataObjectNameAsync(
);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.DownloadByUserIdAndDataObjectNameFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var result = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->User(
        "user-0001" // userId
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->DownloadByUserIdAndDataObjectName(
    );
    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 Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```


---

### DownloadOwn

자신의 데이터 다운로드

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | byte 배열 | 바이너리 데이터 |

#### 구현 예제



**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.DownloadOwnAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.DownloadOwnFuture();
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var result = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->DownloadOwn(
    );
    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 Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```


---

### ReUpload

재업로드

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름 |
| data | byte 배열 | | 바이너리 데이터 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트 |
| uploadUrl | string | 업로드 처리 실행에 사용하는 URL |

#### 구현 예제



**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.ReUploadAsync(
        data: binary
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.ReUploadFuture(
        data: binary
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->ReUpload(
        binary // Binary
    );
    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 Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```



### prepareReUpload

기존 파일의 재업로드(덮어쓰기) 준비<br>

기존 데이터 오브젝트의 내용을 교체하기 위한 새로운 업로드 URL을 반환합니다.<br>
이전 버전의 파일은 이력에 남으므로, 세대 번호를 지정하여 이전 버전을 다운로드할 수도 있습니다.<br>
반환된 URL에 새 파일을 업로드한 후, DoneUpload를 호출하여 확정합니다.<br>

**재업로드 흐름:** PrepareReUpload → 반환된 URL에 파일 PUT → DoneUpload

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |
| contentType | string |  | | "application/octet-stream" |  ~ 256자 | 업로드할 데이터의 MIME-Type |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|
| uploadUrl | string | 업로드 처리를 실행하는 데 사용하는 URL|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.PrepareReUploadAsync(
        contentType: "application/octet-stream"
    );
    var item = await result.ModelAsync();
    var uploadUrl = result.UploadUrl;

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.PrepareReUploadFuture(
        contentType: "application/octet-stream"
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;
    var uploadUrl = future.Result.UploadUrl;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->PrepareReUpload(
        "application/octet-stream" // contentType
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
    const auto UploadUrl = Result->UploadUrl;

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).me(game_session).data_object(
        "dataObject-0001"
    )

var async_result = await domain.prepare_re_upload(
    "application/octet-stream" # content_type
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### prepareUpload

새 파일 업로드 준비<br>

새로운 데이터 오브젝트를 생성하고 업로드용 URL을 반환합니다.<br>
이 API를 호출한 후, 반환된 URL에 HTTP PUT 요청으로 파일을 업로드하고, DoneUpload를 호출하여 확정합니다.<br>
접근 범위(`public` 또는 `protected`)를 설정하거나, 파일 다운로드를 허용할 사용자를 지정할 수 있습니다.<br>
`updateIfExists`를 true로 설정하면, 동일한 이름의 파일이 이미 있는 경우 오류를 반환하는 대신 기존 파일을 덮어써 업데이트합니다.<br>

**업로드 흐름:** PrepareUpload → 반환된 URL에 파일 PUT → DoneUpload

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| name | string |  | |  |  ~ 128자 | 데이터 오브젝트의 이름<br>지정하지 않으면 자동으로 UUID가 할당됩니다. |
| scope | 문자열 열거형<br>enum {<br>"public",<br>"protected",<br>"private"<br>}<br> |  | | "private" |  | 파일 접근 권한<br>이 데이터 오브젝트에 접근할 수 있는 사용자를 제어합니다. `public`은 누구나 접근 가능, `protected`는 allowUserIds에 지정된 사용자 ID만 접근 가능, `private`은 오너 본인만 접근 가능합니다.public: 공개 / protected: 지정한 사용자에게만 공개 / private: 비공개 /  |
| contentType | string |  | | "application/octet-stream" |  ~ 256자 | 업로드할 데이터의 MIME-Type |
| allowUserIds | List&lt;string&gt; | {scope} == "protected" | | [] | 0 ~ 100 items | 공개할 사용자 ID 목록<br>스코프가 `protected`로 설정되어 있는 경우, 이 데이터 오브젝트에 접근할 수 있는 사용자를 지정합니다. 이 목록에 포함된 사용자 ID를 가진 사용자만 읽기 접근이 허용됩니다. |
| updateIfExists | bool |  | | false |  | 이미 데이터가 존재하는 경우 오류로 처리할지, 데이터를 업데이트할지 여부 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|
| uploadUrl | string | 업로드 처리를 실행하는 데 사용하는 URL|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.PrepareUploadAsync(
        name: "dataObject-0001",
        scope: "public",
        contentType: "application/octet-stream",
        allowUserIds: null,
        updateIfExists: null
    );
    var item = await result.ModelAsync();
    var uploadUrl = result.UploadUrl;

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.PrepareUploadFuture(
        name: "dataObject-0001",
        scope: "public",
        contentType: "application/octet-stream",
        allowUserIds: null,
        updateIfExists: null
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;
    var uploadUrl = future.Result.UploadUrl;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->PrepareUpload(
        "dataObject-0001", // name
        "public", // scope
        "application/octet-stream" // contentType
        // allowUserIds
        // updateIfExists
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();
    const auto UploadUrl = Result->UploadUrl;

```

**Godot**
```gdscript

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

var async_result = await domain.prepare_upload(
    "dataObject-0001", # name
    "public", # scope
    "application/octet-stream", # content_type
    null, # allow_user_ids
    null # update_if_exists
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### restoreDataObject

데이터 오브젝트의 관리 정보 복구<br>

데이터 오브젝트의 메타데이터와 실제 파일 사이의 불일치를 수정합니다.<br>
기록되어 있는 파일 크기나 버전 번호가 실제 파일과 일치하지 않는 경우(업로드가 중단된 경우 등), 이 API가 메타데이터를 올바르게 수정합니다.<br>
일반적으로는 호출할 필요가 없으며, 업로드 중 문제가 발생했을 때 복구 용도로만 필요합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| dataObjectId | string |  | ✓|  |  ~ 1024자 | 데이터 오브젝트 GRN |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| InvalidStatusException | BadRequestException | DataObject가 조작 가능한 상태가 아닙니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    );
    var result = await domain.RestoreDataObjectAsync(
        dataObjectId: "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001"
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Datastore.Exception.InvalidStatusException e) {
    // DataObject is not in operable state.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    );
    var future = domain.RestoreDataObjectFuture(
        dataObjectId: "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001"
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Datastore.Exception.InvalidStatusException)
        {
            // DataObject is not in operable state.
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    );
    const auto Future = Domain->RestoreDataObject(
        "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001" // dataObjectId
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Datastore::Error::FInvalidStatusError::Class))
        {
            // DataObject is not in operable state.
        }
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

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

var async_result = await domain.restore_data_object(
    "grn:gs2:ap-northeast-1:YourOwnerId:datastore:namespace-0001:user:user-0001:data:dataObject-0001" # data_object_id
)
if async_result.error != null:
    if async_result.error is Gs2DatastoreInvalidStatusException:
        # DataObject가 조작 가능한 상태가 아닙니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### updateDataObject

데이터 오브젝트의 접근 설정 변경<br>

업로드된 데이터 오브젝트에 접근할 수 있는 범위를 변경합니다.<br>
스코프를 `public`(누구나 다운로드 가능)과 `protected`(지정한 사용자만 다운로드 가능) 사이에서 전환하거나,<br>
허용할 사용자 ID 목록을 업데이트할 수 있습니다.<br>
파일 내용 자체는 변경되지 않습니다. 파일을 업데이트하려면 대신 PrepareReUpload를 사용하세요.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| scope | 문자열 열거형<br>enum {<br>"public",<br>"protected",<br>"private"<br>}<br> |  | | "private" |  | 파일 접근 권한<br>이 데이터 오브젝트에 접근할 수 있는 사용자를 제어합니다. `public`은 누구나 접근 가능, `protected`는 allowUserIds에 지정된 사용자 ID만 접근 가능, `private`은 오너 본인만 접근 가능합니다.public: 공개 / protected: 지정한 사용자에게만 공개 / private: 비공개 /  |
| allowUserIds | List&lt;string&gt; | {scope} == "protected" | | [] | 0 ~ 100 items | 공개할 사용자 ID 목록<br>스코프가 `protected`로 설정되어 있는 경우, 이 데이터 오브젝트에 접근할 수 있는 사용자를 지정합니다. 이 목록에 포함된 사용자 ID를 가진 사용자만 읽기 접근이 허용됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzDataObject](#ezdataobject) | 데이터 오브젝트|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var result = await domain.UpdateDataObjectAsync(
        scope: "public",
        allowUserIds: null
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var future = domain.UpdateDataObjectFuture(
        scope: "public",
        allowUserIds: null
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto Future = Domain->UpdateDataObject(
        "public" // scope
        // allowUserIds
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.datastore.namespace_(
        "namespace-0001"
    ).me(game_session).data_object(
        "dataObject-0001"
    )

var async_result = await domain.update_data_object(
    "public", # scope
    null # allow_user_ids
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### listDataObjectHistories

데이터 오브젝트의 버전 이력 조회<br>

데이터 오브젝트의 과거 버전(세대) 목록을 조회합니다.<br>
PrepareReUpload로 파일을 재업로드할 때마다 이전 버전이 이력에 저장됩니다.<br>
각 항목에는 파일 크기와 생성 일시가 포함되어 있어, 언제 무엇이 변경되었는지 확인할 수 있습니다.<br>
버전 이력 화면을 구성하거나, 플레이어가 이전 세이브 데이터로 되돌리는 기능을 구현할 때 사용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| dataObjectName | string |  | ✓| UUID |  ~ 128자 | 데이터 오브젝트의 이름<br>데이터 오브젝트를 식별하는 고유한 이름입니다. 기본적으로 UUID 형식으로 자동 생성됩니다. 네임스페이스 내에서 업로드된 데이터를 참조·관리하는 데 사용됩니다. |
| pageToken | string |  | |  |  ~ 1024자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzDataObjectHistory&gt;](#ezdataobjecthistory) | 데이터 오브젝트 이력 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var items = await domain.DataObjectHistoriesAsync(
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    var it = domain.DataObjectHistories(
    );
    List<EzDataObjectHistory> items = new List<EzDataObjectHistory>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    const auto It = Domain->DataObjectHistories(
    );
    TArray<Gs2::UE5::Datastore::Model::FEzDataObjectHistoryPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeDataObjectHistories(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeDataObjectHistories(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Datastore.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).DataObject(
        dataObjectName: "dataObject-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeDataObjectHistories(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeDataObjectHistories(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Datastore->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->DataObject(
        "dataObject-0001" // dataObjectName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeDataObjectHistories(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeDataObjectHistories(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---



