# Tudada Unity SDK

Unity WebGL 게임에서 TudadaSDK의 모든 API를 직접 사용할 수 있는 SDK입니다.

> **Note**: 이 SDK는 WX(WeChat SDK)를 우회하지 않고 TudadaSDK와 직접 통신합니다.

## 설치

### 1. 파일 복사

```
Assets/
├── Plugins/
│   └── WebGL/
│       └── TudadaSDK.jslib           ← Plugins/WebGL/TudadaSDK.jslib
└── Scripts/
    ├── TudadaSDK.cs                  ← Scripts/TudadaSDK.cs
    ├── Internal/
    │   └── Constants.cs              ← Scripts/Internal/Constants.cs
    ├── Types/
    │   ├── TudadaTypes.cs            ← Scripts/Types/TudadaTypes.cs
    │   ├── AuthTypes.cs              ← Scripts/Types/AuthTypes.cs
    │   ├── StorageTypes.cs           ← Scripts/Types/StorageTypes.cs
    │   ├── SystemTypes.cs            ← Scripts/Types/SystemTypes.cs
    │   ├── DeviceTypes.cs            ← Scripts/Types/DeviceTypes.cs
    │   ├── AdTypes.cs                ← Scripts/Types/AdTypes.cs
    │   ├── LifecycleTypes.cs         ← Scripts/Types/LifecycleTypes.cs
    │   ├── AccelerometerTypes.cs     ← Scripts/Types/AccelerometerTypes.cs
    │   ├── CheckFeatureTypes.cs      ← Scripts/Types/CheckFeatureTypes.cs
    │   └── ShareForRewardTypes.cs   ← Scripts/Types/ShareForRewardTypes.cs
    └── Objects/
        ├── TudadaRewardedVideoAd.cs  ← Scripts/Objects/TudadaRewardedVideoAd.cs
        └── TudadaAccelerometerSensor.cs ← Scripts/Objects/TudadaAccelerometerSensor.cs
```

### 2. TudadaGameSDK 로드 확인

게임 HTML에서 TudadaGameSDK가 먼저 로드되어 있어야 합니다:

```html
<script src="https://your-cdn-url.com/tudadaGameSDK.js"></script>
```

---

## 빠른 시작

### 기본 사용법

```csharp
using UnityEngine;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        // SDK 사용 가능 여부 확인
        if (TudadaSDK.Instance.IsAvailable())
        {
            // 로그인
            TudadaSDK.Instance.Login(
                onSuccess: (result) => Debug.Log("로그인 코드: " + result.code),
                onFail: (err) => Debug.LogError("로그인 실패: " + err)
            );
        }
    }
}
```

---

## API 레퍼런스

### Auth API (인증)

#### Login

사용자 로그인을 수행합니다. 응답에는 게임 서버 검증용 자격증명(`userInfoPayload`/`userInfoSignature`)이 포함됩니다.

```csharp
TudadaSDK.Instance.Login(
    onSuccess: (result) => {
        Debug.Log("로그인 코드: " + result.code);
        // 게임 서버 검증 (v0.1.6+)
        // SendToGameServer(result.userInfoPayload, result.userInfoSignature);
    },
    onFail: (err) => {
        Debug.LogError("로그인 실패: " + err);
    },
    timeout: 10000  // 선택: 타임아웃 ms (기본값 0 = 무제한)
);
```

#### CheckSession

세션 유효성을 확인합니다.

```csharp
TudadaSDK.Instance.CheckSession(
    onSuccess: (result) => Debug.Log("세션 유효"),
    onFail: (err) => Debug.LogError("세션 만료")
);
```

#### GetUserInfo

사용자 정보를 조회합니다. 사용자 프로필과 게임 서버 검증용 자격증명(`userInfoPayload`/`userInfoSignature`)을 반환합니다.

```csharp
TudadaSDK.Instance.GetUserInfo(
    onSuccess: (result) => {
        Debug.Log("닉네임: " + result.userInfo.nickName);
        Debug.Log("아바타: " + result.userInfo.avatarUrl);
        // 게임 서버 검증
        // SendToGameServer(result.userInfoPayload, result.userInfoSignature);
    },
    onFail: (err) => Debug.LogError("사용자 정보 조회 실패: " + err)
);
```

---

### Storage API (로컬 스토리지)

#### SetStorage / SetStorageSync

로컬 스토리지에 데이터를 저장합니다.

```csharp
// 비동기
TudadaSDK.Instance.SetStorage("playerScore", "1000",
    onSuccess: (result) => Debug.Log("저장 완료"),
    onFail: (err) => Debug.LogError("저장 실패: " + err)
);

// 동기
TudadaSDK.Instance.SetStorageSync("playerScore", "1000");
```

#### GetStorage / GetStorageSync

로컬 스토리지에서 데이터를 조회합니다.

```csharp
// 비동기
TudadaSDK.Instance.GetStorage("playerScore",
    onSuccess: (result) => Debug.Log("점수: " + result.data),
    onFail: (err) => Debug.LogError("조회 실패: " + err)
);

// 동기
string score = TudadaSDK.Instance.GetStorageSync("playerScore");
```

#### RemoveStorage / RemoveStorageSync

로컬 스토리지에서 데이터를 삭제합니다.

```csharp
// 비동기
TudadaSDK.Instance.RemoveStorage("playerScore",
    onSuccess: (result) => Debug.Log("삭제 완료"),
    onFail: (err) => Debug.LogError("삭제 실패: " + err)
);

// 동기
TudadaSDK.Instance.RemoveStorageSync("playerScore");
```

#### ClearStorage / ClearStorageSync

로컬 스토리지 전체를 삭제합니다.

```csharp
// 비동기
TudadaSDK.Instance.ClearStorage(
    onSuccess: (result) => Debug.Log("전체 삭제 완료")
);

// 동기
TudadaSDK.Instance.ClearStorageSync();
```

#### GetStorageInfoSync

스토리지 정보를 조회합니다.

```csharp
StorageInfo info = TudadaSDK.Instance.GetStorageInfoSync();
Debug.Log($"사용량: {info.currentSize}KB / {info.limitSize}KB");
Debug.Log($"키 목록: {string.Join(", ", info.keys)}");
```

---

### TudadaStore API (클라우드 스토리지)

#### TudadaStoreSave

클라우드 스토리지에 데이터를 저장합니다.

> **주의**: value는 최대 **2KB**까지 저장 가능합니다.

```csharp
TudadaSDK.Instance.TudadaStoreSave("saveData", jsonString,
    onSuccess: (result) => Debug.Log("클라우드 저장 완료"),
    onFail: (err) => Debug.LogError("클라우드 저장 실패: " + err)
);
```

#### TudadaStoreGet

클라우드 스토리지에서 데이터를 조회합니다.

```csharp
TudadaSDK.Instance.TudadaStoreGet("saveData",
    onSuccess: (result) => {
        if (!string.IsNullOrEmpty(result.value))
        {
            Debug.Log("조회된 값: " + result.value);
        }
    },
    onFail: (err) => Debug.LogError("클라우드 조회 실패: " + err)
);
```

---

### System API (시스템 정보)

#### GetSystemInfoSync / GetSystemInfo

시스템 정보를 조회합니다.

```csharp
// 동기
SystemInfo info = TudadaSDK.Instance.GetSystemInfoSync();
Debug.Log($"SDK 버전: {info.SDKVersion}");
Debug.Log($"화면 크기: {info.screenWidth}x{info.screenHeight}");
Debug.Log($"플랫폼: {info.platform}");
Debug.Log($"브랜드: {info.brand}");
Debug.Log($"모델: {info.model}");

// 비동기
TudadaSDK.Instance.GetSystemInfo(
    onSuccess: (info) => Debug.Log($"SDK 버전: {info.SDKVersion}"),
    onFail: (err) => Debug.LogError("시스템 정보 조회 실패: " + err)
);
```

#### GetWindowInfo

창 정보를 조회합니다.

```csharp
WindowInfo info = TudadaSDK.Instance.GetWindowInfo();
Debug.Log($"픽셀 비율: {info.pixelRatio}");
Debug.Log($"창 크기: {info.windowWidth}x{info.windowHeight}");
Debug.Log($"상태바 높이: {info.statusBarHeight}");
Debug.Log($"SafeArea: {info.safeArea.left}, {info.safeArea.top}, {info.safeArea.right}, {info.safeArea.bottom}");
```

#### GetAppBaseInfo

앱 기본 정보를 조회합니다.

```csharp
AppBaseInfo info = TudadaSDK.Instance.GetAppBaseInfo();
Debug.Log($"SDK 버전: {info.SDKVersion}");
Debug.Log($"테마: {info.theme}");
Debug.Log($"언어: {info.language}");
```

#### GetDeviceInfo

디바이스 정보를 조회합니다.

```csharp
DeviceInfo info = TudadaSDK.Instance.GetDeviceInfo();
Debug.Log($"브랜드: {info.brand}");
Debug.Log($"모델: {info.model}");
Debug.Log($"시스템: {info.system}");
Debug.Log($"성능 등급: {info.benchmarkLevel}");
```

#### GetMenuButtonBoundingClientRect

메뉴 버튼 위치 정보를 조회합니다.

```csharp
MenuButtonRect rect = TudadaSDK.Instance.GetMenuButtonBoundingClientRect();
Debug.Log($"메뉴 버튼 위치: ({rect.left}, {rect.top}) ~ ({rect.right}, {rect.bottom})");
Debug.Log($"메뉴 버튼 크기: {rect.width}x{rect.height}");
```

---

### Device API (디바이스)

#### VibrateShort

짧은 진동을 발생시킵니다 (15ms).

```csharp
TudadaSDK.Instance.VibrateShort(
    type: VibrateType.medium,  // heavy, medium, light
    onSuccess: (result) => Debug.Log("진동 완료")
);
```

#### VibrateLong

긴 진동을 발생시킵니다 (400ms).

```csharp
TudadaSDK.Instance.VibrateLong(
    onSuccess: (result) => Debug.Log("진동 완료")
);
```

#### ShowKeyboard

키보드를 표시합니다.

```csharp
TudadaSDK.Instance.ShowKeyboard(
    defaultValue: "초기값",
    maxLength: 100,           // 0 = 무제한
    multiple: false,          // 여러 줄 입력
    confirmHold: false,       // 확인 후 키보드 유지
    confirmType: KeyboardConfirmType.done,  // done, send, search, next, go
    onSuccess: (result) => Debug.Log("키보드 표시됨")
);
```

#### HideKeyboard

키보드를 숨깁니다.

```csharp
TudadaSDK.Instance.HideKeyboard(
    onSuccess: (result) => Debug.Log("키보드 숨김")
);
```

#### 키보드 이벤트

```csharp
// 키보드 입력 이벤트
TudadaSDK.OnKeyboardInput += (result) => {
    Debug.Log("입력 중: " + result.value);
};

// 키보드 확인 이벤트
TudadaSDK.OnKeyboardConfirm += (result) => {
    Debug.Log("확인됨: " + result.value);
};

// 키보드 닫힘 이벤트
TudadaSDK.OnKeyboardComplete += (result) => {
    Debug.Log("최종 값: " + result.value);
};
```

#### SetClipboardData

클립보드에 데이터를 저장합니다.

```csharp
TudadaSDK.Instance.SetClipboardData("복사할 텍스트",
    onSuccess: (result) => Debug.Log("클립보드에 복사됨")
);
```

#### GetClipboardData

클립보드에서 데이터를 조회합니다.

```csharp
TudadaSDK.Instance.GetClipboardData(
    onSuccess: (result) => Debug.Log("클립보드 내용: " + result.data)
);
```

---

### CheckFeature API (기능 지원 확인)

#### CheckFeature

특정 API가 현재 플랫폼에서 지원되는지 사전에 확인합니다.

```csharp
TudadaSDK.Instance.CheckFeature("startAccelerometerSensor",
    onSuccess: (result) => {
        Debug.Log($"지원됨: {result.apiName}");
        // 기능 사용
    },
    onFail: (result) => {
        Debug.Log($"미지원: {result.apiName}, 사유: {result.status}");
        if (result.StatusEnum == CheckFeatureStatus.version_required)
        {
            // 업데이트 안내 표시
        }
    }
);
```

**CheckFeatureResult:**

| 필드 | 타입 | 설명 |
|------|------|------|
| `supported` | `bool` | 기능 지원 여부 |
| `status` | `string` | 미지원 사유 |
| `apiName` | `string` | 확인한 API 이름 |
| `detail` | `string` | 상세 설명 |
| `StatusEnum` | `CheckFeatureStatus` | status의 enum 변환 |

**CheckFeatureStatus:**

| 값 | 설명 |
|----|------|
| `supported` | 기능 지원됨 |
| `unknown_api` | SDK에 존재하지 않는 API |
| `version_required` | 앱 버전 업데이트 필요 |
| `platform_unsupported` | 플랫폼 미지원 |
| `device_unsupported` | 디바이스 미지원 |
| `permission_denied` | 권한 거부 |

---

### LaunchOptions API (런치 옵션)

#### GetLaunchOptions

게임 실행 시 전달된 런치 옵션을 조회합니다.

```csharp
using Tudada;

TudadaLaunchOptions options = TudadaSDK.Instance.GetLaunchOptions();
Debug.Log("쿼리 JSON: " + options.query);

// 리퍼러 정보 확인
if (options.referrerInfo != null)
{
    Debug.Log("리퍼러 데이터: " + options.referrerInfo.extraData);
}
```

> **참고**: `query`는 JSON 문자열입니다. `JsonUtility`는 `Dictionary`를 지원하지 않으므로, 서드파티 JSON 라이브러리로 파싱하세요.

---

### Lifecycle API (라이프사이클)

#### OnShow / OnHide 이벤트

앱이 포그라운드/백그라운드로 전환될 때 발생합니다.

```csharp
TudadaSDK.OnShow += (result) => {
    Debug.Log("앱이 포그라운드로 전환됨");
    Debug.Log($"경로: {result.path}, 씬: {result.scene}");
};

TudadaSDK.OnHide += (result) => {
    Debug.Log("앱이 백그라운드로 전환됨");
};
```

#### ExitMiniProgram

미니프로그램을 종료합니다.

```csharp
TudadaSDK.Instance.ExitMiniProgram(
    onSuccess: (result) => Debug.Log("종료 요청됨")
);
```

#### RestartMiniProgram

미니프로그램을 재시작합니다.

```csharp
TudadaSDK.Instance.RestartMiniProgram(
    onSuccess: (result) => Debug.Log("재시작 요청됨")
);
```

---

### Accelerometer API (가속도계)

#### StartAccelerometerSensor

가속도계 센싱을 시작합니다. 시작 후 `OnAccelerometerChange` 이벤트로 데이터를 수신합니다.

```csharp
// 이벤트 등록
TudadaSDK.Instance.OnAccelerometerChange += (res) =>
{
    Debug.Log($"x:{res.x} y:{res.y} z:{res.z}");
};

// 센싱 시작
TudadaSDK.Instance.StartAccelerometerSensor(
    sensitivity: AccelerometerSensitivity.normal, // sensitive | normal | insensitive
    onSuccess: (res) => Debug.Log("Started"),
    onFail: (err) => Debug.LogError(err)
);
```

#### StopAccelerometerSensor

가속도계 센싱을 중지합니다.

```csharp
TudadaSDK.Instance.StopAccelerometerSensor(
    onSuccess: (res) => Debug.Log("Stopped"),
    onFail: (err) => Debug.LogError(err)
);
```

---

### Ad API (광고)

#### ShowRewardedAd (권장)

보상형 광고를 한 번의 호출로 표시합니다. 로드, 표시, 재시도, 실패 안내를 플랫폼이 자동 처리합니다.

```csharp
TudadaSDK.Instance.ShowRewardedAd("ad-unit-id",
    onSuccess: (result) => {
        if (result.isEnded)
        {
            Debug.Log("보상 지급!");
            GiveReward();
        }
    },
    onFail: (err) => {
        Debug.LogError("광고 표시 실패: " + err);
    }
);
```

#### CreateRewardedVideoAd (레거시)

보상형 비디오 광고 인스턴스를 생성합니다.

```csharp
// 광고 인스턴스 생성
TudadaRewardedVideoAd ad = TudadaSDK.Instance.CreateRewardedVideoAd("ad-unit-id");

// 이벤트 리스너 등록
ad.OnLoad += () => {
    Debug.Log("광고 로드 완료");
};

ad.OnError += (error) => {
    Debug.LogError($"광고 에러: {error.errMsg} (코드: {error.errCode})");
};

ad.OnClose += (result) => {
    if (result.isEnded)
    {
        Debug.Log("광고 시청 완료 - 보상 지급!");
        GiveReward();
    }
    else
    {
        Debug.Log("광고 중간에 종료됨");
    }
};

// 광고 로드
ad.Load(
    onSuccess: (result) => Debug.Log("광고 로드 성공"),
    onFail: (err) => Debug.LogError("광고 로드 실패: " + err)
);
```

#### 광고 표시

```csharp
// 광고가 로드되었는지 확인
if (ad.IsLoaded)
{
    ad.Show(
        onSuccess: (result) => Debug.Log("광고 표시됨"),
        onFail: (err) => Debug.LogError("광고 표시 실패: " + err)
    );
}
```

#### 광고 인스턴스 파괴

```csharp
ad.Destroy();
```

#### Banner Ad (슬롯 모델)

게임이 슬롯별로 배너 이미지를 받아 자체 렌더링하고, 사용자 탭 시 액션을 SDK에 위임합니다.

##### GetAvailableBannerIds

활성 배너 슬롯 ID 목록을 조회합니다.

```csharp
TudadaSDK.Instance.GetAvailableBannerIds(
    onSuccess: (result) => {
        foreach (var bannerId in result.bannerIds)
        {
            Debug.Log("배너 슬롯: " + bannerId);
        }
    },
    onFail: (err) => Debug.LogError("배너 ID 조회 실패: " + err)
);
```

##### GetBanner

슬롯에 매칭되는 배너 데이터(`bannerId`, `imageUrl`, `expiresAt`)를 조회합니다. 슬롯에 매칭된 광고가 없을 때는 `onFail`이 호출됩니다.

```csharp
TudadaSDK.Instance.GetBanner("slot-1",
    onSuccess: (result) => {
        if (result.banner != null)
        {
            Debug.Log("이미지 URL: " + result.banner.imageUrl);
            // 게임 측에서 imageUrl로 자체 렌더링
        }
    },
    onFail: (err) => Debug.LogError("배너 조회 실패: " + err)
);
```

##### RunBannerAction

사용자가 배너를 탭했을 때 호출합니다. 응답은 success/fail 신호만 전달하며, 보상 지급은 게임이 자체 처리합니다.

```csharp
TudadaSDK.Instance.RunBannerAction("slot-1",
    onSuccess: () => Debug.Log("배너 액션 처리 완료"),
    onFail: (err) => Debug.LogError("배너 액션 실패: " + err)
);
```

---

### Share API (공유)

#### ShareForReward

보상형 공유 API입니다. 공유 완료 시 보상이 지급됩니다. 플랫폼에 따라 적절한 공유 방식이 자동으로 선택됩니다.

```csharp
// URL 포함 공유
TudadaSDK.Instance.ShareForReward("https://example.com/share",
    onSuccess: (result) => {
        Debug.Log("공유 완료 — 보상 지급!");
        GiveReward();
    },
    onFail: (err) => {
        Debug.LogError("공유 실패: " + err);
    }
);

// URL 없이 공유
TudadaSDK.Instance.ShareForReward(
    onSuccess: (result) => {
        Debug.Log("공유 완료!");
        GiveReward();
    },
    onFail: (err) => Debug.LogError("공유 실패: " + err)
);
```

---

### GameAction API (게임 액션 로깅)

게임 라이프사이클 액션(`START_PLAY`, `COMPLETE_PLAY`, `EXIT_PLAY`)을 fire-and-forget으로 SDK에 송신합니다. SDK가 sessionId를 자동 부여하며, 응답은 받지 않습니다. JS 측 `TudadaSDK.gameAction.start()`와 일치하도록 `TudadaSDK.Instance.gameAction` 으로 노출됩니다.

#### Start

플레이 시작 시점에 송신합니다. 페이로드는 모두 선택입니다.

```csharp
// 단순 호출
TudadaSDK.Instance.gameAction.Start();

// 메타데이터 포함 (필드별 hasXxx 플래그로 사용 여부 표시)
TudadaSDK.Instance.gameAction.Start(new GameActionStartPayload
{
    stage = 3,
    hasStage = true
});
```

#### Complete

플레이 정상 클리어 시 결과(`Win` / `Lose` / `Draw` / `Done`)와 진행 시간과 함께 송신합니다. `result`와 `playTime`은 필수입니다.

```csharp
TudadaSDK.Instance.gameAction.Complete(new GameActionCompletePayload
{
    result = GameActionResult.Win,   // 상수 사용 권장
    playTime = 45000,                 // ms 단위
    score = 1500,
    hasScore = true
});
```

#### Exit

플레이가 정상 완료 없이 종료될 때 사유(`Timeout` / `Abandoned`)와 진행 시간과 함께 송신합니다. `playTime`과 `reason`은 필수입니다.

```csharp
TudadaSDK.Instance.gameAction.Exit(new GameActionExitPayload
{
    playTime = 12000,                          // ms 단위
    reason = GameActionExitReason.Abandoned    // 상수 사용 권장
});
```

---

## 완전한 예제

```csharp
using UnityEngine;
using Tudada;

public class TudadaExample : MonoBehaviour
{
    private TudadaRewardedVideoAd _rewardedAd;

    void Start()
    {
        // 라이프사이클 이벤트 등록
        TudadaSDK.OnShow += OnAppShow;
        TudadaSDK.OnHide += OnAppHide;

        // 로그인 후 광고 준비
        StartLogin();
    }

    void OnDestroy()
    {
        // 이벤트 해제
        TudadaSDK.OnShow -= OnAppShow;
        TudadaSDK.OnHide -= OnAppHide;

        // 광고 정리
        _rewardedAd?.Destroy();
    }

    void StartLogin()
    {
        TudadaSDK.Instance.Login(
            onSuccess: (result) => {
                Debug.Log("로그인 성공: " + result.code);
                LoadUserData();
                PrepareRewardedAd();
            },
            onFail: (err) => Debug.LogError("로그인 실패: " + err)
        );
    }

    void LoadUserData()
    {
        TudadaSDK.Instance.TudadaStoreGet("saveData",
            onSuccess: (result) => {
                if (!string.IsNullOrEmpty(result.value))
                {
                    Debug.Log("저장된 데이터 로드: " + result.value);
                }
            }
        );
    }

    void PrepareRewardedAd()
    {
        _rewardedAd = TudadaSDK.Instance.CreateRewardedVideoAd("your-ad-unit-id");

        _rewardedAd.OnLoad += () => Debug.Log("광고 준비됨");
        _rewardedAd.OnError += (err) => Debug.LogError("광고 에러: " + err.errMsg);
        _rewardedAd.OnClose += OnAdClosed;

        _rewardedAd.Load();
    }

    public void ShowRewardedAd()
    {
        if (_rewardedAd != null && _rewardedAd.IsLoaded)
        {
            _rewardedAd.Show();
        }
        else
        {
            Debug.Log("광고가 아직 로드되지 않음");
        }
    }

    void OnAdClosed(AdCloseResult result)
    {
        if (result.isEnded)
        {
            Debug.Log("보상 지급!");
            // 보상 지급 로직
        }

        // 다음 광고 미리 로드
        _rewardedAd.Load();
    }

    void OnAppShow(OnShowResult result)
    {
        Debug.Log("앱 포그라운드");
        // 게임 재개 로직
    }

    void OnAppHide(OnHideResult result)
    {
        Debug.Log("앱 백그라운드");
        // 게임 일시정지 및 저장 로직
    }
}
```

---

## Unity Editor 지원

- **Auth API**: 목업 데이터 반환
- **Storage API**: PlayerPrefs로 시뮬레이션
- **System API**: Unity 화면 정보 기반 목업 데이터
- **Device API**: 진동은 로그만 출력, 클립보드는 시스템 클립보드 사용
- **LaunchOptions API**: 기본값 반환 (빈 query, null referrerInfo)
- **Lifecycle API**: 로그만 출력 (실제 종료/재시작 안 함)
- **Ad API**: 즉시 성공/시청완료 시뮬레이션
- **Banner Ad API**: 빈/no-op mock 응답으로 폴백
- **Share API**: 항상 success/rewarded=true 반환
- **CheckFeature API**: 항상 supported 반환
- **GameAction API**: 로그만 출력 (실제 송신 없음)

---

## 마이그레이션 가이드

### 기존 TudadaStoreManager에서 마이그레이션

기존 `TudadaStoreManager`를 사용 중이라면, 다음과 같이 마이그레이션하세요:

**Before (TudadaStoreManager)**:
```csharp
TudadaStoreManager.Instance.Save("key", "value",
    onSuccess: (msg) => Debug.Log(msg),
    onFail: (err) => Debug.LogError(err)
);

TudadaStoreManager.Instance.Get("key",
    onSuccess: (value) => Debug.Log(value),
    onFail: (err) => Debug.LogError(err)
);
```

**After (TudadaSDK)**:
```csharp
TudadaSDK.Instance.TudadaStoreSave("key", "value",
    onSuccess: (result) => Debug.Log(result.errMsg),
    onFail: (err) => Debug.LogError(err)
);

TudadaSDK.Instance.TudadaStoreGet("key",
    onSuccess: (result) => Debug.Log(result.value),
    onFail: (err) => Debug.LogError(err)
);
```

---

## 주의사항

1. **WebGL 전용**: 실제 SDK 기능은 WebGL 빌드에서만 동작합니다.
2. **싱글톤 자동 생성**: `TudadaSDK.Instance` 호출 시 `TudadaSDKHandler` GameObject가 자동 생성됩니다.
3. **GameObject 이름**: `TudadaSDKHandler` 이름을 변경하면 안 됩니다.
4. **클라우드 스토리지 용량**: TudadaStore의 value는 최대 **2KB**입니다.
5. **이벤트 해제**: 씬 전환 시 이벤트 리스너를 적절히 해제하세요.

---

## 파일 구조

```
tudada-unity-client/
├── README.md                         # 이 문서
├── CHANGELOG.md                      # 버전 이력
├── Plugins/
│   └── WebGL/
│       └── TudadaSDK.jslib           # WebGL 플러그인 (JavaScript)
├── Scripts/
│   ├── TudadaSDK.cs                  # 메인 SDK 싱글톤
│   ├── TudadaSDK.GameAction.cs       # GameAction 인터페이스 구현
│   ├── Internal/
│   │   └── Constants.cs              # 상수 및 메시지 정의
│   ├── Types/
│   │   ├── TudadaTypes.cs            # 공통 타입
│   │   ├── AuthTypes.cs              # 인증 관련 타입
│   │   ├── StorageTypes.cs           # 스토리지 관련 타입
│   │   ├── SystemTypes.cs            # 시스템 정보 타입
│   │   ├── DeviceTypes.cs            # 디바이스 관련 타입
│   │   ├── AdTypes.cs                # 광고 관련 타입 (Banner Ad 포함)
│   │   ├── AccelerometerTypes.cs     # 가속도계 타입
│   │   ├── CheckFeatureTypes.cs      # 기능 확인 타입
│   │   ├── LifecycleTypes.cs         # 라이프사이클 타입
│   │   ├── ShareForRewardTypes.cs    # 공유 보상 타입
│   │   └── GameActionTypes.cs        # 게임 액션 로깅 타입
│   └── Objects/
│       ├── TudadaAccelerometerSensor.cs  # 가속도계 센서 래퍼
│       └── TudadaRewardedVideoAd.cs  # 광고 인스턴스 래퍼
└── Examples/
    └── TudadaSDKExample.cs           # 사용 예제
```

---

## 라이선스

Copyright 2026 Tudada Platform. All rights reserved.
