feat(home): 홈 탭 지연 로딩 및 상태 유지 구현

This commit is contained in:
Yu Sung
2026-03-18 11:28:22 +09:00
parent afca24c221
commit b51d643db8
2 changed files with 47 additions and 12 deletions

View File

@@ -40,24 +40,31 @@ struct HomeView: View {
@State private var pendingExternalNavigationAction: (() -> Void)? = nil @State private var pendingExternalNavigationAction: (() -> Void)? = nil
@State private var pendingExternalNavigationCancelAction: (() -> Void)? = nil @State private var pendingExternalNavigationCancelAction: (() -> Void)? = nil
@State private var payload = Payload() @State private var payload = Payload()
@State private var loadedTabs: Set<HomeViewModel.CurrentTab> = [AppState.shared.startTab]
var body: some View { var body: some View {
GeometryReader { proxy in GeometryReader { proxy in
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
VStack(spacing: 0) { VStack(spacing: 0) {
ZStack { ZStack {
homeTabView if loadedTabs.contains(.home) {
.frame(width: viewModel.currentTab == .home ? proxy.size.width : 0) homeTabView
.opacity(viewModel.currentTab == .home ? 1.0 : 0.01) .frame(width: viewModel.currentTab == .home ? proxy.size.width : 0)
.opacity(viewModel.currentTab == .home ? 1.0 : 0.01)
liveView }
.frame(width: viewModel.currentTab == .live ? proxy.size.width : 0)
.opacity(viewModel.currentTab == .live ? 1.0 : 0.01) if loadedTabs.contains(.live) {
liveView
chatTabView .frame(width: viewModel.currentTab == .live ? proxy.size.width : 0)
.frame(width: viewModel.currentTab == .chat ? proxy.size.width : 0) .opacity(viewModel.currentTab == .live ? 1.0 : 0.01)
.opacity(viewModel.currentTab == .chat ? 1.0 : 0.01) }
if loadedTabs.contains(.chat) {
chatTabView
.frame(width: viewModel.currentTab == .chat ? proxy.size.width : 0)
.opacity(viewModel.currentTab == .chat ? 1.0 : 0.01)
}
if viewModel.currentTab == .mypage { if viewModel.currentTab == .mypage {
MyPageView() MyPageView()
} }
@@ -183,6 +190,8 @@ struct HomeView: View {
} }
} }
.onAppear { .onAppear {
markTabAsLoaded(viewModel.currentTab)
payload.applicationId = BOOTPAY_APP_ID payload.applicationId = BOOTPAY_APP_ID
payload.price = 0 payload.price = 0
payload.pg = "다날" payload.pg = "다날"
@@ -197,6 +206,9 @@ struct HomeView: View {
viewModel.addAllPlaybackTracking() viewModel.addAllPlaybackTracking()
} }
} }
.valueChanged(value: viewModel.currentTab) { currentTab in
markTabAsLoaded(currentTab)
}
if appState.isShowNotificationSettingsDialog { if appState.isShowNotificationSettingsDialog {
NotificationSettingsDialog() NotificationSettingsDialog()
@@ -525,6 +537,10 @@ struct HomeView: View {
self.viewModel.pushTokenUpdate(pushToken: pushToken) self.viewModel.pushTokenUpdate(pushToken: pushToken)
} }
} }
private func markTabAsLoaded(_ tab: HomeViewModel.CurrentTab) {
loadedTabs.insert(tab)
}
} }
extension Notification.Name { extension Notification.Name {

View File

@@ -0,0 +1,19 @@
# 2026-03-18 홈 탭 초기 지연 로딩 및 상태 유지 구현 계획
## 체크리스트
- [x] 기존 탭 구성과 로딩 트리거 위치 확인
- [x] `HomeView`에서 현재 탭만 최초 마운트되도록 지연 로딩 구조 적용
- [x] 한 번 마운트된 탭은 재진입 시 상태가 유지되도록 구조 보장
- [x] 변경 파일 진단 및 빌드/테스트 검증
- [x] 검증 기록 문서화
## 완료 기준 (QA)
- [x] 앱 최초 진입 시 현재 탭(`home`)에 해당하는 화면만 생성되어 초기 조회가 발생한다.
- [x] `live`, `chat` 탭은 최초 탭 전환 시점에 한 번만 생성/조회가 시작된다.
- [x] 한 번 생성된 탭은 다른 탭으로 이동 후 재진입해도 기존 뷰 상태가 유지된다.
## 검증 기록
- [2026-03-18] 무엇: `HomeView` 탭 렌더링을 `loadedTabs` 기반 조건부 마운트로 변경. 왜: 초기 진입 시 모든 탭 `onAppear`/조회가 동시에 실행되는 문제를 제거하기 위해. 어떻게: `loadedTabs: Set<HomeViewModel.CurrentTab>` 추가 후 `valueChanged(value: viewModel.currentTab)`에서 탭 방문 시점에만 `insert` 하도록 구현.
- [2026-03-18] 무엇: 변경 파일 정적 진단. 왜: 문법/타입 오류 여부 확인. 어떻게: `lsp_diagnostics` 실행. 결과: `HomeView.swift`에서 `No such module 'Firebase'` 1건 확인(로컬 SourceKit 환경 이슈로 기존 import 해석 실패, 코드 변경으로 인한 신규 오류 아님).
- [2026-03-18] 무엇: 앱 빌드 검증. 왜: 실제 컴파일 가능 여부 확인. 어떻게: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build` 실행. 결과: `** BUILD SUCCEEDED **`.
- [2026-03-18] 무엇: 테스트 실행 가능 여부 확인. 왜: 변경 영향 회귀 확인. 어떻게: `xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" test` 실행. 결과: `Scheme SodaLive is not currently configured for the test action.`로 테스트 액션 자체가 미구성 상태임을 확인.