[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CBJ5PQCYPWBEPH8YA6XSAZ":3,"skill-same:01M6CBJ5PQCYPWBEPH8YA6XSAZ":44},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":13,"repositoryHtmlUrl":26,"repositoryHomepageUrl":6,"ownerHtmlUrl":27,"ownerBlogUrl":6,"ownerDescription":28,"updatedAt":29,"install":30,"resources":43},"01M6CBJ5PQCYPWBEPH8YA6XSAZ","24-login-manager",null,"devwinsoft\u002Fdevian","devwinsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F22570160?v=4","01M6C6RR8W05NH8NPKQNCE3Q72","maoshy","01M6C6RRDJ5TJJCZXRTY801YDH",0,"MIT","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F24-login-manager\u002FSKILL.md",[],[18],"24 login manager","2026-11-10 10:12:30",false,"# 24-login-manager — LoginManager\n\nStatus: ACTIVE\nAppliesTo: v10\n\n## Purpose\n\n로그인 세션 복구 + 게임 진입용 데이터 초기화 오케스트레이터.\n`AccountManager`(인증)와 각 시스템 초기화를 연결하지만, Scene\u002FUI는 제어하지 않는다.\n\n\n## Scope\n\n포함:\n- 런타임 인증 세션 복구 (`AccountManager.EnsureRuntimeAuthSessionAsync`)\n- 명시 로그인 (`AccountManager.LoginAsync`)\n- RemoteData 초기화 (`RemoteDataManager.InitializeAsync`) — 버전 체크 + 서버 UTC\n- 저장 동기화 (`SaveDataManager.SyncGameStorageAsync`)\n- 충돌 해소 + 재초기화 (`ResolveConflictAndInitializeAsync`)\n- 초기 상태 판별 (`LoginInitializeResult.IsInitial`) — 호출자가 `InventoryManager.FirstInitAsync`를 외부에서 실행\n- 게임 시스템 초기화 (Mission\u002FAchieve\u002FAd\u002FPush\u002FShop)\n- 시즌 전환 보상 sync (`LeaderboardManager.SyncSeasonTransitionRewardsAsync`)\n- 최종 local 저장 (`SaveDataManager.SaveGameStorageAsync(false, ...)`)\n- 구매 진입 인증 보정 (`EnsurePurchaseLoginReadyAsync`, Android silent Google restore)\n- 결과 반환 (`CommonResult`)\n\n제외:\n- Scene 전환 (`SceneTransManager`) 호출\n- UI 표시\u002F버튼 제어 (`UICanvas*`) 호출\n- 로그인 credential 획득 세부 구현 (AccountManager 소관)\n\n\n## API\n\n- `EnsureRuntimeSessionAndInitializeAsync(CancellationToken ct = default) : Task\u003CCommonResult\u003CLoginInitializeResult>>`\n  - 앱 시작 시 `RemoteDataManager.InitializeAsync`를 **가장 먼저** 호출한 뒤 인증 복구 + 초기화 경로\n  - 이전 로그인 정보가 없거나(`loginType=NONE`) 자동 복구가 불가능하면 로컬 모드 초기화(`syncAndInitializeAsync`) 결과를 그대로 반환한다.\n  - 클라이언트 버전은 내부에서 `Application.version` → `VersionNumber.Parse()`로 자동 결정한다.\n- `LoginAndInitializeAsync(LoginType loginType, CancellationToken ct = default) : Task\u003CCommonResult\u003CLoginInitializeResult>>`\n  - 사용자 선택 로그인 시 `RemoteDataManager.InitializeAsync` 선행 + 초기화 경로\n- `ResolveConflictAndInitializeAsync(SyncResolution resolution, CancellationToken ct = default) : Task\u003CCommonResult\u003CLoginInitializeResult>>`\n  - 충돌 해소 경로에서도 `RemoteDataManager.InitializeAsync`를 먼저 수행한다.\n  - 재초기화 후에도 `Conflict`면 `SAVEDATA_SYNC_RESOLVE_FAILED` 실패 반환\n- `VersionCheck(CancellationToken ct = default) : Task\u003CCommonResult\u003CVersionCheckResult>>`\n- `VersionCheckAsync(CancellationToken ct = default) : Task\u003CCommonResult\u003CVersionCheckResult>>`\n  - 버전 판정 API (`RemoteDataManager.InitializeAsync` 위임 결과 사용)\n  - 클라이언트 버전 SSOT: `Application.version` (`PlayerSettings.bundleVersion`)\n- `IsPurchaseLoginReady() : bool`\n  - 현재 Firebase 인증 세션 존재 여부\n- `EnsurePurchaseLoginReadyAsync(CancellationToken ct = default) : Task\u003CCommonResult\u003Cbool>>`\n  - 구매 진입 시 인증 세션 보정(필요 시 silent restore)\n\n`LoginInitializeResult`:\n- `SyncState` (`Success` \u002F `Initial` \u002F `Conflict`)\n- `IsInitial`\n- `IsConflict`\n- `VersionResult` (`Success` \u002F `RecommendUpdate` \u002F `ForceUpdate`)\n- `IsRecommendUpdate`, `IsForceUpdate`\n- `LocalDeviceId`, `CloudDeviceId` (Conflict 디버깅 정보)\n- `LocalSummary`, `CloudSummary` (`SaveRecordSummary`)\n  - 충돌 시 local\u002Fcloud 저장 요약(메타 + payload 해석 결과)\n\n반환 규약:\n- 성공: `CommonResult.Success(LoginInitializeResult)`\n- 실패: `CommonResult.Failure(CommonError, ...)`\n\n\n## Fatal \u002F Non-fatal\n\nfatal (실패 반환):\n- 계정 로그인\u002F세션 복구 실패\n- 저장 동기화 실패\n- Resolve 후 재충돌(명시적 resolve 경로)\n- 초기 지급\u002F저장 실패 (호출자 책임)\n- Mission\u002FAchieve\u002FShop 초기화 실패\n- 최종 저장 실패\n\nnon-fatal (로그만 남기고 진행):\n- 강제 업데이트 필요 (`VersionResult == ForceUpdate`)는 실패가 아니라 성공 결과로 반환된다.\n- 서버 UTC 동기화 실패 (클라이언트 UTC fallback)\n- `PurchaseManager.SyncAsync` 실패\n- `AdsManager.InitializeAsync` 실패\n- `PushManager.InitializeAsync` 실패\n- `LeaderboardManager.SyncSeasonTransitionRewardsAsync` 실패\n\n\n## Hard Rules\n\n- LoginManager는 Scene\u002FUI 레이어를 참조하지 않는다.\n- LoginManager 공개 API는 `CommonResult` 계열만 반환한다.\n- RemoteData 초기화는 로그인 초기화 진입점의 첫 단계에서 수행한다.\n- Manager 초기화 함수 내부에서 SaveData 저장을 수행하지 않고, LoginManager 마지막 단계에서만 local save를 수행한다.\n- 버전 체크\u002F서버 UTC fetch 및 해당 상태(property) 소유는 `RemoteDataManager`가 담당한다.\n- 인증 상태 판단은 `AccountManager`를 통해서만 수행한다.\n- Save payload 해석\u002F요약 생성은 LoginManager에서 구현하지 않는다. (`SaveDataManager` 책임)\n\n## Sample Wiring\n\n- `SceneLoading`은 부트 시 `EnsureRuntimeSessionAndInitializeAsync`를 호출한다.\n- `UICanvasLoading`은 버튼 로그인 시 `LoginAndInitializeAsync`를 호출한다.\n- `Conflict` 감지\u002F선택 UI는 Scene\u002FUI 레이어에서 처리하고, 실제 해소는 `ResolveConflictAndInitializeAsync`를 호출한다.\n- 초기 지급: `LoginAndInitializeAsync`는 `IsInitial` 시 `InventoryManager.FirstInitAsync` + `SaveDataManager.SaveGameStorageAsync`를 내부 수행한다. `EnsureRuntimeSessionAndInitializeAsync` \u002F `ResolveConflictAndInitializeAsync` 호출자는 `result.IsInitial`을 확인한 뒤 외부에서 직접 수행한다.\n\n\n## Implementation Location (3-path mirror)\n\n- UPM (정본): `framework-cs\u002Fupm\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FLogin\u002FLoginManager.cs`\n- UPM (정본): `framework-cs\u002Fupm\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FLogin\u002FVersionCheckResult.cs`\n- Packages (sync): `framework-cs\u002Fapps\u002FUnityExample\u002FPackages\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FLogin\u002FLoginManager.cs`\n- Packages (sync): `framework-cs\u002Fapps\u002FUnityExample\u002FPackages\u002Fcom.devian.foundation\u002FSamples~\u002FMobilePackage\u002FRuntime\u002FLogin\u002FVersionCheckResult.cs`\n- Assets\u002FSamples (import): `framework-cs\u002Fapps\u002FUnityExample\u002FAssets\u002FSamples\u002FDevian Samples\u002F{version}\u002FMobilePackage\u002FRuntime\u002FLogin\u002FLoginManager.cs`\n- Assets\u002FSamples (import): `framework-cs\u002Fapps\u002FUnityExample\u002FAssets\u002FSamples\u002FDevian Samples\u002F{version}\u002FMobilePackage\u002FRuntime\u002FLogin\u002FVersionCheckResult.cs`\n\n\n## Related\n\n- [20-account-system\u002F33-account-manager](..\u002F20-account-system\u002F33-account-manager\u002FSKILL.md)\n- [21-savedata-system\u002F10-savedata-manager](..\u002F21-savedata-system\u002F10-savedata-manager\u002FSKILL.md)\n- [29-remote-data-system\u002F10-remote-data-manager](..\u002F29-remote-data-system\u002F10-remote-data-manager\u002FSKILL.md)\n",{},"main","8cb5bd877a3a7004867d2f55697cb9e4ff2f1734","skills\u002Fdevian-unity\u002F50-mobile-package\u002F24-login-manager","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft","Kim Hyong Joon","2026-04-12 18:44:10",{"source":7,"skillName":5,"commands":31,"prompt":41,"zipAvailable":42},[32,35,38],{"tool":33,"command":34},"npx","npx skills add devwinsoft\u002Fdevian --skill 24-login-manager",{"tool":36,"command":37},"bunx","bunx skills add devwinsoft\u002Fdevian --skill 24-login-manager",{"tool":39,"command":40},"pnpm","pnpm dlx skills add devwinsoft\u002Fdevian --skill 24-login-manager","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian-unity\u002F50-mobile-package\u002F24-login-manager\u002FSKILL.md",true,[],[45,51,59,67,76,83],{"skillNo":46,"name":47,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":48,"categories":49,"keywords":50,"publishedAt":19},"01M6CBX3K2JJV38ANHVGCAT8A0","devian","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002FSKILL.md",[],[47],{"skillNo":52,"name":53,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":19},"01M6CBX210BFMM8AMN770J9WY8","90-project-archive","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F90-project-archive\u002FSKILL.md",[],[57,58],"archive","90 project archive",{"skillNo":60,"name":61,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":64,"publishedAt":19},"01M6CBWZSGRR9R3YJZB3177667","30-env-management","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F30-env-management\u002FSKILL.md",[],[65,66],"management","30 env management",{"skillNo":68,"name":69,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":19},"01M6CBWYD3TGWBQV7V35551THF","19-page-push-send","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F19-page-push-send\u002FSKILL.md",[],[73,74,75],"push","page","19 page push send",{"skillNo":77,"name":78,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":19},"01M6CBWWRQ05A7604GHJKTPJTF","18-page-savedata","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F18-page-savedata\u002FSKILL.md",[],[74,82],"18 page savedata",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":88,"publishedAt":19},"01M6CBWVH4S2VF1V7B2GBTESCT","17-page-deobfuscate","https:\u002F\u002Fgithub.com\u002Fdevwinsoft\u002Fdevian\u002Fblob\u002Fmain\u002Fskills\u002Fdevian\u002F80-tools\u002F20-operation\u002F17-page-deobfuscate\u002FSKILL.md",[],[74,89],"17 page deobfuscate"]