> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Game Saves 오프라인 모드

> PlayFab Game Saves 오프라인 모드를 사용하여 플레이어가 연결 없이도 진행 상황을 이어갈 수 있게 하고, 네트워크가 복구되면 클라우드 세이브를 동기화하세요.

Game Saves는 온라인과 오프라인 모두에서 작동하도록 설계되어 있어, 네트워크 연결이 불가능한 경우에도 플레이어가 계속해서 플레이할 수 있습니다. 이 시스템은 네트워크 가용성과 사용자 선택에 따라 두 가지 뚜렷한 모드로 동작합니다.

## 연결 모드

### 클라우드 연결 모드

네트워크가 사용 가능하고 `PFGameSaveFilesAddUserWithUiAsync()`가 성공적으로 완료되면, 시스템은 클라우드 연결 모드로 동작합니다. 이 모드에서는 다음이 이루어집니다.

* 모든 API가 정상적으로 작동하며 완전한 클라우드 동기화가 수행됩니다.
* 세이브 데이터 업로드가 정상적으로 작동합니다.
* 저장소 할당량 정보를 사용할 수 있습니다.
* 활성 디바이스 모니터링이 제대로 작동합니다.

### 오프라인 모드(클라우드 미연결)

네트워크를 사용할 수 없거나 사용자가 오프라인 플레이를 선택한 경우, 시스템은 제한된 기능을 가진 오프라인 모드로 진입합니다. `PFGameSaveFilesIsConnectedToCloud()`를 사용하여 현재 연결 상태를 확인함으로써 시스템이 어떤 모드에서 동작 중인지 확인할 수 있습니다.

## 초기 동기화 중 네트워크 실패 처리

네트워크 연결 없이 `PFGameSaveFilesAddUserWithUiAsync()`가 호출되면 `PFGameSaveFilesUiSyncFailedCallback`이 트리거됩니다. 시스템이 사용자의 응답을 기다리는 동안 `XAsyncBlock` 콜백은 실행되지 않습니다. 사용자가 종료 액션을 선택할 때까지 비동기 작업이 일시 중지됩니다.

```cpp theme={null}
// Handle sync failure callback
void MyPFGameSaveFilesUiSyncFailedCallback(PFLocalUserHandle localUserHandle, PFGameSaveFilesSyncState syncState, HRESULT error, void* context)
{
    // Tell the user something like this:
    std::cout << "We couldn't sync your data with the cloud just now" << std::endl;
    std::cout << "Try syncing again or use this game offline" << std::endl;
    std::cout << "[Try Again]" << std::endl;
    std::cout << "[Use Offline]" << std::endl;

    // if user chooses [Try Again], call PFGameSaveFilesSetUiSyncFailedResponse(localUserHandle, PFGameSaveFilesUiSyncFailedUserAction::Retry);
    // if user chooses [Use Offline], call PFGameSaveFilesSetUiSyncFailedResponse(localUserHandle, PFGameSaveFilesUiSyncFailedUserAction::UseOffline);

    // These API calls can happen inside or outside of this callback
}
```

**사용자 응답 옵션:**

* **다시 시도(`Retry`)**: 네트워크 호출을 재시도합니다. 재시도도 실패하면 이 콜백이 다시 실행되고 사용자가 다시 응답해야 합니다. 각 재시도 루프에서 `XAsyncBlock` 콜백은 지연된 상태로 유지됩니다.
* **오프라인으로 사용(`UseOffline`)**: `XAsyncBlock` 콜백이 `S_OK`로 실행되지만, 시스템은 오프라인 모드로 진입합니다(`PFGameSaveFilesIsConnectedToCloud()`로 감지 가능).
* **취소**: `XAsyncBlock` 콜백이 `E_PF_GAMESAVE_USER_CANCELLED`로 실행됩니다.

## 오프라인 모드에서의 API 동작

오프라인 모드에서는 API가 다르게 동작합니다.

### 정상적으로 작동하는 API

* `PFGameSaveFilesGetFolder()` - 로컬 세이브 폴더 경로를 반환합니다.

### 제한된 기능을 가진 API

* `PFGameSaveFilesUploadWithUiAsync()` - 즉시 `S_OK`를 반환하지만, 비동기 완료는 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`를 반환합니다.
* `PFGameSaveFilesGetRemainingQuota()` - `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`를 반환합니다.
* `PFGameSaveFilesSetActiveDeviceChangedCallback()` - 설정할 수는 있지만 트리거되지 않습니다.

### 온라인 모드로 복귀

* `PFGameSaveFilesAddUserWithUiAsync()`를 다시 호출하여 재연결을 시도합니다.
* Game Saves 시스템을 완전히 다시 초기화할 필요는 없습니다.
* 네트워크가 여전히 사용 불가능하면 실패 UI가 다시 표시됩니다.
* 재시도 후 `PFGameSaveFilesIsConnectedToCloud()`를 사용하여 연결 상태를 확인합니다.

## Game Saves가 오프라인 모드로 진입하거나 오류를 반환하는 경우

통합 시 자주 묻는 질문은 \*초기 동기화(`PFGameSaveFilesAddUserWithUiAsync`)가 언제 플레이어를 오프라인 모드로 전환하고, 언제 오류 코드와 함께 실패하는가?\*입니다. 종료 결과는 연결됨, 오프라인, 취소됨의 세 가지이며, 취소는 플레이어 또는 게임 중 어느 쪽에서든 발생할 수 있습니다.

| 결과 | 도달 방법 | 비동기 결과 | `IsConnectedToCloud()` | 세이브 폴더 사용 가능 여부 | 권장 처리 |
| - | - | - | - | - | - |
| **연결됨** | 동기화가 성공적으로 완료됨 | `S_OK` | `true` | 예 | 플레이를 허용합니다. 세이브가 클라우드와 동기화됩니다. |
| **오프라인** | 사용자가 동기화 실패 프롬프트에서 **Use Offline**을 선택함(또는 다른 디바이스가 활성 디바이스가 됨) | `S_OK` | `false` | 예 | 로컬 세이브로 플레이를 허용하고, 세이브가 동기화되지 않는다는 점을 플레이어에게 알립니다. |
| **플레이어가 취소함** | 사용자가 프롬프트에서 **Cancel**을 선택함 | `E_PF_GAMESAVE_USER_CANCELLED`(`0x800704C7`) | 해당 없음 | **아니요** | **플레이를 차단**하거나 안전한 메뉴로 돌아갑니다. 저장하거나 불러올 폴더가 없습니다. |
| **게임이 취소함** | 게임이 `XAsyncBlock`에 대해 `XAsyncCancel`을 호출함 | `E_ABORT`(`0x80004004`) | 해당 없음 | **아니요** | 플레이어 취소와 동일합니다. 저장하거나 불러올 폴더가 없습니다. |

핵심 사항:

* **오프라인 모드는 오류가 아니라 성공 결과입니다.** 명시적인 **Use Offline** 선택으로만 진입하며, 비동기 작업은 `S_OK`로 완료됩니다. `PFGameSaveFilesIsConnectedToCloud()`로 감지합니다.
* **일시적/기술적 실패는 동기화 실패 UI를 통해 표시되며**, `PFGameSaveFilesAddUserWithUiAsync`에서 직접 반환되지 않습니다. 그런 다음 사용자(또는 자동 응답)가 `Retry`, `UseOffline` 또는 `Cancel`을 선택하며, 이는 위의 세 가지 결과 중 하나로 이어집니다.
* **취소는 종료 결과이며 세이브 폴더를 생성하지 않습니다.** "취소됨"(또는 폴더를 남기지 않는 모든 실패)은 *플레이를 허용하지 않음*으로 처리하세요.
* **플레이어 취소와 게임 취소는 서로 다른 코드를 반환합니다.** 플레이어가 프롬프트에서 **Cancel**을 선택한 경우에만 `E_PF_GAMESAVE_USER_CANCELLED`가 생성됩니다. 게임이 `XAsyncCancel`로 작업을 직접 취소하면 `E_ABORT`로 완료됩니다. 둘을 같은 방식으로 처리하려면 두 코드를 모두 확인하세요.

> [!NOTE]
>
> **충돌** 프롬프트를 취소하는 것도 종료 결과입니다. `E_PF_GAMESAVE_USER_CANCELLED`로 완료되며, 동기화 실패 "Use Offline" 프롬프트로 자동으로 넘어가지 않습니다. "충돌을 거부하되 오프라인으로 계속 플레이"하는 환경을 원한다면 명시적으로 설계하세요(예: `PFGameSaveFilesAddUserWithUiAsync`를 다시 실행하고 그 결과로 발생한 실패에서 Use Offline을 선택). [Game Saves 충돌](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/conflicts.md)을 참조하세요.

## Game Saves 오류 코드

다음 HRESULT는 Game Saves SDK에서 정의합니다. `0x8923xxxx` 범위의 코드는 Game Saves 전용이며, `0x800704C7`은 `HRESULT_FROM_WIN32(ERROR_CANCELLED)`입니다.

| 코드 | 값 | 반환되는 경우 |
| - | - | - |
| `E_PF_GAMESAVE_USER_CANCELLED` | `0x800704C7` | 사용자가 UI 프롬프트에서 **Cancel**을 선택했습니다. 종료 결과이며 세이브 폴더가 없습니다. (게임이 `XAsyncCancel`로 `XAsyncBlock`을 취소하면 작업은 대신 표준 `E_ABORT`(`0x80004004`)로 완료됩니다.) |
| `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD` | `0x89237004` | 세션이 오프라인 모드인 동안 `PFGameSaveFilesUploadWithUiAsync()` 및 `PFGameSaveFilesGetRemainingQuota()`에서 반환됩니다. |
| `E_PF_GAMESAVE_NETWORK_FAILURE` | `0x89237005` | 클라우드에 연결된 상태에서 일시적인 네트워크 실패가 발생한 후 `PFGameSaveFilesUploadWithUiAsync()`에서 반환됩니다. 나중에 안전하게 재시도할 수 있습니다. |
| `E_PF_GAMESAVE_DEVICE_NO_LONGER_ACTIVE` | `0x89237007` | 이 디바이스가 더 이상 활성 디바이스가 아닐 때(다른 디바이스가 인계받음) `PFGameSaveFilesUploadWithUiAsync()`에서 반환됩니다. |
| `E_PF_GAMESAVE_DISK_FULL` | `0x89237008` | 세이브 데이터를 다운로드할 로컬 디스크 공간이 부족합니다. 사용자에게 공간을 확보하도록 안내하고 재시도하세요. |
| `E_PF_GAMESAVE_OPERATION_IN_PROGRESS` | `0x89237009` | 이 사용자에 대해 충돌하는 작업이 이미 실행 중입니다. 다른 업로드나 클라우드 재설정이 진행 중일 때 `PFGameSaveFilesUploadWithUiAsync()`가 호출되었거나, 클라우드 재설정이 진행 중일 때 `PFGameSaveFilesAddUserWithUiAsync()`가 호출되었거나, 다른 재설정, 업로드 또는 다운로드가 진행 중일 때 `PFGameSaveFilesResetCloudAsync()`가 호출되었습니다. 아무것도 시작되거나 수정되지 않았습니다. 진행 중인 작업이 완료될 때까지 기다린 다음 재시도하세요. October 2026 GDK(2610)부터 사용할 수 있습니다. |
| `E_PF_GAMESAVE_LOCAL_FILE_UNAVAILABLE` | `0x8923700A` | 업로드 중에 로컬 세이브 파일을 읽을 수 없을 때(예: 타이틀이 여전히 쓰기 위해 파일을 열어 두고 있는 경우) `PFGameSaveFilesUploadWithUiAsync()`에서 반환됩니다. 클라우드에 커밋된 것은 없습니다. 일시적인 오류이므로 타이틀이 해당 파일 쓰기를 멈추면 재시도하세요. October 2025 GDK(2510) QFE5, April 2026 GDK(2604) QFE6 및 October 2026 GDK(2610)에서 사용할 수 있습니다. |
| `E_PF_GAMESAVE_DOWNLOAD_IN_PROGRESS` | `0x89237006` | `PFGameSaveFilesAddUserWithUiAsync()`가 초기 동기화를 마치기 전에 `PFGameSaveFilesUploadWithUiAsync()`가 호출되었습니다. |
| `E_PF_GAMESAVE_USER_NOT_ADDED` | `0x89237003` | 해당 사용자에 대해 `PFGameSaveFilesAddUserWithUiAsync()`가 완료되기 전에 API가 호출되었습니다. |
| `E_PF_GAMESAVE_USER_ALREADY_ADDED` | `0x89237002` | 이미 추가된 사용자에 대해 `PFGameSaveFilesAddUserWithUiAsync()`가 다시 호출되었습니다. |
| `E_PF_GAMESAVE_ALREADY_INITIALIZED` | `0x89237001` | 중간에 `PFGameSaveFilesUninitializeAsync()` 호출 없이 `PFGameSaveFilesInitialize()`가 두 번 호출되었습니다. |
| `E_PF_GAMESAVE_NOT_INITIALIZED` | `0x89237000` | `PFGameSaveFilesInitialize()` 전에 API가 호출되었습니다. |

이러한 결과가 기본 제공(out-of-process) 시스템 UI와 사용자 지정 콜백에서 각각 어떻게 생성되는지는 [Game Saves UI 콜백](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/ui-callbacks.md#in-process-vs-out-of-process-system-ui)을 참조하세요.

## 추가 연결 상태 시나리오

`PFGameSaveFilesIsConnectedToCloud()` API는 연결 해제가 여러 방식으로 발생할 수 있기 때문에 특히 유용합니다.

* **동기화 중 네트워크 사용 불가**: 사용자가 명시적으로 오프라인 플레이를 선택합니다.
* **활성 디바이스 변경**: 다른 디바이스가 활성 디바이스로 인계되어 이 디바이스가 자동으로 오프라인 모드로 전환됩니다.

클라우드 작업을 시도하기 전에 이 API를 사용하여 적절한 사용자 피드백을 제공하세요.

## 오프라인 지원을 위한 모범 사례

1. **항상 동기화 실패 콜백을 구현하여** 네트워크 문제를 원활하게 처리하세요
2. 클라우드 작업을 시도하기 전에 `PFGameSaveFilesIsConnectedToCloud()`를 사용하여 **연결 상태를 정기적으로 확인하세요**
3. 오프라인 모드로 작동 중일 때 **플레이어에게 알려서** 세이브가 동기화되지 않는다는 점을 인지시키세요
4. 네트워크 연결이 복구되면 **재시도 옵션을 제공하세요**
5. **로컬 세이브는 항상 작동합니다** - 플레이어는 네트워크 상태와 관계없이 계속 플레이할 수 있습니다
6. **연결 해제 모니터링** - 다른 디바이스가 활성화될 때 디바이스가 연결 해제될 수 있음을 기억하세요

## 연결 모드에서의 업로드 동작

연결 모드에서도 네트워크 문제나 속도 제한으로 인해 업로드가 실패할 수 있습니다. 이런 경우 `XAsyncBlock` 콜백이 즉시 실행되지 않습니다. 시스템이 [UI 콜백 상태 머신](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/ui-callbacks.md#how-the-state-machine-works)을 통해 사용자의 응답을 기다리는 동안 비동기 작업이 일시 중지됩니다.

1. `PFGameSaveFilesUiSyncFailedCallback`이 오류 세부 정보와 함께 실행됩니다.
2. `XAsyncBlock` 콜백은 아직 실행되지 않습니다. 시스템이 응답을 기다리는 동안 작업이 일시 중지됩니다.
3. 사용자가 `Retry`를 선택하면 업로드가 재시도됩니다. 다시 실패하면 콜백이 다시 실행되고 사용자가 다시 응답해야 합니다.
4. 사용자가 `Cancel`을 선택하면 `XAsyncBlock` 콜백이 `E_PF_GAMESAVE_USER_CANCELLED`로 실행됩니다.
5. 재시도가 성공하면 `XAsyncBlock` 콜백이 `S_OK`로 실행됩니다.

게임은 네트워크가 복구된 후 나중에 `PFGameSaveFilesUploadWithUiAsync()`를 다시 호출할 수 있습니다.

> [!NOTE]
>
> **In-process에서는 업로드가 실행 중인 동안 동기화 실패 프롬프트에 `Use Offline`을 제공하지 마세요.** 업로드는 초기 동기화와 동일한 `PFGameSaveFilesUiSyncFailedCallback`을 실행하므로 SDK가 선택지를 제한하지 않습니다. 하지만 게임이 업로드 실패에 `UseOffline`으로 응답하면 업로드는 `E_ABORT`로 실패합니다(디버그 빌드에서는 어설션이 발생합니다). 이 경우 `Retry`와 `Cancel`만 제공하세요.

## 업로드로 인해 세션이 오프라인이 되는 경우

업로드는 일반적으로 연결 모드를 변경하지 않지만, 다음과 같은 경우 세션이 오프라인 상태로 끝날 수 있습니다. 각 경우 업로드는 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`로 완료되며, 이후 `PFGameSaveFilesIsConnectedToCloud()`는 `false`를 반환합니다.

* **세션이 이미 오프라인 상태였습니다.** 네트워크가 복구되었더라도 업로드는 실행되지 않고 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`로 완료됩니다.
* **클라우드에 더 최신 세이브가 있습니다.** 업로드를 마무리할 때 다른 디바이스에서 더 최신 세이브가 커밋되었다고 서비스가 보고하면, Game Saves는 세션을 오프라인으로 전환하고 활성 디바이스 변경 콜백을 실행한 다음 업로드를 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`로 완료합니다.
* **플레이어가 다시 잠금 프롬프트에서 `Use Offline`을 선택합니다.** 이전 업로드가 데이터를 커밋했지만 이후에 실패한 경우, 다음 업로드는 먼저 세이브 잠금을 다시 획득해야 합니다. 이 단계에서 표시되는 동기화 실패 프롬프트에 `UseOffline`으로 응답할 수 있으며, 그러면 세션이 오프라인으로 전환되고 업로드는 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`로 완료됩니다.

다시 연결하려면 `PFGameSaveFilesAddUserWithUiAsync()`를 다시 호출하세요.

## 업로드 취소

업로드는 초기 동기화와 같은 방식으로 취소할 수 있습니다. 사용자는 업로드가 진행 중일 때 **진행률** UI를 `Cancel`하거나, 업로드에 오류가 발생한 경우 **동기화 실패** 프롬프트를 `Cancel`할 수 있습니다. 어느 경우든 `PFGameSaveFilesUploadWithUiAsync()`는 `E_PF_GAMESAVE_USER_CANCELLED`(`0x800704C7`)로 완료되며 세이브는 업로드되지 않습니다. 종료 결과는 **성공**, **취소됨** 또는 **실패**입니다(여기서 `E_PF_GAMESAVE_DISCONNECTED_FROM_CLOUD`는 세션이 이제 오프라인임을 의미합니다. [업로드로 인해 세션이 오프라인이 되는 경우](#when-an-upload-takes-the-session-offline) 참조). "플레이어가 취소함"과 "업로드 오류 발생"을 구분할 수 있도록 `E_PF_GAMESAVE_USER_CANCELLED`를 다른 실패와 별도로 처리하세요. 게임이 `XAsyncCancel`로 업로드를 직접 취소하면 `E_PF_GAMESAVE_USER_CANCELLED`가 아닌 `E_ABORT`로 완료됩니다.


## Related topics

- [Game Saves 빠른 시작](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/quickstart.md)
- [Game Saves 오류 처리 FAQ](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/error-handling-faq.md)
- [Game Saves UI 콜백](https://docs.xbox.com/ko/services/playfab/player-progression/game-saves/ui-callbacks.md)
- [오프라인 플레이 처리 모범 사례](https://docs.xbox.com/ko/services/xbox-services/develop/best-practices/live-best-practices-offline-play.md)
- [XBOX 서비스 사용자 권한의 클라이언트 측 사용](https://docs.xbox.com/ko/services/xbox-services/fundamentals/identity/privileges/concepts/live-user-privileges-client.md)
