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

# 에러 핸들링 구현 패턴

GS2 를 사용한 게임 개발에서의 실전적인 에러 핸들링과 재시도 전략 모범 사례




GS2 를 이용한 네트워크 기능 구현에서 에러 핸들링은 게임의 안정성과 사용자 경험(UX)에 직결됩니다.
여기서는 단순히 예외를 포착하는 데 그치지 않는 실전적인 구현 패턴을 소개합니다.

## 1. 에러 분류와 대응 방침

GS2 의 예외는 그 성질에 따라 크게 3가지로 분류할 수 있습니다.

| 분류 | 해당하는 예외 (예) | 대응 방침 |
| --- | --- | --- |
| **일시적인 에러** | `InternalServerError`, `ServiceUnavailable`, `RequestTimeout` | **자동 재시도**. 몇 차례 시도해도 안 되는 경우 에러 다이얼로그를 표시. |
| **로직/설정 에러** | `BadRequest`, `NotFound` | **개발 중에 해결해야 할 에러**. 릴리스 후에는 버그로 간주하여 로그를 수집하고, 사용자에게는 적절한 메시지를 표시. |
| **인증·상태 에러** | `Unauthorized`, `Conflict`, `QuotaLimitExceeded` | **특별한 복구 처리**가 필요. 재로그인이나 데이터 재동기화(도메인 재취득)를 수행. |

## 2. 재시도 전략 구현(지수 백오프)

서버의 과부하나 일시적인 네트워크 불안정으로 인한 에러에 대해서는 즉시 재시도하는 것이 아니라, 간격을 두고 재시도하는 "지수 백오프(Exponential Backoff)"가 권장됩니다.

### Unity / C# 에서의 예(자동 재시도)

```csharp
public async Task<T> ExecuteWithAutoRetry<T>(Func<Task<T>> action, int maxRetries = 3)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            return await action();
        }
        catch (Gs2.Core.Exception.Gs2Exception e) when (e.RecommendAutoRetry) // 자동 재시도가 권장되는지 판정
        {
            if (i == maxRetries - 1) throw;

            // 지수 백오프: 1s, 2s, 4s...
            await Task.Delay((int)Math.Pow(2, i) * 1000);
        }
    }
    throw new Exception("Reached unreachable code");
}
```

### 수동 재시도 판단

`RecommendAutoRetry` 가 `false` 이고 `RecommendRetry` 가 `true` 인 경우(예: `ConflictException` 나 `QuotaLimitExceededException`), 자동으로 계속 반복하면 상황을 악화시킬 가능성이 있으므로, 사용자에게 확인을 요청하는 다이얼로그를 표시하여 재시도하게 하는 것이 일반적입니다.

## 3. 인증 에러(`UnauthorizedException`)의 자동 복구

액세스 토큰의 유효기간 만료 등으로 `UnauthorizedException` 이 발생한 경우, 사용자에게 타이틀 화면으로 돌아가도록 안내하는 것이 아니라, 백그라운드에서 자동으로 재로그인하여 요청을 재시도하는 것이 이상적입니다.

### 구현 예시

```csharp
try
{
    await gs2.Inventory.Namespace("...").Me(GameSession).Inventory("...").ModelAsync();
}
catch (Gs2.Core.Exception.UnauthorizedException)
{
    // 1. 백그라운드에서 재로그인 처리를 실행
    GameSession = await ReLogin();
    
    // 2. 새로운 GameSession 을 사용하여 요청을 재시도
    await gs2.Inventory.Namespace("...").Me(GameSession).Inventory("...").ModelAsync();
}
```

## 4. 충돌(`ConflictException`) 해소

스탬프 시트 실행 중에 통신이 끊긴 경우 등에 `ConflictException` 이 발생할 수 있습니다. 이는 "이전 처리가 아직 서버 측에서 완료되지 않았거나, 혹은 중복되고 있다"는 것을 나타냅니다.

- **대응책**: 대부분의 경우 단순한 재시도로 해결됩니다. GS2 SDK 의 상위 레벨 API(Domain 오브젝트)를 사용하는 경우, 내부 캐시와 서버 상태 사이에 불일치가 있을 가능성이 있으므로 Domain 오브젝트를 다시 가져온 후 재시도할 것을 권장합니다.

## 5. 사용자에게 주는 피드백(UX)

모든 에러에 대해 다이얼로그를 표시하면 사용자는 스트레스를 느낍니다.

1.  **사일런트 재시도**: 일시적인 에러는 우선 백그라운드에서 1~2회 재시도합니다. 그동안 UI 에는 "통신 중..." 인디케이터만 표시합니다.
2.  **복구 가능한 다이얼로그**: 재시도해도 실패한 경우, "통신에 실패했습니다. 전파가 양호한 곳에서 다시 시도해 주세요"와 같은 메시지와 **[재시도] [타이틀로]** 버튼을 표시합니다.
3.  **치명적인 에러**: 점검 중이거나 앱 버전의 강제 업데이트가 필요한 경우에는, 타이틀 화면으로 돌아가는 것 외에는 선택지가 없는 전용 다이얼로그를 표시합니다.

## 정리

- 재시도 가능한 에러에는 **지수 백오프**를 적용한다.
- 인증 에러는 **자동 재로그인**으로 감춘다.
- 개발 중에는 **카오스 모드**를 활성화하여 이러한 핸들링이 올바르게 동작하는지 검증한다.



