docs(mypage): 공지 롤링 요구사항과 검증을 기록한다
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
# 마이 탭 공지사항 롤링 구현 계획
|
||||
|
||||
| 문서 항목 | 내용 |
|
||||
|---|---|
|
||||
| 상태 | 사용자 계약·정상 동작 확인 / 제목 전용 롤링 보완 완료 |
|
||||
| 작성일 | 2026-10-28 |
|
||||
| 요구사항 / API 기준 | `prd.md` / §11 |
|
||||
| 현재 Phase | 1 |
|
||||
| 현재 활성 Goal | 없음 |
|
||||
|
||||
## 목표
|
||||
|
||||
마이 탭에서 최대 3개 공지를 서버 순서로 읽고 현재 공지의 상세 화면에 진입할 수 있다.
|
||||
|
||||
## 현재 상태
|
||||
|
||||
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | 후속 UI 보완 완료 | 4/4 | 없음 | 이번 턴 실제 터치·VoiceOver·Reduce Motion 설정 재검증은 생략 |
|
||||
|
||||
## 범위
|
||||
|
||||
포함: 신규 공개 배열 API, 기존 배너 문구 제거·제목 확장·5초 롤링, 기존 상세 이동, 0/1/2/3 목 회귀, 미사용 구형 클라이언트 API 제거.
|
||||
|
||||
제외: 서버 수정·구형 서버 endpoint 삭제, 홈·공지 목록 동작 변경, 페이지 표시기·스와이프, 공통 인증 변경, 신규 의존성.
|
||||
|
||||
## 기술적 제약
|
||||
|
||||
- iOS 16.6, SwiftUI/Combine/Moya 및 기존 View→ViewModel→Repository→Api 구조.
|
||||
- 기존 `UpdateBannerView`를 수정한다. 테스트 가능한 상태가 필요하면 작은 모델만 `V2/MyPage` 아래 추가한다. 별도 범용 캐러셀은 만들지 않는다.
|
||||
- `NoticeItem`, `ApiResponse`와 상세 본문/date 처리를 재사용한다. 기존 공지 목록이 id 없이 디코딩되는 동작을 깨지 않는다.
|
||||
- 공개 요청에는 Authorization을 보내지 않는다. 기존 목록 요청과 공통 플러그인은 유지한다.
|
||||
- 오류 시 기존 공지를 보존하고, 정상 빈 배열만 숨김 처리한다.
|
||||
- `work/tests`의 실제 소스를 컴파일하는 self-check 패턴을 사용한다. 소스 문자열 검사만으로 동작 통과를 주장하지 않는다.
|
||||
|
||||
## Phase 1
|
||||
|
||||
**Phase 결과:** 공개 최신 공지 배열과 롤링 배너가 연결되고 회귀 검증 근거가 남는다.
|
||||
|
||||
**선행조건:** 사용자 계약, 조사 완료, ulw의 최소 해석 진행 지시.
|
||||
|
||||
### Task 1.1 배열 API와 기존 배너 연결
|
||||
|
||||
**Goal 실행 `P1-T1`:** 0/1/2/3 공지를 올바르게 표시·순환하고 선택 항목을 기존 상세로 전달한다.
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `SodaLive/Sources/Settings/Notice/NoticeApi.swift`, `NoticeRepository.swift`
|
||||
- Modify: `SodaLive/Sources/MyPage/MyPageViewModel.swift`, `MyPageView.swift`
|
||||
- Conditional modify: `SodaLive/Sources/Settings/Notice/GetNoticeResponse.swift` (id를 보존할 경우 기존 payload 호환 유지)
|
||||
- Conditional create: `SodaLive/Sources/V2/MyPage/Models/MyPageNoticeRoller.swift` (실제 UI가 사용하는 최소 상태)
|
||||
- Modify when adding the model: `SodaLive.xcodeproj/project.pbxproj` (두 앱 타깃의 명시적 소스 등록)
|
||||
- Test: `work/tests/MyPageNoticesCheck.swift`, `work/tests/check-my-page-notices.sh`
|
||||
- Modify test fixture: `work/tests/AuthContentSettingsExitCheck.swift`, 필요한 경우 해당 runner
|
||||
|
||||
**Interfaces:** `getLatestNotices()`, `latestNotices: [NoticeItem]`, 기존 `.noticeDetail(notice:)`.
|
||||
|
||||
- [x] **RED:** 실제 API/VM/표시 상태를 사용하는 0/1/2/3 및 nullable envelope·오류·순서·선택 대상 검사를 작성했다.
|
||||
- [x] **RED 확인:** 신규 API 검사가 `FAIL: Latest notices must use public v2 array endpoint; got /notice/latest`와 exit 1로 실패했다.
|
||||
- [x] **GREEN:** v2 공개 GET, 배열 상태, 기존 배너 제목 폭·화살표·5초 세로 롤링을 구현했다. 생명주기 정책의 native 발견 회귀는 아래 P1-R1로 추적한다.
|
||||
- [x] **GREEN 확인:** 같은 검사와 `bash work/tests/check-auth-content-settings-exit.sh`가 exit 0이다.
|
||||
- [x] **REFACTOR:** 신규 상태는 25줄로 제한하고 구형 API 호출 0건을 확인했다. 관련 없는 변경 없음.
|
||||
|
||||
### Task 1.2 검증·리뷰·외부 조건 기록
|
||||
|
||||
**Goal 실행 `P1-GATE`:** 변경·검증·미확인 사항을 근거와 함께 인계한다.
|
||||
|
||||
**Files:** Phase 1 변경 파일, 이 문서, `prd.md`, `reviews/phase1-my-page-notices.md`.
|
||||
|
||||
**TDD 예외 사유:** 검증 및 문서화 Task로 제품 동작을 추가하지 않는다.
|
||||
|
||||
**대체 검증 방법:** 실제 명령 결과, native QA와 리뷰 근거를 기록한다.
|
||||
|
||||
- [x] 모든 `work/tests/check-*.sh` 13개 실행 PASS.
|
||||
- [x] 두 앱 스킴의 iOS/시뮬레이터 빌드 PASS. LSP 도구 미제공을 별도 기록.
|
||||
- [x] native 개수·긴 제목·순환·생명주기 확인, 선택 payload 검사와 실제 탭 미검증의 경계를 명시.
|
||||
- [x] 독립 리뷰의 두 회귀를 신규 P1-R1로 수정하고 재리뷰 APPROVE.
|
||||
- [x] 소유 임시 자원 정리·부재 확인, 검증 및 남은 외부 조건 기록.
|
||||
|
||||
### Task 1.3 리뷰 회귀 수정
|
||||
|
||||
**Goal P1-R1:** 기존 배너 전체 클릭 영역을 복원하고 새 scenePhase에 맞춰 롤링을 즉시 중단·재개한다.
|
||||
|
||||
- Review: `reviews/phase1-my-page-notices.md`, REV-P1-001/002.
|
||||
- Modify: `SodaLive/Sources/MyPage/MyPageView.swift`의 배너만.
|
||||
- Evidence: 기존 `reviews/evidence/native/`는 RED 증거로 유지, 새 검증은 별도 후속 폴더에 기록.
|
||||
- [x] **RED:** 기존 여백의 Button 외부 배치와 native lifecycle 실패를 확인했다.
|
||||
- [x] **RED 확인:** native probe에서 inactive 전달 후 제목 변경, active 복귀 후 7초 이상 미변경을 두 실행에서 재현했다.
|
||||
- [x] **GREEN:** padding을 Button label 내부로 이동하고 새 phase/접근성 값을 명시적 함수 인자로 전달했다.
|
||||
- [x] **GREEN 확인:** 동일 native 하네스로 0/1/2/3·긴 제목·생명주기·OS 복귀를 재실행하여 통과했다. 실제 좌표 탭은 별도 미검증이다.
|
||||
- [x] **REFACTOR:** 관련 없는 변경 없이 최종 빌드·코드 재리뷰 APPROVE와 실제 탭 검증 한계를 기록했다.
|
||||
|
||||
### Task 1.4 화살표 고정·제목 전용 롤링
|
||||
|
||||
**Goal P1-T2:** 배너와 화살표는 고정한 채 제목만 동일한 세로 롤링으로 교체한다.
|
||||
|
||||
- Modify: `SodaLive/Sources/MyPage/MyPageView.swift`의 `UpdateBannerView` 표시 구조만.
|
||||
- 유지: API/상태 모델/5초 타이머/생명주기/전체 배너 클릭 영역/글꼴·색상·여백.
|
||||
- TDD 예외: UI 전환 modifier의 적용 범위 변경이며 신규 비즈니스 로직이 없다. 소스 문자열만 검사하는 테스트를 추가하지 않는다.
|
||||
- 대체 검증: 기존 native 하네스를 재사용해 전환 전·중·후의 화살표 영역 불변 및 제목 변경을 확인하고, 기존 `check-my-page-notices.sh`와 앱 빌드를 실행한다.
|
||||
- [x] 제목에만 identity·transition·clipping을 적용하고 고정된 버튼·화살표의 구조를 유지했다.
|
||||
- [x] native 10프레임의 화살표 변경 픽셀 0, wrap 중간 제목 이동을 확인했다.
|
||||
- [x] 전체 13개 회귀 검사·dev simulator 빌드·비교 검사·리뷰 APPROVE 및 확인 한계를 기록했다.
|
||||
|
||||
### 검증 시나리오 (기존)
|
||||
|
||||
| 시나리오 | 이진 통과 기준 | 증명 표면 |
|
||||
|---|---|---|
|
||||
| S1 개수 | 0은 미표시, 1은 시간이 지나도 같은 항목 | 실제 상태 드라이버 + native UI |
|
||||
| S2 순서/클릭 | 2/3개가 서버 순서로 마지막→처음 반복, 현재 payload가 클릭 대상 | 실제 상태/VM 목 검사 + native UI |
|
||||
| S3 레이아웃 | 자세히 없음, 화살표 있음, 긴 한글 제목과 겹침 없음 | native 렌더링 |
|
||||
| S4 경계/회귀 | message/errorProperty 누락/null 허용, date/HTML 원문, 실패 시 보존, 새 빈 배열 숨김, 기존 목록·홈 미변경 | 실제 디코딩·VM 목 검사 + diff/빌드 |
|
||||
| S5 생명주기 | 화면 이탈·앱 비활성 시 중단, 재진입 시 중복 롤링 없음 | native 실행 + 코드 검토 |
|
||||
|
||||
```bash
|
||||
bash work/tests/check-my-page-notices.sh
|
||||
bash work/tests/check-auth-content-settings-exit.sh
|
||||
for check in work/tests/check-*.sh; do bash "$check"; done
|
||||
xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build
|
||||
```
|
||||
|
||||
**Expected:** 관련 검사·빌드 exit 0, native 동작 확인. 서버 배포/401과 테스트 타깃 부재는 별도 제한으로 남기며 통과로 표시하지 않는다.
|
||||
|
||||
## 실행 순서와 의존성
|
||||
|
||||
`조사 완료 → P1-T1(단일 UI 구현자) → P1-GATE(검증/읽기 전용 리뷰) → 소유 자원 정리`
|
||||
|
||||
## 변경 금지 항목
|
||||
|
||||
테스트 삭제·완화, 구형 API 자동 fallback, 공통 인증 플러그인 수정, 서버 배포/삭제, 기존 사용자 변경 덮어쓰기.
|
||||
|
||||
## Progress
|
||||
|
||||
### 조사 — 2026-10-28
|
||||
|
||||
- 현재 `/notice/latest` 호출자는 마이 탭 한 곳. `ApiResponse`의 공통 nullable 필드는 이미 optional.
|
||||
- `NoticeItem`은 현재 id 없이 title/content/date만 가진다. 기존 목록·상세 호환이 우선이다.
|
||||
- V2 메인과 구형 메인 모두 MyPage를 선택 중일 때만 생성한다. V2 배너의 기존 간격은 5초.
|
||||
- 기준 검사 `bash work/tests/check-auth-content-settings-exit.sh` exit 0. 실제 MyPageViewModel을 컴파일하므로 plural 상태 변경 시 fixture도 갱신해야 한다.
|
||||
- `xcodebuild -version`: Xcode 26.0 (17A324). XCTest 번들 타깃 없음, booted simulator 없음.
|
||||
- `curl --max-time 20 https://test-api.sodalive.net/api/v2/notices/latest` 무인증 요청: HTTP 401. 본문을 저장소에 보관하지 않음.
|
||||
|
||||
### P1-GATE 중간 검증 — 2026-10-28
|
||||
|
||||
- 부모가 `bash work/tests/check-my-page-notices.sh`를 직접 실행, exit 0. 실제 API/Repository/VM/DTO/롤링 상태를 컴파일하고 전송 계층만 격리했다. 0/1/2/3, 서버 순서·반복·선택 payload, HTML/date 원문, 공통 nullable 필드 누락/null, 배열 교체·축소, 오류 보존, main-thread 발행을 확인했다. 실제 물리 탭 검증은 아니다.
|
||||
- `for check in work/tests/check-*.sh; do bash "$check"; done` 전체 13개 스크립트 PASS, 실패 0. 기존 인증 mock의 Debug/Release 심볼 검사 포함.
|
||||
- `xcodebuild -quiet -workspace SodaLive.xcworkspace -scheme SodaLive-dev -configuration Debug -destination 'generic/platform=iOS' -disableAutomaticPackageResolution -skipPackageUpdates CODE_SIGNING_ALLOWED=NO build` exit 0.
|
||||
- 위 명령의 `-scheme SodaLive` 빌드도 exit 0. 서명 없는 iOS 컴파일이며 시뮬레이터/실기기에서 전체 앱을 실행한 증거와 구분한다.
|
||||
- 초기 별도 DerivedData 빌드는 SPM 해석 중 120초 도구 timeout. 기존 workspace 캐시를 재사용한 첫 시도는 구현 중 신규 상태 파일의 타깃 등록 전이라 실패했고, 등록 후 두 스킴 모두 통과했다. 공유 캐시는 삭제하지 않는다.
|
||||
- `git diff --check` exit 0. LSP 진단 도구는 제공되지 않아 실행하지 못했으며 빌드 결과와 구분한다.
|
||||
- 네이티브 배너 하네스 및 독립 리뷰는 진행 중이다.
|
||||
- 추가 검증: `SodaLive-dev`, `-sdk iphonesimulator -destination 'generic/platform=iOS Simulator'`와 기존 캐시/패키지 고정 옵션으로 시뮬레이터 빌드 exit 0.
|
||||
- `rg -n 'getLatestNotice\b|latestNotice\b|/notice/latest' SodaLive/Sources work/tests` 결과 0건. 기존 홈 소스는 변경하지 않았다.
|
||||
- 빌드의 기존 중복 소스 등록·Alamofire/BootpayUI 의존성 경고는 신규 롤러와 관계없어 수정하지 않았다.
|
||||
- 추가 검증: `SodaLive`의 iOS Simulator 빌드도 exit 0. 두 스킴의 iOS/Simulator 컴파일이 모두 통과했다.
|
||||
- 최초 native QA: 개수·순서·wrap·긴 제목·remount·축소 PASS, inactive/active S5 FAIL. 독립 코드 리뷰는 기존 배너 padding 클릭 영역 축소를 확인했다. 완료로 판정하지 않고 P1-R1을 등록했다.
|
||||
|
||||
### P1-R1 후속 검증 — 2026-10-28
|
||||
|
||||
- 변경 원인: onChange의 새 phase를 버리고 이전 환경 값을 캡처한 함수 경로를 사용했다. phase·Reduce Motion을 명시적 함수 인자로 전달하여 새 상태로 판단한다. 좌우24·상하6 여백은 Button label 내부로 옮겨 기존 전체 배너의 contentShape를 복원했다.
|
||||
- 제품 수정은 기존 `UpdateBannerView` 내부에 한정. 최종 MyPageView SHA-256: `7f8ac15573ab4c9fe01508431297e40d2e95a22ad37585859f03224475749d02`.
|
||||
- 새 [native 증거](reviews/evidence/native-fix/README.md): 기존 RED 하네스를 그대로 사용한 새 36 PNG. inactive에서 둘째 유지, active 복귀 후 셋째 진행, OS 복귀 후 진행을 확인했다. 정적 긴 제목 캡처는 RED와 동일해 geometry/style이 유지됐다.
|
||||
- 부모가 후속 lifecycle/긴 제목 PNG를 직접 열어 확인했다. 별도 실제 상태 helper+추출 callback 드라이버를 직접 컴파일·실행하여 title/content/date payload 동일성 10건과 빈 상태 검사를 확인했다. 물리 탭/전체 상세 이동 검증으로 간주하지 않는다.
|
||||
- 수정 후 전체 `work/tests/check-*.sh` 13개 재실행 PASS, 실패 0.
|
||||
- 수정 후 `SodaLive` iOS 및 iOS Simulator 빌드 exit 0. 연속 빌드 실행이 총 600초 timeout으로 dev 단계에서 중단됐으나, `SodaLive-dev` iOS와 Simulator를 각각 재실행해 모두 exit 0. 최종 두 스킴·두 플랫폼의 컴파일이 확인됐다.
|
||||
- 수정 후 `git diff --check` exit 0. LSP 미제공, 실제 좌표 탭·VoiceOver·Reduce Motion 설정 변경·전체 앱/실서버 연동은 미검증으로 유지한다.
|
||||
- 부모 작업 TMP `my-notice-ulw.TGNyMy`와 durable note를 명령 결과 기록 후 삭제하고 `test ! -e`로 부재를 확인했다. 공유 Xcode 캐시는 보존했다. native-fix의 별도 소유 자원 정리 영수증은 최종 검토 시 확인한다.
|
||||
- 최종 Oracle 재리뷰 APPROVE, native 하네스 범위 시각 PASS. 두 회귀 해결과 실제 탭 미검증은 `reviews/phase1-my-page-notices.md`에 누적했다.
|
||||
- QA worker 연결 실패로 fallback이 동일 코드로 추가 재실행했다. 정리 영수증 확인 후 부모가 잔여 첫 worker TMP를 제거했고, `pgrep` 프로세스 부재·`simctl` booted device 없음·`test ! -e` 소유 TMP 4개 부재를 확인했다.
|
||||
- 최종 남은 조건: 테스트 서버 공개 GET의 401 해소 및 정상 실제 응답 검증, 실제 앱에서 공지/배너 여백 탭→해당 상세 이동. 서버 endpoint 삭제는 이전 앱·타 클라이언트 사용 여부 확인 후 서버 담당이 판단한다. 코드·로컬 검증 완료를 출시/전체 E2E 완료로 표시하지 않는다.
|
||||
|
||||
### P1-T2 사용자 확인 및 후속 보완 — 2026-10-28
|
||||
|
||||
- 사용자가 계약·정상 동작·롤링 애니메이션 정상 동작을 확인했다. 최초 401 측정은 과거 기록으로 유지하되 현재 미해결 계약으로 다시 보고하지 않는다. 환경별 응답을 에이전트가 재호출한 것은 아니다.
|
||||
- 추가 요청은 화살표를 고정하고 제목만 롤링하는 것으로 한정한다. 기존 표시/클릭/타이머/데이터 계약은 유지한다.
|
||||
- 변경 전 기준 `bash work/tests/check-my-page-notices.sh` exit 0. UI-only 전환 범위 변경이므로 신규 소스 문자열 테스트 대신 실제 native 전환 프레임으로 검증한다.
|
||||
- 후속 구현은 기존 Button의 `.id`/`.transition`을 제목 Text로 이동하고 제목만 ZStack에서 clip하는 변경이다. 화살표·버튼·padding·타이머·API는 유지했다.
|
||||
- 부모 실행: 전체 `work/tests/check-*.sh` 13개 PASS, 실패 0. `SodaLive-dev` Debug, generic iOS Simulator, no-sign 빌드 exit 0 (`-disableAutomaticPackageResolution -skipPackageUpdates`). `git diff --check` exit 0.
|
||||
- 이번 변경 소스 SHA-256: `af77569942c0bbdbad0242431362b5987dc278d8f70d015ccc1ef675b2b264ed`. native 전환 프레임 검증 및 범위 한정 리뷰 진행 중.
|
||||
- 최종 native 증거: [title-only](reviews/evidence/title-only/README.md). 부모가 비교판을 직접 열고 `python3 docs/20261028_마이_탭_공지사항_롤링/reviews/evidence/title-only/compare.py`를 재실행해 exit 0을 확인했다. 10장 화살표 변경 픽셀 0, wrap rest→mid 3,857px 및 mid→end 3,796px 제목 변경, 단일 공지 고정·긴 제목 비겹침·배너 밖 제품 소스 불변을 확인했다.
|
||||
- 독립 범위 한정 리뷰 APPROVE. reviewer도 실제 비교판을 열어 제목만 전환됨을 확인했다. 실제 좌표 탭·VoiceOver·Reduce Motion 런타임은 이번 턴 재검증하지 않았으며 기존 코드 경로를 유지했다. LSP 도구는 미제공이다.
|
||||
- native 소유 프로세스·device·TMP 부재 영수증은 `reviews/evidence/title-only/cleanup.json`에 보존했다. 부모 임시 루트·노트도 제거하고 `test ! -e`로 두 소유 TMP 루트의 부재를 확인했다. 커밋·서버 호출은 하지 않았다.
|
||||
|
||||
## Decision Log
|
||||
|
||||
| 날짜 | ID | 상태 | 결정 | 근거 |
|
||||
|---|---|---|---|---|
|
||||
| 2026-10-28 | PLAN-001 | 확정 | 계획 에이전트 제안에서 별도 캐러셀 UI 생성을 생략하고 기존 배너 수정으로 축소 | 요청의 기존 UI 재사용, 최소 변경 |
|
||||
| 2026-10-28 | PLAN-002 | 확정 | 사용자 제공 envelope를 그대로 사용, 5초 간격·세로 전환·기존 오류 보존 | PRD DEC-001/002 및 사용자 명시 계약 |
|
||||
| 2026-10-28 | PLAN-003 | 확정 | 사용자가 계약·정상 동작·롤링 정상 확인. 추가 변경은 화살표 고정과 제목 전환 범위로 한정 | PRD DEC-005, P1-T2 |
|
||||
|
||||
## 발견된 문제
|
||||
|
||||
| ID | 심각도 | 상태 | 발견 내용 | 처리 계획 |
|
||||
|---|---|---|---|---|
|
||||
| EXT-001 | High | 사용자 확인으로 해소 | 최초 테스트 서버 호출은 HTTP 401이었으나 후속 메시지에서 계약·정상 동작 확인 | 과거 측정 기록 보존. 에이전트가 이번 턴 실서버 재측정했다고 주장하지 않음 |
|
||||
@@ -0,0 +1,126 @@
|
||||
# 마이 탭 최신 공지사항 롤링
|
||||
|
||||
## 문서 정보
|
||||
|
||||
| 항목 | 내용 |
|
||||
|---|---|
|
||||
| 문서 상태 | 사용자 계약·정상 동작 확인 / 제목 전용 롤링 보완 완료 |
|
||||
| 작성일 / 최종 수정일 | 2026-10-28 |
|
||||
| 대상 제품 | SodaLive iOS 메인 마이 탭 상단 공지 |
|
||||
| 작성자·결정권자 | 에이전트 / 사용자 |
|
||||
| 관련 API Contract | 이 문서 §11, 사용자 제공 계약 |
|
||||
| 관련 구현 계획 | `plan-task.md` |
|
||||
| 관련 review | `reviews/phase1-my-page-notices.md` |
|
||||
|
||||
## 1. Overview
|
||||
|
||||
마이 탭 상단에서 서버가 반환한 최신 공지 최대 3개를 순서대로 보여준다. 기존 상세 화면과 본문 표시를 재사용하고 제목을 읽을 수 있는 폭을 넓힌다.
|
||||
|
||||
## 2. Problem Statement
|
||||
|
||||
현재 단일 공지만 표시하며 우측 `자세히` 문구가 제목 폭을 차지한다. 배열 응답에 맞게 상태와 표시를 변경해야 한다.
|
||||
|
||||
## 3. Goals
|
||||
|
||||
- 0개는 숨김, 1개는 고정, 2~3개는 자동 순환한다.
|
||||
- 현재 보이는 공지를 누르면 해당 제목·본문·날짜의 기존 상세 화면으로 이동한다.
|
||||
- 제목·화살표 외 별도 표시나 조작 UI를 추가하지 않는다.
|
||||
|
||||
## 4. Non-Goals
|
||||
|
||||
- 서버 구현·배포·서버 구형 endpoint 삭제, 홈 응답/화면 변경.
|
||||
- 페이지네이션, 개수 파라미터, 수동 스와이프·페이지 표시기, 주기적 API 재조회.
|
||||
- 공통 인증 플러그인 변경, 새 의존성, 관련 없는 리팩터링.
|
||||
|
||||
## 5. Target Users and Permissions
|
||||
|
||||
로그인 여부와 무관하게 마이 탭을 방문하는 iOS 사용자. 신규 GET은 인증 불필요이며 본문·query parameter가 없다.
|
||||
|
||||
## 6. 핵심 사용자 흐름
|
||||
|
||||
1. 마이 탭 진입 시 최신 공지 배열을 조회한다.
|
||||
2. 첫 항목부터 표시하고 2개 이상이면 5초마다 다음 항목으로 순환한다.
|
||||
3. 공지를 누르면 기존 `.noticeDetail(notice:)`로 이동한다.
|
||||
4. 화면 이탈·앱 비활성화 시 롤링을 중지한다. 데이터 변경 시 첫 항목으로 초기화한다.
|
||||
|
||||
## 7. 정보 구조와 라우팅
|
||||
|
||||
`MainView(.my) → MyPageView → UpdateBannerView → AppStep.noticeDetail → NoticeDetailView`를 유지한다.
|
||||
|
||||
## 8. 기능 요구사항
|
||||
|
||||
| ID | 상태 | 요구사항 | 수용 기준 | Goal |
|
||||
|---|---|---|---|---|
|
||||
| NOTICE-001 | 확정 | 신규 v2 배열 조회 | 정확한 GET 경로, body/query 없음, 배열 순서 유지 | P1-T1 |
|
||||
| NOTICE-002 | 확정 | 개수별 표시 | 0 숨김, 1 고정, 2/3 순환과 마지막→첫 항목 | P1-T1 |
|
||||
| NOTICE-003 | 확정 | 제목 폭 확보 | 자세히 제거, 화살표 유지, 긴 제목 겹침 없음 | P1-T1 |
|
||||
| NOTICE-004 | 확정 | 기존 상세 재사용 | 각 항목의 전체 본문과 date 문자열 그대로 전달 | P1-T1 |
|
||||
| NOTICE-005 | 확정 | 기존 상태·인접 기능 보존 | 실패 시 기존 공지 유지, 빈 배열은 숨김, 목록·홈 변경 없음 | P1-GATE |
|
||||
| NOTICE-006 | 확정 | 구형 호출 제거 | 미사용 `/notice/latest` 클라이언트 API 제거 | P1-T1 |
|
||||
|
||||
## 9. 반응형 기능 범위
|
||||
|
||||
기존 iPhone 화면 폭과 글꼴·색상·여백을 유지한다. 긴 제목은 화살표와 겹치지 않게 표시한다.
|
||||
|
||||
## 10. UI/UX Expectations
|
||||
|
||||
- 기존 배너 스타일을 재사용하며 우측 문구가 차지하던 폭을 제목에 제공한다.
|
||||
- 5초 간격은 기존 `V2/Component/Banner/BannerCarousel.swift`의 관례를 따른 구현 선택이다.
|
||||
- 짧은 세로 전환으로 순환하며 동작 줄이기 설정에서는 위치 이동을 피한다.
|
||||
- 화살표와 배너 버튼은 고정하고 제목만 세로 전환한다. 기존 간격·상세 이동·클릭 영역은 유지한다.
|
||||
- 기존 로딩·오류 정책을 유지한다. 오류를 빈 배열로 오인하지 않는다.
|
||||
|
||||
## 11. API 계약
|
||||
|
||||
| 항목 | 계약 |
|
||||
|---|---|
|
||||
| 요청 | `GET /api/v2/notices/latest`, body/query 없음, 비로그인 허용 |
|
||||
| 성공 | HTTP 200, `success=true`, `data: [공지]` |
|
||||
| 개수 / 순서 | 활성 공지 id 내림차순 최대 3개, 클라이언트 재정렬 없음 |
|
||||
| 공지 필드 | `id`: Long 정수, `title`: String, `content`: 전체 본문 String, `date`: `yyyy-MM-dd` String |
|
||||
| 날짜 | Asia/Seoul 날짜 문자열을 그대로 전달, 시간대 변환 없음 |
|
||||
| 공통 필드 | `message`, `errorProperty`는 nullable이며 JSON 포함 여부에 의존하지 않음 |
|
||||
| 빈 결과 | `data=[]` |
|
||||
| 유지 | 기존 `/notice` 목록, 기존 홈의 `latestNotice` 계약 |
|
||||
|
||||
## 12. 보안과 데이터 취급
|
||||
|
||||
공개 최신 공지 요청에 인증을 요구하지 않는다. 토큰·실서비스 본문을 문서나 목 데이터에 기록하지 않는다. 기존 HTML 표시 방식을 변경하지 않는다.
|
||||
|
||||
## 13. 성능과 품질 요구사항
|
||||
|
||||
iOS 16.6 이상과 기존 SwiftUI/Combine/Moya 구조를 유지한다. 테스트는 기존 `work/tests`의 독립 Swift 드라이버 패턴을 따른다. 앱 소스에 테스트 전용 mock 또는 자동 fallback을 추가하지 않는다.
|
||||
|
||||
## 14. 성공 기준
|
||||
|
||||
- [x] 목 응답 0/1/2/3개와 순환·선택 payload·전체 본문·날짜를 검증했다. 실제 물리 탭은 별도 미검증이다.
|
||||
- [x] 공통 nullable 필드 누락/null, 오류 및 배열 축소를 검증했다.
|
||||
- [x] 제목 확장·화살표 유지·생명주기를 실제 소스 기반 native 하네스에서 확인했다.
|
||||
- [x] 두 스킴 iOS/Simulator 빌드·전체 13개 회귀 검사·리뷰 APPROVE를 계획 문서에 기록했다.
|
||||
|
||||
## 15. Open Questions
|
||||
|
||||
제품 구현을 막는 미결 사항 없음. 다음 외부 사항은 실서버 완료로 간주하지 않는다.
|
||||
|
||||
| ID | 상태 | 확인 사항 | 담당 / 시점 |
|
||||
|---|---|---|---|
|
||||
| EXT-001 | 사용자 확인 완료 | 사용자가 계약 및 정상 동작 확인을 전달함. 환경별 세부 결과를 에이전트가 별도 재측정한 것은 아님 | 2026-10-28 후속 사용자 메시지 |
|
||||
| EXT-002 | 외부 의존 | 구형 서버 API 삭제는 다른 앱·배포 버전의 사용 여부까지 확인 필요 | 서버 담당 / endpoint 제거 전 |
|
||||
|
||||
## 16. 요구사항 추적표
|
||||
|
||||
NOTICE-001~006 → §11 → `plan-task.md` Phase 1 → 목 드라이버 / 네이티브 QA / 리뷰.
|
||||
|
||||
## 17. Decision Log
|
||||
|
||||
| 날짜 | ID | 상태 | 결정 | 근거 |
|
||||
|---|---|---|---|---|
|
||||
| 2026-10-28 | DEC-001 | 확정 | 요청 범위만 구현하고 질문 대신 조사 결과와 최소 해석으로 진행 | 사용자 ulw 지시 |
|
||||
| 2026-10-28 | DEC-002 | 확정 | 5초 간격, 화면·앱 활성 시만 롤링 | 기존 V2 배너 간격 및 생명주기 |
|
||||
| 2026-10-28 | DEC-003 | 확정 | 구형 API 제거는 이 저장소의 미사용 클라이언트 선언에 한정 | 서버/이전 앱 사용 여부는 이 저장소에서 보증 불가 |
|
||||
| 2026-10-28 | DEC-004 | 확정 | 제공 계약으로 클라이언트 구현하되 실서버 통합은 미완료로 구분 | 테스트 서버 신규 경로 무인증 GET 결과 HTTP 401, 자동 구형 fallback 금지 |
|
||||
| 2026-10-28 | DEC-005 | 정정 | 기존 서버 확인 대기는 사용자 확인으로 해소. 롤링 정상 동작 확인 후 화살표 고정·제목만 롤링하도록 보완 | 사용자 후속 요청, P1-T2 |
|
||||
|
||||
## 18. 변경 관리
|
||||
|
||||
후속 결정·검증은 기존 기록을 유지하며 이 PRD와 `plan-task.md`에 누적한다.
|
||||
Reference in New Issue
Block a user