47 KiB
메인 탭 당겨서 새로고침 구현 계획
| 문서 항목 | 내용 |
|---|---|
| 상태 | Phase 4 layout 보존 motion 자동 검증 완료 / 사용자 확인 대기 |
| 작성일 | 2026-08-18 |
| 요구사항 기준 | docs/20260818_메인_탭_당겨서_새로고침/prd.md |
| API 기준 | 기존 홈/콘텐츠/대화 API 계약 재사용, 신규 API 없음 |
| 현재 Phase | Phase 1~3 완료, Phase 4 Gate 수동 검증 대기 |
| 현재 활성 Goal | P4-R3 |
목표
메인 홈, 콘텐츠, 대화 탭에서 현재 선택된 내부 탭·필터 상태를 유지한 채 사용자가 당겨서 최신 데이터를 다시 조회할 수 있게 한다.
현재 상태
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|---|---|---|---|---|
| 1 | 완료 | 1/1 |
없음 | 없음 |
| 2 | 완료 | 1/1 |
없음 | 없음 |
| 3 | 완료 | 1/1 |
없음 | 없음 |
| 4 | 회귀 수정 대기 | 4/5 |
P4-R3 |
layout 보존 motion 구현·검증 |
- 동시에 하나의 미완료 goal만 운용한다.
- 완료된 Task와 검증 기록은 되돌리거나 삭제하지 않는다. 후속 수정은 회귀 수정 Task와 새 goal ID를 추가한다.
- 구현 체크박스는 완료 상태를 반영하고, 사용자 수동 검증 체크박스는 실제 확인 전까지 미완료로 둔다.
범위
포함
- 메인 홈 내부
추천,랭킹,팔로잉현재 선택 상태 새로고침. - 메인 콘텐츠 내부
추천,랭킹,전체현재 선택 상태 새로고침. - 콘텐츠
랭킹의 현재AudioRankingType, 콘텐츠전체의 현재MainContentAllType,ContentSort,SeriesPublishedDaysOfWeek유지. - 대화 탭의 현재
MainChatFilter유지와 첫 페이지 재조회. - loading, empty, error, content 상태에서 모두 새로고침 가능하도록 상태 화면 구조 점검.
- 메인 홈·콘텐츠·대화 7개 View의 20개 refresh 가능 상태에서 기본 indicator를
pull-to-refresh.jsonLottie로 교체. - Lottie를 최대
48×48pt, 원본 비율 유지, 중앙 정렬하고 상하8pt를 포함한 총64ptrefresh 영역으로 표시. - Reduce Motion 활성화 시 animation을 재생하지 않고 첫 번째 유효 frame
1을 표시.
제외
마이탭 새로고침.- 하단 탭 전체 일괄 새로고침.
- 신규 API endpoint, 신규 dependency, 신규 공통 refresh abstraction.
- 자동 갱신, polling, push 기반 갱신.
LiveNowAllView,ContentDetailView와 그 밖의 legacy 화면 pull-to-refresh 변경.
기술적 제약
- 기술 스택: Swift, SwiftUI, Combine, Moya, CocoaPods 기반 iOS 앱.
- 아키텍처: 기존
View -> ViewModel -> Repository -> Api(TargetType)흐름을 유지한다. - UI: SwiftUI
.refreshable또는 동등한 iOS 기본 refresh affordance를 우선 사용한다. - 후속 UI: SwiftUI
.refreshable은 custom indicator API를 제공하지 않으므로 Phase 4에서는 V2 공용LottieRefreshableScrollView한 개로만 대체한다. - Animation: Lottie
4.6.1,SodaLive/Resources/pull-to-refresh.json만 사용한다. asset은150×150,30fps, frame0..<45, marker 없음이다. - 접근성:
accessibilityReduceMotion == true이면 frame1을 정지 표시하고, Lottie visual은 접근성 트리에서 숨긴다. refresh action 이름은 기존I18n.LiveNow.refreshButton을 재사용한다. - 데이터·보안: 기존 인증 header, token 저장, 언어 header 흐름을 변경하지 않는다.
- 의존성: Lottie
4.6.1외 신규 dependency를 추가하지 않고, 기존RefreshableScrollViewpackage source는 수정하지 않는다. - 계약: 제공되지 않은 endpoint, DTO, enum, 오류 status/key를 추정하지 않는다.
- 구현: 모든 구현 Task는
RED → GREEN → REFACTOR순서를 체크박스에 명시한다. 현재 저장소는 테스트 번들 타깃이 명확하지 않으므로 테스트 작성이 불가능한 Task는 대체 검증 방법을 사용한다. - 검증:
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build와 수동 QA를 필수 Gate로 둔다.
Task TDD 작성 규칙
테스트 작성이 현실적으로 가능한 경우 아래 순서를 따른다.
- RED: 가장 작은 실패 test를 작성한다.
- RED 확인: focused test를 실행해 요구 동작이 없어서 발생한 의도한 assertion 실패를 확인한다.
- GREEN: RED를 통과시키는 최소 구현을 작성한다.
- GREEN 확인: 같은 focused test를 다시 실행해 성공을 확인한다.
- REFACTOR: 새 동작을 바꾸지 않는 범위에서 이번 Task가 만든 중복만 정리하고, focused test·직접 영향 회귀·lint를 다시 실행해 실제 결과를 Progress에 기록한다.
현재 docs/agent-guides/build-test-verification.md 기준으로 테스트 번들 타깃이 확인되지 않는다. 테스트 타깃이 없는 상태에서는 각 구현 Task에 TDD 예외 사유와 대체 검증 방법을 기록하고 빌드와 수동 QA로 Gate를 통과시킨다.
Phase 1 홈 탭 새로고침
Phase 결과: 홈 탭의 현재 내부 선택 상태에서 당겨서 새로고침을 실행할 수 있다.
선행조건: PRD REFRESH-001~004, HOME-REFRESH-001~003 확정.
Phase 완료 조건: P1-T1과 P1-GATE 완료, 검증 기록 누적.
구현 항목
Task 1.1 홈 내부 탭별 refresh 연결
Goal 실행 P1-T1: 홈 추천, 랭킹, 팔로잉에서 현재 선택된 내부 탭의 기존 fetch 경로를 당겨서 새로고침에 연결한다.
- 시작 조건: PRD
HOME-REFRESH-001~003,REFRESH-003확정. - 완료 증거: 홈 내부 3개 탭의 새로고침 동작과 빈/오류 상태 재시도 확인.
- 범위 밖: 콘텐츠/대화 탭 변경, 공통 refresh abstraction 생성.
- TDD 예외 사유: 현재 테스트 번들 타깃이 확인되지 않는 SwiftUI 화면 동작 변경이다.
- 대체 검증 방법: 구현 diff 검토,
xcodebuild빌드, 시뮬레이터 또는 기기 수동 QA.
Files:
- Modify:
SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift - Modify:
SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift - Modify:
SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift - Modify:
SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingViewModel.swift - Modify:
SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift - Modify:
SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingViewModel.swift
Interfaces:
-
Consumes: 기존
fetchRecommendations(),fetchRankings(),fetchFollowing()메서드. -
Produces: 각 홈 내부 View의 pull-to-refresh 사용자 동작.
-
상태 확인: 각 View의 현재 loading, empty, error, content 분기를 확인하고 새로고침 가능한 ScrollView 배치가 필요한 지점을 표시한다.
-
GREEN: 최소 구현으로 각 View에 새로고침 동작을 연결한다. 필요 시 ViewModel에
refresh()처럼 기존 fetch를 감싸는 얇은 메서드만 추가한다. -
GREEN 확인: 홈
추천,랭킹,팔로잉에서 당겨서 새로고침 후 선택된 내부 탭이 유지되는지 수동 확인한다. -
REFACTOR: 이번 Task에서 만든 중복만 정리하고 신규 공통 abstraction은 만들지 않는다.
완료 조건
P1-T1의 체크박스와 완료 증거가 모두 충족됐다.- 홈 내부 탭 3개가 PRD 요구사항과 추적된다.
- 알려진 문서와 구현의 차이가 없다.
검증 방법
Phase 1 Gate
Goal 실행 P1-GATE: 홈 탭 새로고침 사용자 흐름과 공통 품질 기준을 최종 판정한다.
- 시작 조건:
P1-T1완료. - 완료 증거: 아래 자동·수동 검증 통과와 Progress 기록.
- 범위 밖: 콘텐츠/대화 탭 구현.
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build
Expected: exit code 0. 홈 탭 진입 후 추천, 랭킹, 팔로잉 각각에서 당겨서 새로고침이 동작하고 내부 선택 상태가 유지된다.
수동 검증:
- 홈
추천콘텐츠 상태에서 새로고침한다. - 홈
랭킹콘텐츠 상태에서 새로고침한다. - 홈
팔로잉콘텐츠 또는 로그인 필요/빈 상태에서 새로고침한다. - 새로고침 실패 시 기존 오류 표시 정책을 유지한다.
Phase 2 콘텐츠 탭 새로고침
Phase 결과: 콘텐츠 탭의 현재 내부 선택 상태와 필터 조건에서 당겨서 새로고침을 실행할 수 있다.
선행조건: P1-GATE 완료.
Phase 완료 조건: P2-T1과 P2-GATE 완료, 검증 기록 누적.
구현 항목
Task 2.1 콘텐츠 내부 탭별 refresh 연결
Goal 실행 P2-T1: 콘텐츠 추천, 랭킹, 전체에서 현재 선택된 내부 탭과 필터 상태의 기존 fetch 경로를 당겨서 새로고침에 연결한다.
- 시작 조건: PRD
CONTENT-REFRESH-001~003,REFRESH-003~004확정. - 완료 증거: 콘텐츠 내부 3개 탭과
전체하위 필터 상태의 새로고침 확인. - 범위 밖: 홈/대화 탭 변경, 전체 데이터 일괄 refresh.
- TDD 예외 사유: 현재 테스트 번들 타깃이 확인되지 않는 SwiftUI 화면 동작 변경이다.
- 대체 검증 방법: 구현 diff 검토,
xcodebuild빌드, 시뮬레이터 또는 기기 수동 QA.
Files:
- Modify:
SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift - Modify:
SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationViewModel.swift - Modify:
SodaLive/Sources/V2/Main/Content/Ranking/MainContentRankingView.swift - Modify:
SodaLive/Sources/V2/Main/Content/Ranking/MainContentRankingViewModel.swift - Modify:
SodaLive/Sources/V2/Main/Content/All/MainContentAllView.swift - Modify:
SodaLive/Sources/V2/Main/Content/All/MainContentAllViewModel.swift
Interfaces:
-
Consumes: 기존
fetchRecommendations(),fetchRankings(),fetchFirstPage()메서드와 선택 상태selectedType,selectedSort,selectedDayOfWeek,selectedType. -
Produces: 콘텐츠 내부 View의 pull-to-refresh 사용자 동작.
-
상태 확인: 콘텐츠
추천,랭킹,전체의 loading, empty, error, content 분기를 확인한다. -
GREEN: 최소 구현으로 각 View에 새로고침 동작을 연결한다. 콘텐츠
전체는 현재 type/sort/dayOfWeek를 유지하고 첫 페이지를 다시 조회한다. -
GREEN 확인: 콘텐츠
추천,랭킹,전체에서 당겨서 새로고침 후 선택된 내부 탭과 필터가 유지되는지 확인한다. -
REFACTOR: 새로 만든 중복만 정리하고 신규 공통 abstraction은 만들지 않는다.
완료 조건
P2-T1의 체크박스와 완료 증거가 모두 충족됐다.- 콘텐츠 내부 탭 3개와
전체하위 필터 상태가 PRD 요구사항과 추적된다. - 알려진 문서와 구현의 차이가 없다.
검증 방법
Phase 2 Gate
Goal 실행 P2-GATE: 콘텐츠 탭 새로고침 사용자 흐름과 공통 품질 기준을 최종 판정한다.
- 시작 조건:
P2-T1완료. - 완료 증거: 아래 자동·수동 검증 통과와 Progress 기록.
- 범위 밖: 대화 탭 구현.
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build
Expected: exit code 0. 콘텐츠 탭 진입 후 추천, 랭킹, 전체 각각에서 당겨서 새로고침이 동작하고 현재 선택 상태가 유지된다.
수동 검증:
- 콘텐츠
추천에서 새로고침한다. - 콘텐츠
랭킹에서 랭킹 타입을 바꾼 뒤 새로고침하고 선택 타입이 유지되는지 확인한다. - 콘텐츠
전체 > 오디오에서 sort를 바꾼 뒤 새로고침하고 type/sort가 유지되는지 확인한다. - 콘텐츠
전체 > 시리즈에서 요일과 sort를 바꾼 뒤 새로고침하고 type/dayOfWeek/sort가 유지되는지 확인한다. - 빈/오류 상태에서도 새로고침을 재시도할 수 있는지 확인한다.
Phase 3 대화 탭 새로고침
Phase 결과: 대화 탭의 현재 필터에서 당겨서 첫 페이지를 다시 조회할 수 있다.
선행조건: P2-GATE 완료.
Phase 완료 조건: P3-T1과 P3-GATE 완료, 검증 기록 누적.
구현 항목
Task 3.1 대화 필터별 refresh 연결
Goal 실행 P3-T1: 대화 전체, AI, DM 현재 필터의 기존 첫 페이지 fetch 경로를 당겨서 새로고침에 연결한다.
- 시작 조건: PRD
CHAT-REFRESH-001~002,REFRESH-003~004확정. - 완료 증거: 대화 3개 필터의 새로고침과 pagination 초기화 확인.
- 범위 밖: 채팅방 상세 라우팅 변경, 신규 DM 기능.
- TDD 예외 사유: 현재 테스트 번들 타깃이 확인되지 않는 SwiftUI 화면 동작 변경이다.
- 대체 검증 방법: 구현 diff 검토,
xcodebuild빌드, 시뮬레이터 또는 기기 수동 QA.
Files:
- Modify:
SodaLive/Sources/V2/Main/Chat/MainChatView.swift - Modify:
SodaLive/Sources/V2/Main/Chat/MainChatViewModel.swift
Interfaces:
-
Consumes: 기존
selectedFilter,fetchFirstPage(filter:),fetchNextPageIfNeeded(currentItem:). -
Produces: 대화 View의 pull-to-refresh 사용자 동작.
-
상태 확인: 대화 탭의 loading, empty, error, content 분기와
ScrollView배치를 확인한다. -
GREEN: 최소 구현으로 현재
selectedFilter를 유지한 첫 페이지 새로고침을 연결한다. -
GREEN 확인:
전체,AI,DM각 필터에서 새로고침 후 필터가 유지되고rooms,hasMore,nextCursor가 첫 페이지 응답 기준으로 갱신되는지 확인한다. -
REFACTOR: 새로 만든 중복만 정리하고 신규 공통 abstraction은 만들지 않는다.
완료 조건
P3-T1의 체크박스와 완료 증거가 모두 충족됐다.- 대화 필터 3개와 pagination 초기화가 PRD 요구사항과 추적된다.
- 알려진 문서와 구현의 차이가 없다.
검증 방법
Phase 3 Gate
Goal 실행 P3-GATE: 대화 탭 새로고침 사용자 흐름과 최종 회귀 범위를 판정한다.
- 시작 조건:
P3-T1완료. - 완료 증거: 아래 자동·수동 검증 통과와 Progress 기록.
- 범위 밖: 실패와 무관한 다음 기능 구현.
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build
Expected: exit code 0. 대화 전체, AI, DM 필터에서 당겨서 새로고침이 동작하고 필터와 pagination 상태가 올바르게 유지·초기화된다.
수동 검증:
- 대화
전체에서 새로고침한다. - 대화
AI에서 새로고침하고 필터가 유지되는지 확인한다. - 대화
DM에서 새로고침하고 필터가 유지되는지 확인한다. - 빈/오류 상태에서도 새로고침을 재시도할 수 있는지 확인한다.
- 새로고침 후 스크롤 하단에서 다음 페이지 조회가 이어지는지 확인한다.
Phase 4 Lottie 새로고침 표시
Phase 결과: 메인 홈·콘텐츠·대화 7개 View의 모든 refresh 가능 상태에서 기본 indicator 대신 지정된 Lottie가 표시되고 기존 새로고침 동작은 유지된다.
선행조건: Phase 1~3 Gate 완료, PRD REFRESH-ANIMATION-001~005와 DEC-004~011 확정, 사용자 코드 구현 승인.
Phase 완료 조건: P4-T1, P4-T2, P4-GATE 완료와 검증 기록 누적.
구현 항목
Task 4.1 공용 Lottie refresh component
Goal 실행 P4-T1: Lottie 표시와 pull-to-refresh 상태를 한 곳에서 관리하는 V2 공용 vertical scroll component를 만든다.
- 시작 조건:
SodaLive/Resources/pull-to-refresh.json과 Lottie4.6.1이SodaLive,SodaLive-devtarget에 포함되어 있다. - 완료 증거: component source 2개가 두 target에서 빌드되고, pull offset·단일 refresh·완료 초기화·Reduce Motion 상태가 아래 contract를 따른다.
- 범위 밖: 메인 7개 View 연결, legacy
RefreshableScrollView변경, pull progress와 animation progress 연동. - TDD 예외 사유: 현재 저장소에 앱 test bundle target이 없고, ScrollView bounce와 Lottie playback은 runtime UI 동작이다.
- 대체 검증 방법: asset metadata 검사, 두 scheme Debug 빌드, Phase 4 수동 상태 전이 확인.
Files:
- Create:
SodaLive/Sources/V2/Component/Refresh/LottieRefreshIndicator.swift - Create:
SodaLive/Sources/V2/Component/Refresh/LottieRefreshableScrollView.swift - Modify:
SodaLive.xcodeproj/project.pbxproj— 두 source를SodaLive,SodaLive-devSources build phase에 추가 - Verify:
SodaLive/Resources/pull-to-refresh.json
Interfaces:
struct LottieRefreshIndicator: View {
let isAnimating: Bool
}
struct LottieRefreshableScrollView<Content: View>: View {
init(
action: @escaping () async -> Void,
@ViewBuilder content: @escaping () -> Content
)
}
-
Consumes: Lottie
LottieView,pull-to-refresh.json,SodaSpacing.s8,SodaSpacing.s48,EnvironmentValues.accessibilityReduceMotion,I18n.LiveNow.refreshButton. -
Produces: indicator가 없는 vertical
ScrollView,64pttrigger/표시 영역, 중복 실행을 막는 단일 async refresh action, accessibility refresh action. -
Asset 확인: JSON이
150×150,30fps, frame0..<45이고 frame0은 비어 있으며 frame1부터 stroke가 표시되는지 확인한다. -
GREEN:
LottieRefreshIndicator는 일반 설정에서 영역 노출 중 frame0..<45를 반복 재생하고, Reduce Motion에서는 frame1에 정지한다. 크기는 aspect fit 최대48×48pt, 중앙 정렬, Lottie 자체는 accessibility tree에서 숨긴다. -
GREEN:
LottieRefreshableScrollView는 양수 pull offset에서 indicator를 보이고,64pt기준으로 action을 한 번만 실행하며, action이 끝날 때까지 영역을 유지한 뒤 offset·animation을 처음 상태로 되돌린다. -
GREEN: 기준 거리 미만에서 손을 놓으면 API를 호출하지 않고 indicator를 숨겨 frame
0으로 초기화하며, refresh 중 재당김은 두 번째 action을 만들지 않는다. -
GREEN 확인: 두 scheme을 빌드해 Lottie API, async closure, project source membership 오류가 없음을 확인한다.
-
REFACTOR: offset 측정용
PreferenceKey와 refresh 상태는LottieRefreshableScrollView.swift내부 private 구현으로 두고 추가 protocol·ViewModel·config type을 만들지 않는다.
Task 4.2 메인 7개 View 연결
Goal 실행 P4-T2: 기존 7개 View의 20개 .refreshable 경로를 공용 Lottie refresh component로 교체한다.
- 시작 조건:
P4-T1완료. - 완료 증거: 대상 파일의
.refreshable0건, 기존refresh()호출·상태 분기·레이아웃 유지, 두 scheme 빌드와 수동 QA 통과. - 범위 밖: 7개 ViewModel 변경,
LiveNowAllView,ContentDetailView, 최초 loading용 본문ProgressView디자인 변경. - TDD 예외 사유: 현재 앱 test bundle target이 없는 SwiftUI 화면 연결 변경이다.
- 대체 검증 방법: 대상 modifier 정적 검색, ViewModel diff 부재 확인, 두 scheme Debug 빌드, 화면별 수동 QA.
Files:
- Modify:
SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift - Modify:
SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift - Modify:
SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift - Modify:
SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift - Modify:
SodaLive/Sources/V2/Main/Content/Ranking/MainContentRankingView.swift - Modify:
SodaLive/Sources/V2/Main/Content/All/MainContentAllView.swift - Modify:
SodaLive/Sources/V2/Main/Chat/MainChatView.swift - Test: 별도 test file 없음 — Phase 4 Gate의 정적 검사·빌드·수동 검증 사용
Interfaces:
-
Consumes:
LottieRefreshableScrollView(action:content:)와 기존 7개 ViewModel의refresh() async. -
Produces: content·loading·empty/error 분기 전체에서 동일한 Lottie pull-to-refresh 사용자 동작.
-
상태 확인: 현재 7개 View의
.refreshable20곳과 각 content·loading·empty/error 분기를 대조한다. -
GREEN: 각
ScrollView와.refreshable조합을LottieRefreshableScrollView로 교체하고 action에는 기존await viewModel.refresh()만 연결한다. -
GREEN: 기존
frame(minHeight:), background, padding, paginationonAppear와 최초 loadingProgressView를 그대로 유지한다. -
GREEN 확인: 대상 7개 파일의
.refreshable이 0건이고LottieRefreshableScrollView적용 수가 기존 상태 분기 수와 일치하는지 정적 검사한다. -
GREEN 확인: 홈·콘텐츠·대화의 선택 탭·필터·pagination·실패 재시도 동작이 Phase 1~3 결과와 동일한지 수동 확인한다.
-
REFACTOR: 7개 ViewModel, legacy pull-to-refresh 화면과 요청 범위 밖 UI에 diff가 없는지 확인한다.
Task 4.3 pull trigger 회귀 수정
Goal 실행 P4-R1: Phase 4 첫 수동 검증에서 Lottie 표시와 refresh가 모두 실행되지 않은 회귀를 수정한다.
-
원인: viewport와 같은 짧은 content의 bounce 부재, 신뢰할 수 없는
DragGesture.onEnded, 현재 hierarchy에서 양수 pull offset을 만들지 못한 named coordinate 측정이 겹쳤다. -
TDD 예외 사유: 앱 test bundle target이 없고 실제
ScrollViewrubber-band와 gesture arbitration이 필요한 runtime UI 회귀다. -
대체 검증 방법: 사용자 실패 재현, Apple·OSS·설치 package source 비교, 정적 검사, 두 scheme Debug 빌드, 사용자 재확인.
-
Files:
- Modify:
SodaLive/Sources/V2/Component/Refresh/LottieRefreshableScrollView.swift
- Modify:
-
RED: 사용자가 당김 시 Lottie 표시와 refresh가 모두 실행되지 않음을 확인했다.
-
원인 확인: 짧은 content의 bounce 부재,
DragGesture.onEnded트리거와 offset 측정을 기존 동작 package·Apple·OSS 패턴과 비교했다. -
GREEN: vertical bounce를 항상 허용하고 global moving-minus-fixed offset이
64pt를 넘겼다가 돌아오는 전이에서 action을 한 번만 실행하도록 수정했다. -
GREEN 확인: named coordinate·
DragGesture0건, global moving/fixed 측정과 bounce·armed 전이 존재, 두 scheme Debug build 성공을 확인했다. -
수동 확인: 기존 실패 화면에서 Lottie가 표시되고 refresh action이 한 번 실행되는지 사용자가 재확인한다.
Task 4.4 refresh motion 회귀 수정 (롤백)
Goal 실행 P4-R2: 기능 복구 후 refresh 시작·완료 시 발생하는 UI 덜컹임을 제거한다.
-
원인: ScrollView bounce-back 중 refresh row 높이
0→64pt와 indicator offset-64→0이 동시에 즉시 변경되고, 완료 시 row가64→0pt로 즉시 축소됐다. -
TDD 예외 사유: 앱 test bundle target이 없고 실제 ScrollView rubber-band와 layout handoff가 필요한 runtime motion 회귀다.
-
대체 검증 방법: 사용자 체감 RED, 기존 package freeze 패턴 비교, 정적 검사, 두 scheme Debug 빌드, 사용자 재확인.
-
Files:
- Modify:
SodaLive/Sources/V2/Component/Refresh/LottieRefreshableScrollView.swift
- Modify:
-
RED: 사용자가 Lottie 표시와 refresh는 동작하지만 진행이 덜컹거림을 확인했다.
-
원인 확인: 시작·완료의 구조적 row 삽입/축소와 indicator 위치 전환이 bounce-back과 충돌함을 확인했다.
-
GREEN: 구조적 refresh row를 제거하고 content·indicator가 같은 frozen alignment handoff를 사용하도록 수정했다.
-
GREEN 확인: frozen alignment, Reduce Motion 분기, 완료 animation과 두 scheme Debug build 성공을 확인했다.
-
수동 확인: motion은 부드러워졌지만 기존 UI 간격이 변경되는 회귀를 사용자가 확인했다.
-
ROLLBACK: 사용자 결정에 따라 frozen motion handoff와 후속 layout 보정을 모두 제거하고
P4-R1기능 복구 상태로 되돌렸다.
Task 4.5 layout 보존 refresh motion 수정
Goal 실행 P4-R3: 기존 VStack layout과 UI 간격을 유지하면서 refresh 시작·완료의 목록 위치 전환을 부드럽게 한다.
-
원인:
isRefreshing전환 때 refresh 영역 높이와 indicator offset이 animation 없이 즉시0↔64pt로 변경되어 목록이 위아래로 순간 이동한다. -
선택 설계: 현재
VStack과refreshRegion구조는 유지하고isRefreshing시작·완료 상태만 동일한.easeOut(duration: 0.2)transaction으로 처리한다. -
대안 제외: 고정 64pt 영역은 scroll layout을 변경하고,
UIRefreshControl재작성은 현재 회귀에 비해 범위가 크므로 제외한다. -
접근성: Reduce Motion에서는 animation 없이 즉시 상태를 전환한다.
-
TDD 예외 사유: 앱 test bundle target이 없고 실제 ScrollView rubber-band와 layout animation이 필요한 runtime motion 회귀다.
-
대체 검증 방법: 사용자 체감 RED, 구조·상태 전이 정적 검사, 두 scheme Debug 빌드, 사용자 재확인.
-
Files:
- Modify:
SodaLive/Sources/V2/Component/Refresh/LottieRefreshableScrollView.swift
- Modify:
-
RED: Lottie와 refresh는 동작하지만 시작·완료 때 목록이 위아래로 끊겨 이동함을 사용자가 확인했다.
-
설계 승인: 기존 구조의 높이·offset 전환만 animate하는 최소안을 사용자가 선택했다.
-
GREEN: refresh 시작·완료를 같은 Reduce Motion-aware animation transaction으로 처리한다.
-
GREEN 확인: 기존
VStack, global offset 측정, bounce, armed trigger와 두 scheme build 성공을 확인한다. -
수동 확인: 기존 UI 간격이 유지되고 시작·완료 목록 이동이 부드러운지 사용자가 확인한다.
완료 조건
P4-T1,P4-T2의 체크박스와 완료 증거가 모두 충족됐다.REFRESH-ANIMATION-001~005가 구현 또는 명시적 제외로 추적된다.- Lottie와 기본 indicator가 동시에 보이는 대상 상태가 없다.
- Phase 1~3에서 검증한 데이터 refresh 동작에 회귀가 없다.
검증 방법
Phase 4 Gate
Goal 실행 P4-GATE: Lottie 표시, 접근성 설정, 기존 데이터 refresh와 범위 제외를 최종 판정한다.
- 시작 조건:
P4-T1,P4-T2완료. - 완료 증거: 아래 자동·수동 검증 통과와 Progress 기록.
- 범위 밖: Gate 실패와 무관한 UI 변경, legacy refresh 화면 변경, test 삭제·완화.
jq -e '.w == 150 and .h == 150 and .fr == 30 and .ip == 0 and .op == 45' SodaLive/Resources/pull-to-refresh.json
! rg -n '\.refreshable' SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift SodaLive/Sources/V2/Main/Home/Ranking/MainHomeRankingView.swift SodaLive/Sources/V2/Main/Home/Following/MainHomeFollowingView.swift SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift SodaLive/Sources/V2/Main/Content/Ranking/MainContentRankingView.swift SodaLive/Sources/V2/Main/Content/All/MainContentAllView.swift SodaLive/Sources/V2/Main/Chat/MainChatView.swift
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -sdk iphonesimulator -destination "generic/platform=iOS Simulator" build CODE_SIGNING_ALLOWED=NO
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug -sdk iphonesimulator -destination "generic/platform=iOS Simulator" build CODE_SIGNING_ALLOWED=NO
git diff --check
Expected: 모든 명령 exit code 0. 대상 7개 파일에 .refreshable이 없고 두 scheme이 빌드되며, asset metadata와 target membership이 일치한다.
수동 검증:
- 기준 거리 미만으로 당기는 동안 Lottie가 반복 재생되고 손을 놓으면 API 호출 없이 사라진 뒤 다음 당김에서 처음부터 재생된다.
64pt기준을 넘겨 새로고침하면 Lottie가 API 완료까지 유지되고 기본 indicator는 보이지 않으며 요청은 한 번만 실행된다.- Reduce Motion을 켜면 frame
1이 정지 표시되고, accessibility refresh action으로도 동일한 기존refresh()가 한 번 실행된다. - 홈 3개·콘텐츠 3개·대화 View의 content·loading·empty/error 상태에서 최대
48×48ptLottie와 기존 데이터 갱신 동작을 확인한다. - iPhone과 iPad에서 영역이 총
64pt이고 content가 겹치거나 잘리지 않으며,LiveNowAllView와ContentDetailView는 기존 표시를 유지한다.
실행 순서와 의존성
| 순서 | Goal | 선행조건 | 병행 가능 | 차단 시 다음 행동 |
|---|---|---|---|---|
| 1 | P1-T1 |
PRD 확정 | 아니요 | 홈 탭 상태 분기 재확인 |
| 2 | P1-GATE |
P1-T1 |
아니요 | 실패 소유 Task의 회귀 수정 goal 생성 |
| 3 | P2-T1 |
P1-GATE |
아니요 | 콘텐츠 탭 상태 분기 재확인 |
| 4 | P2-GATE |
P2-T1 |
아니요 | 실패 소유 Task의 회귀 수정 goal 생성 |
| 5 | P3-T1 |
P2-GATE |
아니요 | 대화 탭 상태 분기 재확인 |
| 6 | P3-GATE |
P3-T1 |
아니요 | 실패 소유 Task의 회귀 수정 goal 생성 |
| 7 | P4-T1 |
P3-GATE, 구현 승인 |
아니요 | asset·Lottie target membership 재확인 |
| 8 | P4-T2 |
P4-T1 |
아니요 | 대상 View의 상태 분기와 wrapper contract 재확인 |
| 9 | P4-GATE |
P4-T2 |
아니요 | 실패 소유 Task의 회귀 수정 goal 생성 |
P1-T1 -> P1-GATE -> P2-T1 -> P2-GATE -> P3-T1 -> P3-GATE -> P4-T1 -> P4-T2 -> P4-GATE
변경 금지 항목
- 확정된 요구사항을 근거 없이 변경하지 않는다.
- 기존 완료 체크박스와 Progress·Decision Log·검증 기록을 삭제하거나 덮어쓰지 않는다.
- 신규 endpoint, DTO, dependency와
P4-T1의 component 2개 외 추가 refresh abstraction을 만들지 않는다. - 요청 범위 밖의 리팩터링과 UI 재설계를 하지 않는다.
- test를 삭제·skip·완화하거나 타입 오류를 우회해 Gate를 통과시키지 않는다.
- JWT, password, signed URL, 개인 메시지 본문 같은 민감정보를 log·fixture·문서에 기록하지 않는다.
의사결정 및 중단 규칙
- PRD와 구현 계획이 충돌하면 PRD를 우선하고 Decision Log에 정정 기록을 남긴다.
- 구현 범위가 바뀌면
prd.md를 먼저 보강하고plan-task.md체크리스트를 업데이트한 뒤 코드를 수정한다. - 안전한 최소 기본값이 문서에 있으면 그 값만 구현한다. 안전하게 구현할 수 없으면 추정하지 않는다.
- 체크박스 일부, 빌드 일부 또는 코드 작성만 끝난 상태에서는 goal을
complete로 갱신하지 않는다. - 완료 증거와 Progress 기록까지 충족한 뒤에만 goal을
complete로 갱신한다.
Progress
기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다.
문서 작성 1차 실행 — 2026-08-18
- 상태: 완료
- 무엇을:
prd.md와plan-task.md신규 작성. - 왜: 코드 구현 전 요구사항과 구현 계획을 확정하기 위해 작성.
- 어떻게:
docs/sample/sample-prd.md확인 — 샘플 구조 확인.docs/sample/sample-plan-task.md확인 — 계획 문서 구조 확인.docs/agent-guides/documentation-policy.md확인 — 신규 문서 경로 규칙 확인.- 관련 V2 메인 홈/콘텐츠/대화 View와 ViewModel 확인 — 기존 fetch 경로와 선택 상태 확인.
- 남은 항목: 코드 구현 전체.
- 다음 행동: 사용자가 구현을 승인하면
P1-T1부터 진행.
P1-T1~P3-T1 1차 실행 — 2026-08-18
- 상태: 구현 완료 / 사용자 수동 검증 대기
- 무엇을: 홈, 콘텐츠, 대화 탭의 현재 선택 상태별 당겨서 새로고침과 Combine 완료 대기 연결.
- 왜: 사용자가 현재 화면을 벗어나지 않고 최신 첫 페이지 또는 단일 조회 결과를 받을 수 있게 하기 위해 구현.
- 어떻게:
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -sdk iphonesimulator -destination "generic/platform=iOS Simulator" build CODE_SIGNING_ALLOWED=NO— 성공, exit code 0,BUILD SUCCEEDED.git diff --check— 성공, 오류 없음.- 코드 검토 — 홈/콘텐츠/대화 현재 선택 상태 유지, 빈/오류 상태 ScrollView 제공, 대화/콘텐츠 전체 첫 페이지 응답 기준 pagination 교체 확인.
- visual-qa — 사용자 요청에 따라 미실행.
- 남은 항목: 아래 사용자 수동 테스트 목록과 각 Phase Gate 체크박스 확인.
- 다음 행동: 사용자가 수동 테스트를 수행하고 성공/실패 결과를 전달한다.
P1-T1~P3-T1 리뷰 보정 — 2026-08-18
- 상태: 구현 완료 / 사용자 수동 검증 대기
- 무엇을: Combine 취소 시 refresh 대기 종료, 최초 loading 상태 새로고침, 콘텐츠 전체 최초 요청 중복 방지 보정.
- 왜: checked continuation은 Combine 구독 취소 시 completion을 받지 못해 대기할 수 있고, 기존 최초 loading 화면에는 refresh 가능한 ScrollView가 없었기 때문이다.
- 어떻게:
- 7개 ViewModel — checked continuation 대신
$isLoading.values를 기다리도록 변경하고 이미 로딩 중이면 중복 요청 없이 기존 요청 완료를 기다림. - 7개 View — 최초 loading을 full-height ScrollView로 구성해 기본
.refreshable동작 제공. MainContentAllViewModel.fetchFirstPageIfNeeded()—isLoading == falseguard를 추가해 탭 재진입 중 page 0 중복 요청 차단.xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug -sdk iphonesimulator -destination "generic/platform=iOS Simulator" build CODE_SIGNING_ALLOWED=NO— 성공, exit code 0,BUILD SUCCEEDED.git diff --check— 성공, 오류 없음.- Oracle 최종 코드 리뷰 — criterion-blocking finding 없음, 무조건 승인.
- 7개 ViewModel — checked continuation 대신
- 남은 항목: 아래 사용자 수동 테스트 목록과 각 Phase Gate 체크박스 확인.
- 다음 행동: 사용자가 수동 테스트를 수행하고 성공/실패 결과를 전달한다.
P1-GATE~P3-GATE 사용자 수동 검증 — 2026-08-18
- 상태: 완료
- 무엇을: 홈, 콘텐츠, 대화 탭의 당겨서 새로고침과 선택 상태·pagination·오류 재시도 회귀를 사용자 수동 확인.
- 왜: 실제 로그인 데이터와 네트워크 환경에서 사용자 동작 기준을 최종 판정하기 위해 수행.
- 어떻게:
- 사용자 수동 테스트 목록 전체 — 성공 확인.
- PRD 기능·UI/UX 수용 기준 — 충족 확인.
- 남은 항목: 없음.
- 다음 행동: 없음.
Phase 4 요구사항 인터뷰와 계획 작성 — 2026-08-25
- 상태: 계획 완료 / 구현 미착수
- 무엇을: Lottie playback·크기·Reduce Motion·적용 범위를 확정하고
P4-T1,P4-T2,P4-GATE를 작성했다. - 왜: 기존 7개 View·20개 상태에 같은 표시 규칙을 적용하되 legacy 화면과 데이터 refresh 로직을 변경하지 않기 위해 작성했다.
- 어떻게:
rg로.refreshable과RefreshableScrollView실제 사용 화면 확인 — 메인 7개 View와 legacy 2개 View 확인.jq로pull-to-refresh.json확인 —150×150,30fps, frame0..<45, frame0비어 있음 확인.- Xcode project와
Package.resolved확인 — Lottie4.6.1및 asset이SodaLive,SodaLive-dev두 target에 포함됨 확인. - Lottie·Apple 공식 문서와 현재
RefreshableScrollView1.1.1source 확인 — 공용 V2 component 계획 근거 확인. git diff --check -- docs/20260818_메인_탭_당겨서_새로고침/prd.md docs/20260818_메인_탭_당겨서_새로고침/plan-task.md— 성공, exit code 0.- placeholder·미결정 검색 — 남은 placeholder와 미결 요구사항 없음.
git diff --name-only -- SodaLive/Sources— 출력 없음, 앱 source 코드 미수정 확인.
- 남은 항목:
P4-T1,P4-T2,P4-GATE전체. 이번 요청에서는 코드 구현하지 않음. - 다음 행동: 사용자가 별도로 코드 구현을 요청하면
P4-T1부터 시작.
P4-T1~P4-T2 구현과 자동 Gate — 2026-08-25
- 상태: 구현·자동 검증 완료 / 사용자 수동 검증 대기
- 무엇을: V2 공용 Lottie refresh component 2개를 만들고 메인 7개 View의
.refreshable20곳을 교체했다. - 왜: 기존 데이터 새로고침 동작을 유지하면서 지정된 Lottie 표시·접근성·중복 요청 방지 규칙을 한 곳에서 적용하기 위해 구현했다.
- 어떻게:
jqasset metadata 검사 —150×150,30fps, frame0..<45확인, exit code 0.- 정적 검사 — 대상 7개 View의
.refreshable0건,LottieRefreshableScrollView20건(2/3/3/3/3/3/3) 확인. - 범위 검사 — 7개 ViewModel,
LiveNowAllView,ContentDetailViewdiff 없음 확인. xcodebuildSodaLiveDebug simulator build — 성공, exit code 0,BUILD SUCCEEDED.xcodebuildSodaLive-devDebug simulator build — 성공, exit code 0,BUILD SUCCEEDED.git diff --check— 성공, 오류 없음.- P4-T1·P4-T2 spec review와 code-quality review — 차단 finding 없이 승인.
- visual-qa — 사용자 요청에 따라 미실행.
- 남은 항목: Phase 4 수동 검증 5개와
P4-GATE최종 완료 처리. - 다음 행동: 사용자가 아래 Phase 4 Lottie 후속 검증 5개를 확인하고 결과를 전달한다.
P4-R1 pull trigger 회귀 수정 — 2026-08-25
- 상태: 수정·자동 검증 완료 / 사용자 재확인 대기
- 무엇을:
LottieRefreshableScrollView의 짧은 content bounce와 refresh release 판정을 수정했다. - 왜: Phase 4 첫 수동 검증에서 Lottie 표시와 데이터 refresh가 모두 실행되지 않았기 때문이다.
- 어떻게:
- 사용자 RED — 당김 시 Lottie 표시 없음, refresh 실행 없음 확인.
- 원인 조사 —
ScrollViewcontent-size 기반 bounce와 customDragGesture.onEnded취소 가능성을 Apple·OSS·기존 package source로 교차 확인. - 1차 수정 —
.scrollBounceBehavior(.always, axes: .vertical)추가,DragGesture제거, preference armed→release 전이 적용. 사용자 재확인에서 동일 증상 확인. - 2차 수정 — 기존 동작 package와 같은 global moving-minus-fixed frame 차이로 pull offset 측정 교체.
- 정적 검사 — named coordinate·
DragGesture0건, global moving/fixed 측정과 bounce·armed 상태 전이 확인. xcodebuildSodaLive,SodaLive-devDebug simulator build — 성공,BUILD SUCCEEDED.
- 남은 항목: 기존 실패 화면 사용자 재확인과 Phase 4 수동 검증 5개.
- 다음 행동: 사용자가 기존 실패 화면에서 다시 당겨 Lottie 표시와 refresh 실행 여부를 확인한다.
P4-R2 motion 보정과 롤백 — 2026-08-25
- 상태: 사용자 결정으로 롤백
- 무엇을: refresh 시작·완료 덜컹임을 줄이기 위해 frozen alignment handoff를 적용했으나 기존 UI 간격이 변경되어 해당 보정과 후속 layout 수정을 모두 제거했다.
- 왜: 사용자 우선순위에 따라 기존 UI layout을 motion 개선보다 우선 보존하기 위해 롤백했다.
- 어떻게:
- motion 보정 후 새로고침 진행이 부드러워짐을 사용자 확인.
- 기존 UI 간격 변경 회귀를 사용자 확인.
LottieRefreshableScrollView를 global moving-minus-fixed offset 기반P4-R1기능 복구 상태로 복원.
- 남은 항목: Phase 4 수동 검증 잔여 항목.
- 다음 행동: 기존 UI layout과 Lottie refresh 기능 유지 여부를 확인한다.
P4-R3 layout 보존 motion 수정 — 2026-08-26
- 상태: 구현·자동 검증 완료 / 사용자 확인 대기
- 무엇을: 기존
VStack과 refresh hierarchy를 유지한 채 시작·완료의isRefreshing상태 전환만.easeOut(duration: 0.2)로 처리했다. - 왜: Lottie와 refresh 기능은 정상이지만 refresh 영역 높이가 즉시
0↔64pt로 바뀌어 목록이 위아래로 순간 이동했기 때문이다. - 어떻게:
- 사용자 RED — Lottie와 refresh는 동작하지만 목록 위치가 시작·완료 때 끊겨 이동함을 확인.
- 정적 검사 —
VStack(spacing: 0), global moving-minus-fixed 측정, bounce, armed trigger, refresh 영역 높이·offset 식 보존 확인. - 정적 검사 — Reduce Motion-aware
withAnimation2건,ZStack,alignmentGuide,UIRefreshControl,frozen0건 확인. git diff --check— 성공, 오류 없음.xcodebuildSodaLive,SodaLive-devDebug simulator build — 각각 성공,BUILD SUCCEEDED2건 확인.- spec compliance review와 code-quality review — 승인, runtime motion과 layout은 사용자 확인 대상으로 유지.
- TDD 예외: 앱 test bundle target이 없고 ScrollView rubber-band와 layout animation의 runtime 상호작용이 필요한 UI 회귀이므로 사용자 RED와 정적 검사·두 scheme build를 대체 증거로 사용했다.
- 남은 항목: 기존 UI 간격 유지, 시작·완료 목록 이동, Reduce Motion, 단일 refresh 실행 사용자 확인.
- 다음 행동: 사용자가 기존 화면에서 refresh를 다시 실행해 layout과 motion을 확인한다.
사용자 수동 테스트 목록
사전 조건:
- Debug 앱을 실제 기기 또는 iOS Simulator에서 실행한다.
- 로그인 후 메인 화면에 진입한다.
- 각 테스트에서 화면 상단 목록을 아래로 충분히 당겨 Lottie 새로고침 표시가 보이는지 확인한다.
홈 탭
홈 > 추천: 아래로 당기면 indicator가 API 응답까지 유지되고 최신 데이터가 표시된다. 선택 탭은추천으로 유지된다.홈 > 랭킹: 아래로 당기면 랭킹이 다시 조회되고 선택 탭은랭킹으로 유지된다.홈 > 팔로잉: 아래로 당기면 팔로잉 데이터가 다시 조회되고 선택 탭은팔로잉으로 유지된다.홈 > 팔로잉로그인 필요/빈/오류 상태: 상태 화면에서도 아래로 당겨 재시도할 수 있다.
콘텐츠 탭
콘텐츠 > 추천: 아래로 당기면 추천 데이터가 다시 조회되고 선택 탭은추천으로 유지된다.콘텐츠 > 랭킹: 주간/월간 등 기본값이 아닌 랭킹 타입을 선택한 뒤 새로고침해도 같은 타입이 유지된다.콘텐츠 > 전체 > 오디오: 타입과 정렬을 변경한 뒤 새로고침해도 선택한 타입/정렬이 유지되고 목록이 첫 페이지 결과로 교체된다.콘텐츠 > 전체 > 시리즈: 요일과 정렬을 변경한 뒤 새로고침해도 타입/요일/정렬이 유지되고 목록이 첫 페이지 결과로 교체된다.- 콘텐츠의 빈/오류 상태: 상태 화면에서도 아래로 당겨 재시도할 수 있다.
대화 탭
대화 > 전체: 아래로 당기면ALL첫 페이지가 다시 조회되고 선택 필터는 유지된다.대화 > AI: 아래로 당기면AI첫 페이지가 다시 조회되고 선택 필터는 유지된다.대화 > DM: 아래로 당기면DM첫 페이지가 다시 조회되고 선택 필터는 유지된다.- 대화 빈/오류 상태: 상태 화면에서도 아래로 당겨 재시도할 수 있다.
- 대화 목록 새로고침 후 하단까지 스크롤하면 다음 페이지가 중복 없이 이어서 추가된다.
공통 회귀
- 새로고침 중 다른 내부 탭/필터로 강제 전환되지 않는다.
- 새로고침 실패 시 기존 토스트 또는 오류/빈 상태가 표시되고 다시 당겨 재시도할 수 있다.
- 하단
마이탭에는 이번 변경으로 새로고침 동작이 추가되지 않았다.
Phase 4 Lottie 후속 검증
- 당김 영역 노출 중 반복 재생, 기준 미만 취소 시 숨김·초기화를 확인한다.
- refresh 시작 후 API 완료까지 Lottie가 유지되고 요청이 한 번만 실행되는지 확인한다.
- Reduce Motion에서 frame
1정지 표시와 accessibility refresh action을 확인한다. - 홈·콘텐츠·대화 7개 View의 content·loading·empty/error 상태와 iPhone·iPad 레이아웃을 확인한다.
- legacy
LiveNowAllView,ContentDetailView가 변경되지 않았는지 확인한다.
Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|---|---|---|---|---|---|
| 2026-08-18 | DEC-001 |
확정 | 당겨서 새로고침은 현재 선택된 내부 탭·필터·정렬 상태만 대상으로 한다. | 사용자 A안 선택 | P1-T1, P2-T1, P3-T1, prd.md |
| 2026-08-18 | DEC-002 |
확정 | 빈/오류 상태에서도 당겨서 새로고침을 허용한다. | 사용자 A안 선택 | P1-GATE, P2-GATE, P3-GATE, prd.md |
| 2026-08-18 | DEC-003 |
확정 | 신규 API, dependency, 공통 abstraction 없이 기존 fetch 경로를 연결한다. | 최소 변경 원칙과 기존 ViewModel 구조 | 전체 Goal |
| 2026-08-25 | DEC-004~010 |
확정 | Lottie asset·재생·크기·Reduce Motion·적용 화면을 PRD 인터뷰 결과대로 적용한다. | 사용자 인터뷰와 asset 확인 | P4-T1, P4-T2, P4-GATE, prd.md |
| 2026-08-25 | DEC-011 |
정정 | DEC-003의 공통 abstraction 제외는 Phase 1~3에 유지하고, Phase 4에서는 V2 공용 component 한 개만 허용한다. |
7개 View·20개 상태의 중복 방지 | P4-T1, P4-T2, prd.md |
| 2026-08-26 | DEC-012 |
확정 | 기존 VStack layout을 유지하고 refresh 상태의 0↔64pt 높이·offset 전환만 0.2초 easeOut으로 보간한다. |
기존 UI 간격 보존과 최소 변경 우선 | P4-R3, P4-GATE |
발견된 문제
| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 |
|---|---|---|---|---|---|
ISSUE-001 |
Low | 해결 | 현재 테스트 번들 타깃이 명확하지 않아 SwiftUI 새로고침 동작을 자동 테스트로 고정하기 어렵다. | 전체 Gate | Debug 빌드와 사용자 수동 검증을 완료 증거로 기록했다. 테스트 타깃이 추가되면 focused test를 후속 보강한다. |
ISSUE-002 |
High | 해결 | custom ScrollView의 bounce·gesture 종료·offset 측정 경로 때문에 Lottie와 refresh가 실행되지 않았다. |
P4-R1, P4-GATE |
bounce 강제, preference armed→release 전이, global moving-minus-fixed offset 측정으로 수정하고 사용자 동작 확인을 통과했다. |
ISSUE-003 |
Medium | 롤백 | frozen motion handoff는 덜컹임을 줄였지만 기존 UI 간격을 변경했다. | P4-R2 |
사용자 결정에 따라 motion 보정과 후속 layout 수정을 모두 롤백했다. |
최종 보고 형식
구현 결과: 메인 홈, 콘텐츠, 대화 탭의 Lottie 당겨서 새로고침 구현 완료 여부
- 변경: 기존 데이터 새로고침을 유지한 채 메인 7개 View의 기본 indicator를 공용 Lottie refresh component로 교체
- 결정: DEC-006 영역 노출 중 반복, DEC-007 최대 48×48pt·64pt 영역, DEC-008~009 Reduce Motion frame 1, DEC-010 메인 7개 View 한정
- 검증:
- `xcodebuild` `SodaLive`, `SodaLive-dev` Debug simulator build — 성공/실패와 exit code
- 수동 검증 — Lottie 재생·크기·Reduce Motion·기존 refresh 회귀·legacy 제외 결과
- 남은 항목: 미완료 `P4-*` 또는 없음
- 문서: `docs/20260818_메인_탭_당겨서_새로고침/prd.md`, `docs/20260818_메인_탭_당겨서_새로고침/plan-task.md`