Skip to content

feat: AtchaV2 소셜 로그인 + 계정 수직 슬라이스 완결 (가입·로그아웃·탈퇴·유저정보) - #361

Merged
YuGeonHui merged 13 commits into
env/devfrom
feat/v2-social-login
Sep 24, 2026
Merged

YuGeonHui merged 13 commits into
env/devfrom
feat/v2-social-login

Conversation

@YuGeonHui

@YuGeonHui YuGeonHui commented Sep 13, 2026 •

Copy link
Copy Markdown
Collaborator

요약

레거시 소셜 로그인(카카오·애플)을 V2 구조로 전면 리팩토링해 이식하고, 로그인 이후 토큰 기반 서비스 제공에 필요한 계정 API(가입·로그아웃·탈퇴·유저정보)까지 레거시 실측 계약대로 완결. 화면 비주얼은 레거시 바텀시트 유지, 내부는 uFeatures+클린아키텍처 규약 준수. 신규 유저의 check → sign-up → 세션 채택 전 경로가 이 PR에서 닫힌다.

스택 수습 포함: #359(UI 고도화)·#360(LA 고도화)이 각자 부모 브랜치로만 머지돼 env/dev에 미반영 상태였음 — 이 PR이 해당 커밋 2개(0a5501c, e565a86)를 함께 반영한다(#358과 같은 수습 패턴, merge commit 권장).

확정 제품 결정

  • 강제 로그인 (게스트 모드 없음) — 스플래시에서 토큰 없으면 로그인 시트, 성공해야 홈 진입
  • 자동 최소 가입 — 미가입 계정은 온보딩 화면 없이 SignInUseCase가 POST /auth/sign-up까지 자동 수행: 현재 위치(8초 타임아웃 레이스)+역지오코딩 주소, 실패 시 레거시 실측 폴백(""·(0,0)), 알림 빈도 [1,10]. 홈주소·알림빈도는 가입 후 PATCH로 변경 가능
  • 로그아웃·탈퇴는 UseCase까지 — 설정 화면은 별도 Phase(진입점은 DEV 플로팅 메뉴의 "로그아웃 (DEV)"뿐)
  • 익명 세션 스텁(S1) 삭제 대체 — 토큰 소스는 소셜 로그인, refresh 확정 거절 시 loginRequired 표면화

구조

  • CoreAuth: bootstrapState()/adopt()/sessionExpired 스트림 + signOut()(POST /auth/logout — refresh 토큰을 Bearer로 싣는 레거시 특례를 LogoutEndpoint로 CoreAuth 내부 처리, 서버 실패에도 로컬 정리·만료 라우팅은 항상)·invalidateSession()(탈퇴 후 로컬만). 확정 거절(비-SUCCESS/401)만 세션 사망 — 일시 장애는 토큰 보존
  • Domain: SignInUseCase(인가→가입확인→교환/자동가입→채택, Void 반환) + UserProfile·SignUpForm 엔티티 + UserRepository·SessionEnding 포트 + Logout/Withdraw/GetUserProfile/UpdateHomeAddress/UpdateAlertFrequency UseCase. 탈퇴는 서버 성공 후에만 로컬 세션 무효화. 소셜 토큰 미보관(레거시 UserDefaults 평문 저장 미계승)
  • AtchaData: AuthEndpoint(check/login/signUp — 소셜 Bearer + provider 0/1, plain client 전용) + UserEndpoint(GET /members/me X-Platform: iOS·홈주소/알림빈도 PATCH·DELETE /members/me 사유 body) + DTO 6종·UserRepositoryImpl
  • AuthFeature: 레거시 시트 비주얼 이식(dismiss 제스처 제거), State+원샷 Event. needsSignUp 분기·"DEV 건너뛰기" 제거(자동 가입으로 흡수·폴백 제거 후 함정만 남음)
  • App: SocialLoginAdapter(카카오 톡/계정 분기, Apple continuation take()), TUIST_KAKAO_APP_KEY env 주입, sessionExpired 구독. publicPathSuffixes에 /auth/* 5경로 명시 — 소셜/refresh Bearer가 access 토큰으로 덮이는 사고의 이중 방어
  • DevDemoFallbacks 제거: 서버 실패 은폐 폴백 3종·DEV 3초 타임아웃 삭제 — DEV에서도 실서버 실패가 그대로 표면화(전 구성 10s/30s 통일). 막차 변경 검수 수단인 DevChangeSimulator만 refresh 가로채기 전용 슬림 데코레이터로 축소 존치(register/cancel 에러 은폐 없음). 자동 검수 예약 키워드("결과없음"/"경로없음")는 무효화 — 이후 빈 상태 검수는 실서버 응답 의존

검증

  • 테스트: Domain 86 / AtchaData 54 / CoreAuth 34 / AuthFeature 5 전부 통과 (sign-up 계약 핀·X-Platform 핀·refresh Bearer 핀·탈퇴 세션 보존 포함)
  • 빌드: Debug·Stage·Release 3구성 그린
  • 런타임: 시뮬 신규 설치 → 로그인 시트 렌더(레거시 동일 비주얼) → 홈 진입 증적 확보(소셜 로그인 단계 기준)

잔여 (코드 밖 액션)

  1. 카카오 콘솔 com.atcha.iOS.v2 등록 + TUIST_KAKAO_APP_KEY 주입(레포에 키 없음)
  2. Apple Developer capability 활성 + 실기기 Apple 로그인 검증
  3. 서버의 Apple identityToken audience(V2 번들) 허용 확인
  4. 실서버 검증: 신규 계정 자동 가입→홈 진입, DEV 메뉴 로그아웃→로그인 시트 복귀, GET /members/me의 appVersion 비옵셔널 핀 확인(서버가 생략하면 완화 필요)

🤖 Generated with Claude Code

https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A

YuGeonHui and others added 13 commits August 24, 2026 09:39
- 홈: 브랜드 워드마크 강등 + 큰 컨텍스트 타이틀("어디로 가세요?") + 출발/도착
  그룹 필드 카드(DSGroupedCard + HomeFieldRow) + "최근 경로" 섹션 헤더 +
  하단 고정 알람 CTA(스크롤 밖, safeArea 위) + 통과형 토스트 호스트(CTA 위 앵커)
- 검색: 필드 2개를 홈과 같은 그룹 카드로 수납(전폭 구분선 제거),
  aboveKeyboardArea 앵커만 이동(키보드 위 빈 상태 중앙 배치 유지)
- 모션(핵심만): DSPressEffect 프레스 스케일(버튼·칩·행·경로 카드 셀) +
  첫 진입 페이드인(검색 왕복 시 무재생, reduce motion 존중)
- DS 신규 2종(DSGroupedCard·DSPressEffect)은 양 화면 공용 — 테스트+갤러리 데모
  포함, 기존 컴포넌트·토큰은 무수정
- ViewModel/UseCase/Coordinator 계약 불변 — render()는 기계적 치환 3곳뿐
- 자동 검수: XCUITest 하니스 8케이스 통과, 증적 docs/verification/ui-redesign-toss

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 잠금화면: 3행 나열 → 타이머 히어로 4단 위계(헤더/오버라인/대형 카운트다운
  38pt/메타). 타이머 아이콘 제거 — 긴급도는 숫자 색이 전달, 아이콘 앵커는
  헤더 수단 심벌. stale·final은 heroMessage 공용 헬퍼로 히어로 자리 차지
- ⚠ 당겨짐 배지: solid red400 → 홈 배너 caution 티어(danger 글자 +
  dangerContainer 채움) — 강한 긴급색은 히어로 숫자 몫
- DI: expanded bottom 미니 히어로(오버라인+28pt), leading/trailing/compact
  Pretendard 통일, compactTrailing maxWidth 56 등 기존 수정자 세트 보존,
  expanded .center 미사용 사유 주석화. keylineTint·배경 틴트 불변
- Pretendard 브리지(DSFont+SwiftUI) 신규 — DSFont.pretendard 기반이라
  once-가드 등록+가중치 보존 폴백 내장, WidgetBundle init에서 이른 등록
- #Preview 픽스처 9종(활성 3단계·배지·final 3종·DI expanded/compact/minimal·
  stale 직접 렌더) — 빌드에서 컴파일 검증
- 계약(LastTrainActivityAttributes)·어댑터·카피·파생 헬퍼 무수정
- 시뮬 검수: 잠금화면 활성/배지/운행종료 + 컴팩트 DI 증적
  docs/verification/la-redesign (DI 실물·실기기 타이머 폰트는 실기기 잔여)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AnonymousSessionIssuing/UnconfiguredAnonymousSessionIssuer 삭제 (TokenPair는 TokenPair.swift로 이동)
- AuthSessionManager: bootstrapState()(동기 판정) + adopt(토큰 채택) + clearSession + sessionExpired 스트림
- performRecovery: refresh 확정 거절(비-SUCCESS/401)만 세션 사망 — 토큰 클리어 + loginRequired throw + sessionExpired yield, 일시 장애는 토큰 보존·propagate
- AuthError: issuerNotConfigured → loginRequired
- AppCoordinator: bootstrapState 분기(loginRequired는 임시 재시도 문구 — AuthFeature 통합 커밋에서 교체)
- 테스트 30케이스 재정렬: bootstrapState/adopt/clearSession/확정거절·일시장애 케이스 신규, issuer 폴백 케이스 제거

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- SocialCredential/SocialLoginProvider/SocialLoginError, LoginSession 엔티티
- 포트 4종: SocialLoginService(@mainactor authorize), AuthRepository(check/login), SessionStoring, PushTokenProviding
- DefaultSignInUseCase: authorize → check → (기존 회원) login → 세션 채택 / 미가입은 needsSignUp (소셜 자격 증명 미보관)
- DefaultSignInUseCaseTests 5케이스(호출 순서·미가입 단락·취소 전파·store 실패 전파·fcm nil)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AuthEndpoint: GET /auth/check·/auth/login, 소셜 자격 증명 Bearer + provider(kakao=0/apple=1), fcmToken nil→"" (레거시 실측 계약)
- LoginResponseDTO(전 필드 옵셔널, lat/lon 디코딩만) + toEntity(토큰 부재 throw)
- AuthRepositoryImpl: plain client 주입 규약(소셜 Bearer 보존·401 복구 미촉발)
- 계약 핀 테스트 8케이스

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Project.feature(name: "Auth") 4타겟 + Workspace 등록, 에셋(kakao/apple imageset) 레거시에서 복사
- LoginViewController: 레거시 시트 비주얼(dim 0.9·컨테이너 198·핸들·슬라이드업) 이식, 강제 로그인이라 dismiss 제스처 제거
- LoginViewModel: State(idle/loading) + 원샷 Event(미가입 안내/실패 토스트), 취소는 무표출 idle 복귀, 로딩 중 중복 탭 가드
- SocialLoginButton(레거시 스펙 52·radius8·아이콘 leading16) + AuthPalette(카카오 브랜드 상수 — DSPalette 미오염)
- LoginCoordinator(overFullScreen present, 성공 시 하강 연출 후 onAuthenticated→finish) + AuthDIContainer
- #if DEV 건너뛰기 버튼(시뮬 자동 검수용, DevDemoFallbacks와 함께 제거 예정)
- LoginViewModelTests 6케이스 + Example(미가입→실패→성공 순환 스텁)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AppCoordinator: bootstrapState 분기(loginRequired→스플래시 위 로그인 시트), sessionExpired 구독(만료 시 홈 접고 재로그인, 재로그인 후 syncNow 1회)
- SocialLoginAdapter: 카카오(톡/계정 분기, 취소 매핑) + Apple(continuation take() 패턴, 활성 씬 anchor — 레거시 force unwrap 제거)
- AuthSessionStoreAdapter(SessionStoring→adopt), FCMPushTokenAdapter(Firebase 미구성 시 nil)
- AppDIContainer: plainNetworkClient 승격, AuthRepositoryImpl(plain) + makeAuthDIContainer
- Project.swift: Kakao SDK 3종 링크(앱 타겟 전용), applesignin entitlement, TUIST_KAKAO_APP_KEY env→Info.plist(키 없으면 URL 스킴 미등록), LSApplicationQueriesSchemes
- AppDelegate KakaoSDK.initSDK(키 존재 시) / SceneDelegate openURLContexts 카카오 복귀 처리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- SignInUseCase: needsSignUp 데드엔드 제거 — 미가입이면 현재 위치(8초 타임아웃
  레이스)+역지오코딩 주소로 자동 sign-up까지 수행하고 Void 반환
- SignUpForm: 레거시 실측 기본값(userName ""·address ""·(0,0)·알림빈도 [1,10])
- UserProfile 엔티티 + UserRepository 포트(/members/me 계열 4메서드)
- SessionEnding 포트 + Logout/Withdraw/GetUserProfile/UpdateHomeAddress/
  UpdateAlertFrequency UseCase — 탈퇴는 서버 성공 후에만 로컬 세션 무효화
- AuthRepository에 signUp 추가, 테스트 갱신·신설

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A
…idateSession

- LogoutEndpoint: POST /auth/logout, refresh 토큰을 Bearer로(레거시
  TokenInterceptor 특례 실측) — 토큰이 CoreAuth 밖으로 새지 않도록 여기 배치
- AuthSessionManager.signOut(): 서버 호출은 베스트 에포트(실패 무시),
  로컬 정리+sessionExpired yield는 항상 — 기존 만료 라우팅이 로그인 복귀 처리
- invalidateSession(): 탈퇴 후 경로 — 네트워크 호출 없이 로컬 만료만
- 계약 핀·베스트 에포트·무토큰 스킵 테스트 4건 추가

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A
- AuthEndpoint.signUp: POST /auth/sign-up, 소셜 토큰 Bearer + JSON body
  (레거시 실측), 응답은 LoginResponseDTO 재사용(로그인과 계약 동일)
- UserEndpoint: GET /members/me(X-Platform: iOS)·PATCH home-address·
  PATCH alert-frequency·DELETE /members/me(사유 body)
- DTO 6종(SignUpRequest·UserInfoResponse·UserInfoPatchResponse·
  HomePatch·AlertFrequencyPatch·Withdraw) — 레거시 필드 실측 그대로
- UserRepositoryImpl(authorized client)·AuthRepositoryImpl.signUp(plain 규약 유지)
- 계약 핀 테스트: signUp body·X-Platform·PATCH/DELETE body·lat/lon 키·좌표 nil 규칙

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A
- LoginViewModel: 미가입 분기·showSignUpNotice 이벤트·안내 문구 제거 —
  가입은 UseCase가 자동 수행하므로 성공은 곧 세션 채택 완료
- devSkipTapped 제거 — 데모 폴백 제거 후엔 토큰 없는 홈 진입이
  401 → 로그인 시트 복귀 루프의 함정만 남음(시뮬레이터 검수는 Apple 로그인으로)
- LoginViewController: 가입 안내 알럿·DEV 건너뛰기 버튼 제거
- Example 스텁·LoginViewModelTests Void 시그니처 반영

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A
- AppDIContainer: SignInUseCase에 위치·역지오코딩 주입(최소 가입 재료),
  publicPathSuffixes에 /auth/* 5경로 명시(소셜/refresh Bearer가 access 토큰으로
  덮이지 않는 이중 방어), UserRepository + Logout/Withdraw UseCase 팩토리
- AuthSessionEndingAdapter: SessionEnding → AuthSessionManager 배선
- DevDemoFallbacks 제거: 실패 은폐 폴백 3종·DEV 3초 타임아웃 삭제 —
  DEV에서도 실서버 실패가 그대로 표면화된다(전 구성 10s/30s 통일)
- DevChangeSimulator 분리 존치: refresh 주입 가로채기 전용 슬림 데코레이터
  (register/cancel 에러 은폐 없음) — 막차 변경 검수의 유일 수단
- SceneDelegate DEV 메뉴에 "로그아웃 (DEV)" — 설정 화면 前 유일한 검수 진입점
- CLAUDE.md 미완 상태 절 갱신(가입 완결·로그아웃/탈퇴·폴백 제거 반영)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VEZMsiwwexokUxJBEk2C1A
@YuGeonHui YuGeonHui changed the title feat: AtchaV2 소셜 로그인 이식 (+ 스택 잔여 UI·LA 고도화 반영) feat: AtchaV2 소셜 로그인 + 계정 수직 슬라이스 완결 (가입·로그아웃·탈퇴·유저정보) Sep 13, 2026
@YuGeonHui
YuGeonHui merged commit d75e6e3 into env/dev Sep 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant