Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ AtchaV2(앱, 조합 루트: 어댑터·스플래시·AlarmSyncService) ─► Ho

### 미완 상태 (작업 시 참고)

- **인증은 소셜 로그인(카카오·애플), 가입까지 완결** — 강제 로그인(게스트 없음)으로 `AuthFeature`가 최초 토큰을 발급받는다(`GET /auth/check`·`/auth/login`·`POST /auth/sign-up`, 소셜 토큰을 Bearer로 — plain client 전용). 미가입 계정은 `SignInUseCase`가 온보딩 화면 없이 **최소 가입**(현재 위치+역지오코딩 주소, 실패 시 레거시 실측 폴백 `""`·(0,0), 알림 빈도 [1,10])까지 자동 수행한다. 로그아웃(`POST /auth/logout` — refresh 토큰을 Bearer로 싣는 특례, CoreAuth `AuthSessionManager.signOut()`)·회원탈퇴(`DELETE /members/me`)·유저 정보(`GET /members/me`, 홈주소/알림빈도 PATCH — `UserRepository`)은 `SettingsFeature`(홈 상단 톱니바퀴 진입: 우리집 설정·약관·피드백·버전·로그아웃·계정 탈퇴)로 연결됐다. 알림 빈도는 레거시처럼 미노출. 로그아웃·탈퇴·강제 만료는 전부 `AppCoordinator.handleSessionExpiry`에 합류하고 거기서 `AlarmSessionTeardown`이 로컬 알람·LA·스냅샷·동기화 상태를 비운다(로그아웃은 토큰이 살아 있을 때 서버 알람 취소를 먼저 시도). 홈주소 변경은 `GET /locations/is-service-region` 통과 후 PATCH. 앱 버전은 `GET /app/version`(무토큰, 1.5초 예산·실패 시 통과)으로 권장 업데이트 팝업만. 카카오 키는 `TUIST_KAKAO_APP_KEY` env로 주입(미주입 시 카카오 버튼만 런타임 비활성). 잔여: 카카오/Apple 콘솔에 V2 번들 등록, 서버 audience 허용 확인. `DevDemoFallbacks`(실패 은폐 데모 폴백)와 "DEV 건너뛰기"는 제거됨 — DEV에 남은 것은 막차 변경 주입용 `DevChangeSimulator`(refresh 가로채기 전용, 에러 은폐 없음)뿐이라 서버 실패가 DEV에서도 그대로 표면화된다.
- **인증은 게스트 전용(MVP, 2026-09)** — 소셜 로그인(AuthFeature·카카오 SDK·Apple 로그인)은 제거됐다(복구가 필요하면 PR #361 히스토리). 토큰이 없으면 스플래시에서 `IssueGuestSessionUseCase`가 키체인 보관 기기 UUID(`KeychainDeviceIdentifier`, 재설치해도 유지)로 **`POST /auth/guest`(클라 제안 계약, 서버 미구현·합의 대기)** 를 호출해 토큰을 채택하고, 실패하면 스플래시 재시도 UI를 띄운다. refresh 확정 만료 시에는 화면을 그대로 두고 같은 기기 ID로 조용히 재발급한다 — "같은 deviceId = 같은 게스트 회원"이 서버 계약 전제라 알람·집 주소는 정리하지 않는다. 로그아웃·회원탈퇴 개념은 없다. `SettingsFeature`(홈 상단 톱니바퀴: 우리집 설정·약관·피드백·버전)가 `GET /members/me`·홈주소 PATCH를 쓰고, 홈주소 변경은 `GET /locations/is-service-region` 통과 후 PATCH, 알림 빈도는 미노출. 앱 버전은 `GET /app/version`(무토큰, 1.5초 예산·실패 시 통과)으로 권장 업데이트 팝업만. `DevDemoFallbacks`와 "DEV 건너뛰기"는 제거됨 — DEV에 남은 것은 막차 변경 주입용 `DevChangeSimulator`뿐이라 서버 실패가 DEV에서도 그대로 표면화된다.
- `AppEnvironment`의 base URL은 dev/live 실주소 반영 완료. **Stage는 dev 호스트를 공유 중** — 전용 호스트만 미정.
- `Projects/App/Resources/GoogleService-Info.plist`는 **레거시 번들 ID(`com.atcha.iOS`)용 파일**이라 존재 가드만 통과할 뿐 V2(`com.atcha.iOS.v2`)로의 사일런트 푸시가 성립하지 않는다 — V2용 재발급·교체 필요. FCM 토큰은 로그인/가입 파라미터로만 서버에 가고, 갱신 전달은 `SyncPushTokenUseCase`까지 배선됐지만 서버 API 미확정이라 `UnconfirmedPushTokenRepository`(no-op)가 주입돼 있다. 그래서 갱신 채널은 현재 폴링(앱 시작·포그라운드 복귀)과 홈 pull-to-refresh(수동)뿐.
- AtchaV2는 iOS 26 전용. AlarmKit(CoreAlarm)·Live Activity(CoreLiveActivity + AtchaWidget 익스텐션)는 Phase 9~12에서 구축 완료.
Expand Down
26 changes: 1 addition & 25 deletions Projects/App/Project.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import ProjectDescription
import ProjectDescriptionHelpers

// 카카오 네이티브 앱 키 — xcconfig 금지 규약(AtchaV2) 하에서 env(TUIST_KAKAO_APP_KEY)로
// 주입한다(mise [env] 또는 CI secret). 미주입이면 빈 문자열: generate/빌드는 통과하고
// 카카오 로그인만 런타임 비활성(providerUnavailable → 실패 토스트)이다.
let kakaoAppKey = Environment.kakaoAppKey.getString(default: "")

var appInfoPlist: [String: Plist.Value] = [
let appInfoPlist: [String: Plist.Value] = [
"CFBundleDisplayName": "앗차",
"UILaunchStoryboardName": "LaunchScreen",
"NSAlarmKitUsageDescription": "막차 시간에 맞춰 알람을 울리기 위해 권한이 필요합니다.",
Expand All @@ -27,17 +22,7 @@ var appInfoPlist: [String: Plist.Value] = [
],
"UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait"],
"ITSAppUsesNonExemptEncryption": false,
// 카카오톡 로그인 앱 전환 조회용(레거시 Info.plist 실측).
"LSApplicationQueriesSchemes": ["kakaokompassauth", "kakaolink"],
// 런타임(KakaoConfig)이 읽는 키 — 빈 값이면 KakaoSDK.initSDK를 건너뛴다.
"KAKAO_APP_KEY": .string(kakaoAppKey),
]
// 키 미주입 시 무의미한 `kakao` 빈 스킴이 등록되지 않도록 조건 분기.
if !kakaoAppKey.isEmpty {
appInfoPlist["CFBundleURLTypes"] = [
["CFBundleURLSchemes": ["kakao\(kakaoAppKey)"]],
]
}

let appTarget = Target.target(
name: "AtchaV2",
Expand All @@ -54,13 +39,9 @@ let appTarget = Target.target(
"aps-environment": "development",
// 폴백 노티의 .timeSensitive interruptionLevel용(Phase 15) — 집중 모드 관통.
"com.apple.developer.usernotifications.time-sensitive": true,
// 소셜 로그인(Apple) — App ID capability 활성 + 프로비저닝 갱신 필요(콘솔 작업).
"com.apple.developer.applesignin": ["Default"],
]),
dependencies: [
.target(name: "AtchaWidget"),
.project(target: "AuthFeature", path: "../Feature/Auth"),
.project(target: "AuthFeatureInterface", path: "../Feature/Auth"),
.project(target: "HomeFeature", path: "../Feature/Home"),
.project(target: "HomeFeatureInterface", path: "../Feature/Home"),
.project(target: "SearchFeature", path: "../Feature/Search"),
Expand All @@ -79,11 +60,6 @@ let appTarget = Target.target(
.external(name: "FirebaseCore"),
.external(name: "FirebaseCrashlytics"),
.external(name: "FirebaseMessaging"),
// 소셜 SDK는 앱 타겟에서만 링크(내부 모듈 static framework 중복 심볼 방지 규약)
// — import 지점은 SocialLoginAdapter·AppDelegate·SceneDelegate뿐.
.external(name: "KakaoSDKCommon"),
.external(name: "KakaoSDKAuth"),
.external(name: "KakaoSDKUser"),
],
settings: .atchaV2(base: [
// Firebase static libraries under XcodeProj-based integration need
Expand Down
46 changes: 0 additions & 46 deletions Projects/App/Sources/Adapters/AlarmSessionTeardownAdapter.swift

This file was deleted.

17 changes: 0 additions & 17 deletions Projects/App/Sources/Adapters/AuthSessionEndingAdapter.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import CoreAuth
import Domain

/// `SessionStoring` → `AuthSessionManager.adopt` 배선 — Domain이 CoreAuth를 모른 채
/// 로그인 결과 토큰을 세션에 채택시키는 다리.
/// 게스트 발급 토큰을 세션에 채택시키는 다리.
struct AuthSessionStoreAdapter: SessionStoring {
let sessionManager: AuthSessionManager

Expand Down
23 changes: 23 additions & 0 deletions Projects/App/Sources/Adapters/KeychainDeviceIdentifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import CoreStorage
import Domain
import Foundation

/// 게스트 계정 키 — 키체인은 앱 삭제 후에도 남으므로 재설치해도 같은 게스트 회원으로 이어진다
/// (identifierForVendor는 같은 벤더 앱이 전부 지워지면 바뀐다).
struct KeychainDeviceIdentifier: DeviceIdentifierProviding {
private static let key = "guest.deviceID"
private let store: any KeyValueStore

init(store: any KeyValueStore = KeychainStore()) {
self.store = store
}

func deviceID() throws -> String {
if let data = try store.data(forKey: Self.key), let existing = String(data: data, encoding: .utf8) {
return existing
}
let created = UUID().uuidString
try store.set(Data(created.utf8), forKey: Self.key)
return created
}
}
127 changes: 0 additions & 127 deletions Projects/App/Sources/Adapters/SocialLoginAdapter.swift

This file was deleted.

18 changes: 0 additions & 18 deletions Projects/App/Sources/AlarmSyncService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,24 +136,6 @@ final class AlarmSyncService: AlarmSyncEvents, AlarmChangeEvents, AlarmSyncReque
await sync() != nil ? .newData : .failed
}

// MARK: - 계정 세션 종료 (로그아웃·탈퇴·만료)

/// 현재 보유 세션 — 로그아웃 시 서버 취소 대상 routeId의 출처(스냅샷 부재 대비).
var currentSession: AlarmInfo? { lastInfo }

/// 계정이 바뀌면 이전 계정의 세션 기억이 새 계정의 diff·만료 판정을 오염시킨다 —
/// 메모리 상태를 전부 비우고, 다음 로그인의 첫 sync가 (이미 비워진) 스냅샷부터 다시 시딩한다.
func resetForSignOut() {
inFlight?.cancel()
inFlight = nil
lastInfo = nil
lastCheckedAt = nil
locallyExpiredSession = nil
sessionWalkSeconds = nil
changeBadgeExpiry = nil
isSeededFromSnapshot = false
}

// MARK: - AlarmSyncRequesting (Phase 16 — 홈 pull-to-refresh)

/// 수동 갱신 트리거(4번째) — 기존 sync()에 그대로 합류한다. 실패는 던지지 않고
Expand Down
Loading