PRD: 메인 탭 당겨서 새로고침
문서 정보
| 항목 |
내용 |
| 문서 상태 |
구현 완료 |
| 작성일 |
2026-08-18 |
| 최종 수정일 |
2026-08-18 |
| 대상 제품 |
메인 홈, 콘텐츠, 대화 탭 당겨서 새로고침 |
| 작성자·결정권자 |
사용자, Sisyphus |
| 관련 API Contract |
기존 각 탭 API 계약 재사용, 신규 endpoint 없음 |
| 관련 구현 계획 |
docs/20260818_메인_탭_당겨서_새로고침/plan-task.md |
| 관련 review |
없음 |
요구사항 상태
| 상태 |
의미 |
구현 처리 |
| 확정 |
제품·기술 결정이 완료되어 구현 기준으로 사용 |
plan-task.md의 Task와 완료 증거로 추적 |
| 미결 |
제품·UX·운영 결정이 더 필요함 |
권고안과 결정 주체를 기록하고 임의 구현 금지 |
| 제외 |
현재 릴리스에서 구현하지 않기로 결정 |
제외 이유와 후속 조건을 Decision Log에 기록 |
1. Overview
메인 하단 홈, 콘텐츠, 대화 탭에 iOS 기본 당겨서 새로고침 동작을 추가한다. 사용자가 현재 보고 있는 내부 탭과 필터 상태를 유지한 채 해당 화면의 첫 페이지 또는 단일 조회 API를 다시 호출해 최신 데이터를 표시한다.
구현 결과와 사용자 수동 검증 대기 상태는 관련 구현 계획에서 추적한다.
2. Problem Statement
현재 V2 메인 화면의 주요 조회 탭은 최초 진입 또는 내부 필터 변경 시에만 데이터를 불러온다. 사용자가 이미 보고 있는 화면에서 최신 상태를 직접 확인하려면 탭을 이동하거나 앱 흐름을 다시 진입해야 한다.
문제를 해결했다는 판단은 홈, 콘텐츠, 대화 탭에서 현재 선택 상태 그대로 당겨서 새로고침을 실행하고, 성공·빈 목록·오류 상태에서도 재시도 가능한 것으로 한다.
3. Goals
3.1 제품 목표
홈, 콘텐츠, 대화 하단 탭에서 사용자가 수동으로 최신 데이터를 다시 조회할 수 있다.
- 새로고침은 현재 선택된 내부 탭·필터·정렬 상태만 대상으로 한다.
- 새로고침 실패 시 기존 오류 노출 패턴을 유지하고, 사용자는 같은 화면에서 다시 당겨서 재시도할 수 있다.
3.2 UX 목표
- iOS 기본 pull-to-refresh 제스처와 표시 방식을 사용한다.
- loading, empty, error, content 상태 어디서든 당겨서 새로고침을 시도할 수 있다.
- 새로고침은 현재 선택된 탭을 바꾸지 않고 스크롤 가능한 화면 문맥 안에서 동작한다.
4. Non-Goals
마이 탭에는 당겨서 새로고침을 추가하지 않는다.
- 하단 탭 전체 데이터를 한 번에 새로고침하지 않는다.
- background 자동 갱신, 주기적 polling, push 기반 갱신은 추가하지 않는다.
- 신규 API endpoint, DTO, Repository를 만들지 않는다.
- 새 공통 추상화나 refresh protocol을 만들지 않는다. 기존
ViewModel fetch 경로를 화면별로 연결한다.
5. Target Users and Permissions
5.1 사용자
| 사용자 |
목표 |
주요 작업 |
사용 환경 |
| 로그인 사용자 |
메인 화면의 최신 홈·콘텐츠·대화 데이터를 직접 갱신 |
당겨서 새로고침 |
iOS 앱 |
| 비로그인 또는 로그인 필요 상태 사용자 |
실패/제한 상태에서 재시도 |
당겨서 새로고침 |
iOS 앱 |
5.2 권한
- 인증 주체: 기존 앱 인증 상태와 각 API의 현재 인증 정책을 그대로 사용한다.
- 허용 역할: 기존 각 탭 접근 정책을 따른다.
- 거부 조건: 기존 API 실패·로그인 필요 상태를 그대로 표시한다.
- 리소스 소유권: 기존 API와 화면 모델의 소유권 검증을 변경하지 않는다.
6. 핵심 사용자 흐름
- 사용자가 메인 하단
홈, 콘텐츠, 대화 중 하나에 진입한다.
- 사용자는 내부 탭 또는 필터를 선택해 특정 상태를 보고 있다.
- 사용자가 화면을 아래로 당긴다.
- 앱은 현재 선택 상태를 유지하고 해당 상태의 첫 페이지 또는 단일 조회 API를 다시 호출한다.
- 성공하면 최신 데이터로 교체하고, 실패하면 기존 오류 메시지/토스트 정책을 유지한다.
- 빈 목록이나 오류 상태에서도 사용자는 다시 당겨서 새로고침할 수 있다.
7. 정보 구조와 라우팅
- 새로고침은 라우팅을 변경하지 않는다.
- 현재 하단 탭과 내부 선택 상태는 유지한다.
- 목록 아이템 상세 진입, 검색, 충전, 보관함 라우팅은 변경하지 않는다.
8. 기능 요구사항
8.1 공통 새로고침 동작
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
REFRESH-001 |
확정 |
홈, 콘텐츠, 대화 하단 탭에 당겨서 새로고침을 제공한다. |
각 탭에서 아래로 당기면 현재 화면의 조회 API가 다시 호출된다. |
P1-T1, P2-T1, P3-T1 |
REFRESH-002 |
확정 |
현재 선택된 내부 탭·필터·정렬·요일 상태만 새로고침한다. |
선택 상태가 변경되지 않고 해당 조건의 데이터만 첫 페이지부터 다시 조회된다. |
P1-T1, P2-T1, P3-T1 |
REFRESH-003 |
확정 |
loading, empty, error, content 상태 모두에서 새로고침을 시도할 수 있다. |
빈/오류 화면에서도 pull-to-refresh 제스처 또는 동등한 재시도 흐름이 가능하다. |
P1-GATE, P2-GATE, P3-GATE |
REFRESH-004 |
확정 |
새로고침은 기존 loading guard와 요청 경합 방지 정책을 깨지 않는다. |
중복 요청이 화면 상태를 역전시키지 않고, 기존 최신 요청 우선 패턴을 유지한다. |
P2-T1, P3-T1 |
8.2 홈 탭
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
HOME-REFRESH-001 |
확정 |
홈 추천 선택 시 MainHomeRecommendationViewModel.fetchRecommendations() 경로로 다시 조회한다. |
기존 추천 섹션 데이터가 최신 응답으로 교체된다. |
P1-T1 |
HOME-REFRESH-002 |
확정 |
홈 랭킹 선택 시 MainHomeRankingViewModel.fetchRankings() 경로로 다시 조회한다. |
기존 랭킹 데이터와 rank change 표시가 최신 응답으로 교체된다. |
P1-T1 |
HOME-REFRESH-003 |
확정 |
홈 팔로잉 선택 시 MainHomeFollowingViewModel.fetchFollowing() 경로로 다시 조회한다. |
로그인 필요, 빈 상태, 콘텐츠 상태가 최신 응답 기준으로 갱신된다. |
P1-T1 |
8.3 콘텐츠 탭
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CONTENT-REFRESH-001 |
확정 |
콘텐츠 추천 선택 시 MainContentRecommendationViewModel.fetchRecommendations() 경로로 다시 조회한다. |
추천 콘텐츠 섹션 데이터가 최신 응답으로 교체된다. |
P2-T1 |
CONTENT-REFRESH-002 |
확정 |
콘텐츠 랭킹 선택 시 현재 AudioRankingType을 유지하고 MainContentRankingViewModel.fetchRankings() 경로로 다시 조회한다. |
선택된 랭킹 타입이 유지되고 목록이 최신 응답으로 교체된다. |
P2-T1 |
CONTENT-REFRESH-003 |
확정 |
콘텐츠 전체 선택 시 현재 MainContentAllType, ContentSort, SeriesPublishedDaysOfWeek를 유지하고 첫 페이지를 다시 조회한다. |
기존 목록이 첫 페이지 결과로 교체되고 다음 페이지 상태가 초기화된다. |
P2-T1 |
8.4 대화 탭
| ID |
상태 |
요구사항 |
수용 기준 |
계약/Goal 연결 |
CHAT-REFRESH-001 |
확정 |
대화 탭에서 현재 MainChatFilter를 유지하고 첫 페이지를 다시 조회한다. |
ALL, AI, DM 중 선택된 필터가 유지되고 rooms가 첫 페이지 결과로 교체된다. |
P3-T1 |
CHAT-REFRESH-002 |
확정 |
새로고침 후 pagination 상태를 첫 페이지 기준으로 초기화한다. |
hasMore, nextCursor가 최신 첫 페이지 응답 기준으로 갱신된다. |
P3-T1 |
9. 반응형 기능 범위
| 기능 |
iPhone |
iPad |
비고 |
| 당겨서 새로고침 |
전체 |
전체 |
SwiftUI 기본 동작 기준 |
| 빈/오류 상태 재시도 |
전체 |
전체 |
상태 화면도 스크롤 가능한 컨테이너 안에서 표시 |
10. UI/UX Expectations
10.1 디자인과 component 원칙
- SwiftUI
.refreshable 또는 동등한 iOS 기본 refresh affordance를 사용한다.
- 별도 커스텀 spinner, 신규 refresh button, 신규 공통 컴포넌트는 만들지 않는다.
- 기존
Color.black, ProgressView, sodaToast, empty state 스타일을 유지한다.
10.2 화면 상태
- 첫 로딩 상태와 수동 새로고침 상태를 구분하되, 기존 화면 레이아웃을 크게 바꾸지 않는다.
- 빈/오류 상태에서도 새로고침 가능해야 하므로 상태 화면을 refresh 가능한 스크롤 컨테이너로 감싸는 방식을 계획한다.
- 새로고침 실패 시 현재 탭 선택 상태는 유지한다.
10.3 접근성
- 기본 iOS refresh control 접근성 동작을 해치지 않는다.
- 기존 버튼, 탭, 리스트 아이템의 focus/touch target을 변경하지 않는다.
11. API 계약
11.1 공통 규칙
- 신규 endpoint 없음.
- 기존 Repository와 API 계약을 그대로 사용한다.
- 콘텐츠
전체와 대화 탭은 첫 페이지 재조회 시 기존 pagination cursor/page 상태를 초기화한다.
11.2 Endpoint 추적
| 요구사항 |
Method |
Path |
계약 상태 |
API Contract |
소유 Goal |
HOME-REFRESH-001 |
GET |
기존 홈 추천 API |
제공됨 |
기존 MainHomeRecommendationRepository |
P1-T1 |
HOME-REFRESH-002 |
GET |
기존 홈 랭킹 API |
제공됨 |
기존 MainHomeRankingRepository |
P1-T1 |
HOME-REFRESH-003 |
GET |
기존 홈 팔로잉 API |
제공됨 |
기존 MainHomeFollowingRepository |
P1-T1 |
CONTENT-REFRESH-001 |
GET |
기존 콘텐츠 추천 API |
제공됨 |
기존 MainContentRecommendationRepository |
P2-T1 |
CONTENT-REFRESH-002 |
GET |
기존 콘텐츠 랭킹 API |
제공됨 |
기존 MainContentRankingRepository |
P2-T1 |
CONTENT-REFRESH-003 |
GET |
기존 콘텐츠 전체 API |
제공됨 |
기존 MainContentAllRepository |
P2-T1 |
CHAT-REFRESH-001 |
GET |
/api/v2/chat/rooms |
제공됨 |
docs/20260708_홈_채팅_탭/prd.md |
P3-T1 |
12. 보안과 데이터 취급
- 인증 header, token 저장, 언어 header 흐름은 변경하지 않는다.
- 새로고침 실패 로그에 token, 개인 메시지 본문, signed URL 등 민감정보를 기록하지 않는다.
- 기존
AuthPlugin, UserDefaultsKey.token 처리 로직은 수정 대상이 아니다.
13. 성능과 품질 요구사항
- 이미 로딩 중인 동일 화면에서 중복 새로고침이 과도하게 발생하지 않도록 기존 loading guard를 유지하거나 같은 수준의 guard를 둔다.
- 콘텐츠
전체와 대화 탭은 첫 페이지 새로고침 시 기존 다음 페이지 로딩 상태를 초기화한다.
- 새 공통 abstraction을 만들지 않고 화면별 최소 변경으로 구현한다.
- 현재 테스트 번들 타깃이 명확하지 않으므로 구현 검증은 focused 코드 리뷰,
xcodebuild 빌드, 수동 QA를 필수로 한다.
14. 성공 기준
14.1 기능 수용 기준
14.2 UI/UX 수용 기준
14.3 추적성 완료 기준
15. Open Questions
| ID |
상태 |
결정 필요 사항 |
현재 권고 |
결정 주체 |
결정 기한/시점 |
영향 Goal |
| 없음 |
확정 |
남은 제품 결정 없음 |
없음 |
사용자 |
2026-08-18 |
전체 |
16. 요구사항 추적표
| 요구사항 범위 |
API Contract |
계획 Phase |
Goal |
자동 검증 |
수동 검증 |
REFRESH-001~004 |
기존 API 재사용 |
1~3 |
P1-T1, P2-T1, P3-T1 |
빌드 |
홈/콘텐츠/대화 새로고침 |
HOME-REFRESH-001~003 |
홈 기존 Repository |
1 |
P1-T1, P1-GATE |
빌드 |
홈 내부 탭별 새로고침 |
CONTENT-REFRESH-001~003 |
콘텐츠 기존 Repository |
2 |
P2-T1, P2-GATE |
빌드 |
콘텐츠 내부 탭/필터별 새로고침 |
CHAT-REFRESH-001~002 |
/api/v2/chat/rooms |
3 |
P3-T1, P3-GATE |
빌드 |
대화 필터별 새로고침 |
17. Decision Log
| 날짜 |
ID |
상태 |
결정 |
근거 |
영향 요구사항·계약·Goal |
| 2026-08-18 |
DEC-001 |
확정 |
당겨서 새로고침은 현재 선택된 내부 탭·필터·정렬 상태만 대상으로 한다. |
사용자 인터뷰 A안 선택 |
REFRESH-002, 전체 Goal |
| 2026-08-18 |
DEC-002 |
확정 |
빈/오류 상태에서도 당겨서 새로고침을 허용한다. |
사용자 인터뷰 A안 선택 |
REFRESH-003, 전체 Gate |
| 2026-08-18 |
DEC-003 |
확정 |
신규 API, 신규 dependency, 신규 공통 refresh abstraction은 만들지 않는다. |
기존 화면별 fetch 경로 존재, 최소 변경 원칙 |
REFRESH-004, 전체 Goal |
18. 변경 관리
요구사항 변경 시 다음을 확인한다.