Files
sodalive-ios/docs/20261028_마이_탭_공지사항_롤링/plan-task.md
T

17 KiB

마이 탭 공지사항 롤링 구현 계획

문서 항목 내용
상태 사용자 계약·정상 동작 확인 / 제목 전용 롤링 보완 완료
작성일 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:).

  • RED: 실제 API/VM/표시 상태를 사용하는 0/1/2/3 및 nullable envelope·오류·순서·선택 대상 검사를 작성했다.
  • RED 확인: 신규 API 검사가 FAIL: Latest notices must use public v2 array endpoint; got /notice/latest와 exit 1로 실패했다.
  • GREEN: v2 공개 GET, 배열 상태, 기존 배너 제목 폭·화살표·5초 세로 롤링을 구현했다. 생명주기 정책의 native 발견 회귀는 아래 P1-R1로 추적한다.
  • GREEN 확인: 같은 검사와 bash work/tests/check-auth-content-settings-exit.sh가 exit 0이다.
  • 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와 리뷰 근거를 기록한다.

  • 모든 work/tests/check-*.sh 13개 실행 PASS.
  • 두 앱 스킴의 iOS/시뮬레이터 빌드 PASS. LSP 도구 미제공을 별도 기록.
  • native 개수·긴 제목·순환·생명주기 확인, 선택 payload 검사와 실제 탭 미검증의 경계를 명시.
  • 독립 리뷰의 두 회귀를 신규 P1-R1로 수정하고 재리뷰 APPROVE.
  • 소유 임시 자원 정리·부재 확인, 검증 및 남은 외부 조건 기록.

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 증거로 유지, 새 검증은 별도 후속 폴더에 기록.
  • RED: 기존 여백의 Button 외부 배치와 native lifecycle 실패를 확인했다.
  • RED 확인: native probe에서 inactive 전달 후 제목 변경, active 복귀 후 7초 이상 미변경을 두 실행에서 재현했다.
  • GREEN: padding을 Button label 내부로 이동하고 새 phase/접근성 값을 명시적 함수 인자로 전달했다.
  • GREEN 확인: 동일 native 하네스로 0/1/2/3·긴 제목·생명주기·OS 복귀를 재실행하여 통과했다. 실제 좌표 탭은 별도 미검증이다.
  • 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와 앱 빌드를 실행한다.
  • 제목에만 identity·transition·clipping을 적용하고 고정된 버튼·화살표의 구조를 유지했다.
  • native 10프레임의 화살표 변경 픽셀 0, wrap 중간 제목 이동을 확인했다.
  • 전체 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 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 증거: 기존 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. 부모가 비교판을 직접 열고 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이었으나 후속 메시지에서 계약·정상 동작 확인 과거 측정 기록 보존. 에이전트가 이번 턴 실서버 재측정했다고 주장하지 않음