[Refactor] LoginViewModel에서 세션/인증 상태 분리 (SessionStore 도입)#39
Conversation
- SessionStore 신규: isLoggedIn/userInfo/name + restore/login/logout/forceLogout 인증 UseCase 호출 담당, 실패는 throw. refresh-실패 콜백 배선 이관 - LoginViewModel: 폼 표현(loginId/password/isLoading/errorMessage)만 남기고 로그인은 session.login()에 위임 - ContentView: SessionStore를 소유하고 isLoggedIn으로 루트 분기, .task로 자동 로그인 복원(restore)을 루트에서 수행 - LoginView: session 주입받아 LoginViewModel을 @State로 생성, onAppear 제거
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
Walkthrough세션/인증 상태를 관리하는 SessionStore가 신규 도입되어 LoginViewModel에서 분리되었다. LoginViewModel은 로그인 폼 상태만 관리하며 로그인 처리를 SessionStore에 위임한다. ContentView와 LoginView는 SessionStore를 주입받도록 변경되었고, SessionStore와 LoginViewModel에 대한 단위 테스트 및 목 프로바이더가 추가되었다. Changes세션 분리 리팩토링
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant ContentView
participant SessionStore
participant LoginView
participant LoginViewModel
participant UserUseCaseProvider
ContentView->>SessionStore: init(provider:)
ContentView->>SessionStore: restore()
SessionStore->>UserUseCaseProvider: hasTokens()
alt 토큰 있음
SessionStore->>UserUseCaseProvider: fetchUser()
UserUseCaseProvider-->>SessionStore: userInfo
SessionStore-->>ContentView: isLoggedIn = true
else 토큰 없음
SessionStore-->>ContentView: isLoggedIn = false
end
ContentView->>LoginView: init(session:)
LoginView->>LoginViewModel: init(session:)
LoginViewModel->>SessionStore: login(id:password:)
SessionStore->>UserUseCaseProvider: login().execute
UserUseCaseProvider-->>SessionStore: 결과
SessionStore->>UserUseCaseProvider: fetchUser()
SessionStore-->>LoginViewModel: 성공/실패
LoginViewModel-->>LoginView: errorMessage/isLoading 갱신
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- MockUserUseCaseProvider: UseCase 결과 stub + 호출 기록 spy - SessionStoreTests: restore/login/logout/리프레시 실패 콜백 8케이스 - LoginViewModelTests: 입력 검증 및 isLoading/errorMessage 상태 전이 4케이스
There was a problem hiding this comment.
🧹 Nitpick comments (1)
Rephoto_iOS/Features/User/Presentation/ViewModels/LoginViewModel.swift (1)
13-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value가이드라인상 "ViewModel은 UseCase에만 의존" 규칙과의 정합성 확인.
LoginViewModel이 이제 UseCase가 아닌SessionStore(Presentation 레이어 객체)에 의존합니다. PR 목표상 폼 상태/세션 상태 분리를 위한 의도된 설계이지만, 코딩 가이드라인은 "ViewModel depends only on UseCase"를 명시하고 있어 문자 그대로는 위반입니다.SessionStore를 Domain 레이어의 공유 유스케이스/서비스로 재정의하거나, 가이드라인에 이 패턴(세션 스토어 위임)에 대한 예외를 명시하는 것을 검토해주세요.As per coding guidelines,
**/*.swift: "Maintain Clean Architecture + MVVM pattern with clear separation: Presentation → Domain ← Data layers, where ViewModel depends only on UseCase, and Data implements Domain Protocol".Also applies to: 24-26, 38-38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Rephoto_iOS/Features/User/Presentation/ViewModels/LoginViewModel.swift` around lines 13 - 14, LoginViewModel currently depends on SessionStore, which conflicts with the “ViewModel depends only on UseCase” guideline. Update LoginViewModel to depend on a Domain-layer abstraction instead of the Presentation-layer SessionStore, or move SessionStore behind a UseCase/service that the ViewModel can inject; if this dependency is intentional, document the exception in the MVVM/Clean Architecture guideline and align the initializer and session-handling members in LoginViewModel accordingly.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@Rephoto_iOS/Features/User/Presentation/ViewModels/LoginViewModel.swift`:
- Around line 13-14: LoginViewModel currently depends on SessionStore, which
conflicts with the “ViewModel depends only on UseCase” guideline. Update
LoginViewModel to depend on a Domain-layer abstraction instead of the
Presentation-layer SessionStore, or move SessionStore behind a UseCase/service
that the ViewModel can inject; if this dependency is intentional, document the
exception in the MVVM/Clean Architecture guideline and align the initializer and
session-handling members in LoginViewModel accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 49159f28-fc7a-4924-82ff-690947c0644e
📒 Files selected for processing (7)
Rephoto_iOS/App/ContentView.swiftRephoto_iOS/Features/User/Presentation/Session/SessionStore.swiftRephoto_iOS/Features/User/Presentation/ViewModels/LoginViewModel.swiftRephoto_iOS/Features/User/Presentation/Views/LoginView.swiftRephoto_iOSTests/LoginViewModelTests.swiftRephoto_iOSTests/MockUserUseCaseProvider.swiftRephoto_iOSTests/SessionStoreTests.swift
- AppContainer: sessionStore singleton 등록 (@mainactor 타입이라 assumeIsolated로 해석) - ContentView: 직접 생성 대신 @injected 주입으로 전환 - Rephoto_iOSApp: userProvider 전달 배선 제거 - RephotoTabView: 세션 주입 + .environment로 하위 탭에 공급
|
@coderabbitai 수정사항 확인해줘 |
|
Oops, something went wrong! Please try again later. 🐰 💔 |
✨ PR 유형
🛠️ 작업내용
LoginViewModel에 섞여 있던 폼 상태와 앱 전역 세션/인증 상태의 관심사를 분리했습니다.SessionStore신규 — 인증/세션 소유isLoggedIn,userInfo,namerestore()(자동 로그인 복원) /login()/logout()/forceLogout()throw로 알림 (로딩/에러 등 화면 표현 상태는 갖지 않음)LoginViewModel슬림화 — 폼 표현만 (loginId/password/isLoading/errorMessage), 로그인은session.login()에 위임ContentView—SessionStore를@State로 소유,isLoggedIn으로 루트 분기,.task로 자동 로그인 복원을 루트에서 수행LoginView—session주입받아LoginViewModel을@State로 생성, 기존onAppear제거흐름
📋 추후 진행 상황
SessionStore에 연결SessionStore단위 테스트 (로그인/복원/강제 로그아웃 시나리오)📌 리뷰 포인트
fetchUser()실패를 조용히 무시하도록 변경함 (이미 로그인은 성공, 프로필만 미로딩 → 비치명적 처리). 에러 노출은 추후 프로필 화면 관심사로 분리 예정restore)을 LoginView가 아닌 앱 루트(ContentView)에서 수행하도록 이동✅ Checklist
Closes #37
Summary by CodeRabbit