docs(main): V2 직접 진입 문서를 정리한다

This commit is contained in:
2026-09-18 14:49:00 +09:00
parent cc8f271f44
commit c261b32148
3 changed files with 312 additions and 0 deletions
@@ -0,0 +1,184 @@
# 레거시 메인 직접 진입 V2 전환 구현 계획
> **For agentic workers:** 구현은 TDD 순서로 수행하고 각 검증 결과를 이 문서에 누적한다.
| 문서 항목 | 내용 |
|---|---|
| 상태 | 구현 중 |
| 작성일 | 2026-09-18 |
| 요구사항 기준 | `docs/20260918_레거시_메인_직접_진입_v2_전환/prd.md` |
| API 기준 | 해당 없음 |
| 현재 Phase | Phase 3 예약 status package 경로 정합성 |
| 현재 활성 Goal | `P3-T1` |
## 목표
레거시 `MainActivity`를 직접 여는 두 사용자 흐름이 기존 부가 동작을 유지하면서 `MainV2Activity`로 진입한다.
## 범위
### 포함
- `LiveRoomActivity` 딥링크 confirm Intent target 교체
- `LiveReservationCancelActivity` 다른 수다 예약 Intent target 교체
- source regression test와 focused build 검증
### 제외
- 레거시 Fragment 및 `MainActivity` 내부 변경
- 라우팅 공통화 또는 리팩터링
- 기기·에뮬레이터 UI 테스트
## 기술적 제약
- Kotlin/Android 기존 Intent 패턴을 유지한다.
- 신규 의존성이나 추상화를 추가하지 않는다.
- `app/src/test` local unit source test만 추가한다.
## Phase 1
**Phase 결과:** 두 직접 진입 경로가 V2 메인을 사용한다.
### Task 1.1 직접 진입 target 전환
**Goal 실행 `P1-T1`:** 두 경로의 target만 `MainV2Activity`로 최소 교체한다.
**Files:**
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/live/room/LiveRoomActivity.kt`
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/live/reservation/status/LiveReservationCancelActivity.kt`
- Create: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2DirectEntrySourceTest.kt`
- [x] **RED:** 두 production 파일의 V2 target과 레거시 target 부재를 검증하는 source test를 작성한다.
- [x] **RED 확인:** `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2DirectEntrySourceTest" --max-workers=1`이 기존 `MainActivity` target 때문에 실패하는지 확인한다.
- [x] **GREEN:** 두 파일의 import와 Intent target만 `MainV2Activity`로 교체한다.
- [x] **GREEN 확인:** 같은 focused test가 성공하는지 확인한다.
- [x] **REFACTOR:** 추가 리팩터링 없이 관련 source test, compile, lint를 실행한다.
### 시나리오 계약
| 시나리오 | 통과 조건 | 검증 surface | Test ID |
|---|---|---|---|
| 라이브 푸시 confirm | `MainV2Activity` target이며 bundle과 두 flag가 유지된다. | source test | `라이브룸 딥링크 confirm은 MainV2Activity로 이동한다` |
| 예약 취소 다른 수다 예약 | `finishAffinity()` 후 `MainV2Activity`를 시작한다. | source test | `예약 취소의 다른 수다 예약은 MainV2Activity로 이동한다` |
| 인접 회귀 | 대상 두 파일에 레거시 `MainActivity` import/target이 없다. | source scan | 위 두 test의 부재 assertion |
### Phase 1 Gate
```bash
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2DirectEntrySourceTest" --max-workers=1
./gradlew :app:compileDebugKotlin --max-workers=1
./gradlew :app:ktlintCheck --max-workers=1
git diff --check
```
**Expected:** 모든 명령 exit code 0, 두 경로에서 `MainActivity::class.java` 직접 진입 0건.
## Phase 1 Verification Log
- 2026-09-18: 구현 전 계획 확정. 검증 결과는 Task 진행에 따라 누적한다.
- 2026-09-18: RED에서 `MainV2DirectEntrySourceTest` 2개가 각각 `MainV2Activity` import 부재 assertion으로 실패함을 확인했다.
- 2026-09-18: GREEN에서 같은 focused test 2개가 성공했다. 두 대상 파일의 레거시 `MainActivity` import/Intent 검색 결과는 0건이며 `git diff --check`도 성공했다.
- 2026-09-18: `:app:compileDebugKotlin`은 성공했다. 전체 `:app:testDebugUnitTest`는 1,263개 중 이번 변경과 무관한 `ChatMainFragmentLayoutTest:71`, `ChatMainTitleBarActionSourceTest:16` 2개가 실패했다.
- 2026-09-18: `:app:ktlintCheck`는 기존 `live.reservation_status` package underscore 규칙 위반으로 실패했다. 후속 요청에 따라 package 선언과 manifest/import 참조를 `live.reservation.status`로 변경했다.
- 2026-09-18: Oracle 리뷰 결과 차단 사항 없이 승인됐다. 호출 순서 assertion 보강은 비차단 참고 사항으로 남겼다.
- 2026-09-18: 후속 ktlint 수정 RED에서 `LiveReservationPackageSourceTest` 2개가 기존 `reservation_status` package/import/manifest 참조 때문에 실패했다. 이후 package 선언과 참조를 `live.reservation.status`로 교체했고, `LiveReservationPackageSourceTest`, `MainV2DirectEntrySourceTest`, `:app:compileDebugKotlin`, `:app:ktlintCheck`, production/manifest underscore package 참조 검색이 모두 성공했다.
## Phase 2
**Phase 결과:** foreground 라이브룸에서 다른 라이브룸으로 이동할 때 V2 메인이 로딩을 표시하며 1초 뒤 입장을 시작한다.
### Task 2.1 foreground 라이브룸 딥링크 지연 처리
**Goal 실행 `P2-T1`:** `SINGLE_TOP`을 유지하면서 대상 라이브룸 딥링크에만 기존 deferred loading 흐름을 적용한다.
**Files:**
- Modify: `app/src/main/java/kr/co/vividnext/sodalive/v2/main/MainV2Activity.kt`
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2ActivitySourceTest.kt`
- [x] **RED:** foreground live route 선택적 지연, loading 인계, 종료 callback 제거를 검증하는 source test를 추가한다.
- [x] **RED 확인:** `./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2ActivitySourceTest" --max-workers=1`이 새 동작 부재로 실패하는지 확인한다.
- [x] **GREEN:** 기존 `routeLoadingDialog`, `isDeferredRouteLoading`, `isLiveEntryLoading`을 재사용해 `onNewIntent()` 대상 경로만 1초 지연하고 종료 시 callback을 제거한다.
- [x] **GREEN 확인:** 같은 focused test와 직접 영향 회귀 테스트가 성공하는지 확인한다.
- [x] **REFACTOR:** 신규 추상화 없이 중복만 정리하고 compile, ktlint, diff 검증 결과를 기록한다.
### Task 2.R1 비라이브 ID fallback 판별 회귀 수정
**Goal 실행 `P2-R1`:** `REV-P2-001`에서 확인된 route 판별과 실제 실행의 ID fallback 차이를 제거한다.
- **시작 조건:** `REV-P2-001` 확정.
- **대상 파일:** `MainV2Activity.kt`, `MainV2ActivitySourceTest.kt`
- **수정 범위:** 문자열 ID가 숫자로 파싱되면 그 값의 양수 여부를 사용하고, 파싱되지 않을 때만 Long extra로 fallback한다.
- **회귀 테스트:** 문자열 ID `0`/음수와 양수 Long extra가 함께 있어도 실제 router와 동일하게 live fallback을 선택하는 source contract.
- **완료 증거:** RED assertion 실패, focused GREEN, Phase 2 Gate 재실행, review 재검토.
- [x] **RED:** `hasPositiveId()`가 `executeBundleRoute()`와 동일한 Elvis fallback 순서를 사용하는지 검증한다.
- [x] **RED 확인:** focused test에서 기존 OR 판별 때문에 assertion이 실패하는지 확인한다.
- [x] **GREEN:** `hasPositiveId()` 값 선택 순서만 최소 수정한다.
- [x] **GREEN 확인:** focused test와 Phase 2 Gate를 다시 실행한다.
- [x] **REFACTOR:** 추가 변경 없이 리뷰 보고서와 Verification Log에 증거를 누적한다.
### 시나리오 계약
| 시나리오 | 통과 조건 | 검증 surface | Test ID |
|---|---|---|---|
| foreground 라이브 이동 | `LiveRoomActivity.isForeground`이고 실제 live route일 때 loading을 표시하고 1000ms 뒤 처리한다. | source test | `MainV2Activity는 foreground 라이브 딥링크만 로딩과 함께 지연한다` |
| 종료 경계 | Activity 종료 시 pending route callback과 loading dialog가 남지 않는다. | source test | `MainV2Activity는 종료할 때 지연 딥링크와 로딩을 정리한다` |
| 인접 회귀 | 채팅·오디오·일반 딥링크는 즉시 처리되고 direct entry의 `CLEAR_TOP | SINGLE_TOP`이 유지된다. | source test | 기존 audio/direct-entry test 및 선택적 지연 test |
### Phase 2 Gate
```bash
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.v2.main.MainV2ActivitySourceTest" --tests "kr.co.vividnext.sodalive.v2.main.MainV2DirectEntrySourceTest" --tests "kr.co.vividnext.sodalive.v2.main.MainV2AudioNotificationRouteSourceTest" --tests "kr.co.vividnext.sodalive.v2.live.action.*" --max-workers=1
./gradlew :app:compileDebugKotlin --max-workers=1
./gradlew :app:ktlintCheck --max-workers=1
git diff --check
```
**Expected:** 모든 명령 exit code 0, foreground live route만 1초 지연, 기존 direct-entry flags 유지.
## Phase 3
**Phase 결과:** `live.reservation.status` package 선언과 물리 디렉터리 구조가 일치한다.
### Task 3.1 예약 status package 경로 이동
**Goal 실행 `P3-T1`:** 기존 package 선언을 유지하며 대상 Kotlin 파일 6개를 `live/reservation/status/`로 이동한다.
**Files:**
- Move: `app/src/main/java/kr/co/vividnext/sodalive/live/reservation_status/*.kt`
- To: `app/src/main/java/kr/co/vividnext/sodalive/live/reservation/status/*.kt`
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/live/LiveReservationPackageSourceTest.kt`
- Modify: `app/src/test/java/kr/co/vividnext/sodalive/v2/main/MainV2DirectEntrySourceTest.kt`
- [x] **RED:** 새 디렉터리에 6개 파일이 존재하고 기존 디렉터리가 없음을 검증하는 source test를 추가한다.
- [x] **RED 확인:** focused test가 새 경로 부재 assertion으로 실패하는지 확인한다.
- [x] **GREEN:** Kotlin 파일을 새 경로로 이동하고 source test의 파일 경로를 갱신한다.
- [x] **GREEN 확인:** package source test와 direct-entry source test가 성공하는지 확인한다.
- [x] **REFACTOR:** resource 이름은 변경하지 않고 compile, ktlint, diff check 결과를 기록한다.
### Phase 3 Gate
```bash
./gradlew :app:testDebugUnitTest --tests "kr.co.vividnext.sodalive.live.LiveReservationPackageSourceTest" --tests "kr.co.vividnext.sodalive.v2.main.MainV2DirectEntrySourceTest" --max-workers=1
./gradlew :app:compileDebugKotlin --max-workers=1
./gradlew :app:ktlintCheck --max-workers=1
git diff --check
```
**Expected:** 대상 6개 파일은 `live/reservation/status/`에만 존재하고 모든 명령 exit code 0.
## Verification Log
- 2026-09-18: Phase 2 RED에서 신규 선택적 지연·종료 정리 test 2개가 기존 즉시 처리 때문에 실패했고 기존 test 12개는 통과했다.
- 2026-09-18: Phase 2 GREEN에서 `MainV2ActivitySourceTest` 14개와 direct-entry/audio/Live Action focused 회귀가 성공했다. `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`도 성공했다.
- 2026-09-18: 전체 `:app:testDebugUnitTest`는 1,267개 중 기존 `ChatMainFragmentLayoutTest:71`, `ChatMainTitleBarActionSourceTest:16` 2개만 실패했다.
- 2026-09-18: 5개 병렬 리뷰에서 보안·자동 QA·맥락 검토는 통과했고, 목표·코드 품질 리뷰가 `REV-P2-001` fallback 판별 불일치를 확정했다.
- 2026-09-18: `P2-R1` RED에서 `MainV2ActivitySourceTest` 15개 중 신규 fallback test 1개만 실패했다. 최소 수정 후 같은 test와 Phase 2 focused 회귀, compile, ktlint, diff check가 성공했다.
- 2026-09-18: 수정 후 전체 `:app:testDebugUnitTest`는 1,268개 중 동일한 기존 채팅 source test 2개만 실패했다. 목표·코드 품질 Oracle 재검토는 모두 `REV-P2-001` 해결 및 신규 blocker 없음으로 승인했다.
- 2026-09-18: Phase 3 RED에서 `LiveReservationPackageSourceTest` 3개 중 신규 경로 정합성 test 1개가 새 디렉터리 부재로 실패했다.
- 2026-09-18: Phase 3 GREEN에서 package/direct-entry source test 5개, `:app:compileDebugKotlin`, `:app:ktlintCheck`, `git diff --check`가 성공했다. 대상 Kotlin 파일 6개는 `live/reservation/status/`에만 존재하고 resource 변경은 없다.
- 2026-09-18: 전체 `:app:testDebugUnitTest`는 1,269개 중 기존 `ChatMainFragmentLayoutTest:71`, `ChatMainTitleBarActionSourceTest:16` 2개만 실패했다.
- 2026-09-18: Phase 3 목표·자동 QA·코드 품질·보안·맥락 검토 5개가 모두 통과했으며 확정 발견 사항은 없다.
@@ -0,0 +1,75 @@
# 레거시 메인 직접 진입 V2 전환 PRD
## 문서 정보
| 항목 | 내용 |
|---|---|
| 문서 상태 | 구현 완료 |
| 작성일 | 2026-09-18 |
| 최종 수정일 | 2026-09-18 |
| 대상 제품 | Android SodaLive 메인 라우팅 |
| 관련 구현 계획 | `docs/20260918_레거시_메인_직접_진입_v2_전환/plan-task.md` |
| 관련 review | `docs/20260918_레거시_메인_직접_진입_v2_전환/reviews/phase1-main-v2-direct-entry-review.md`<br>`docs/20260918_레거시_메인_직접_진입_v2_전환/reviews/phase2-live-deeplink-deferral-review.md`<br>`docs/20260918_레거시_메인_직접_진입_v2_전환/reviews/phase3-reservation-status-package-path-review.md` |
## 1. Overview
레거시 `MainActivity`를 직접 여는 두 사용자 흐름을 `MainV2Activity`로 전환한다. 기존 딥링크 데이터와 Activity stack 정리 동작은 유지한다.
## 2. Problem Statement
- 라이브룸에서 다른 라이브 푸시 이동을 확인하면 레거시 `MainActivity`를 경유한다.
- 라이브 예약 취소 화면의 "다른 수다 예약" 동작도 task를 비운 뒤 레거시 `MainActivity`를 연다.
- 이 때문에 후속 화면 종료 시 현재 제품 기준인 V2 메인이 아닌 레거시 메인이 표시될 수 있다.
## 3. Goals
- 두 직접 진입 경로가 `MainV2Activity`를 연다.
- 라이브룸 딥링크 경로의 `Constants.EXTRA_DATA`, `FLAG_ACTIVITY_CLEAR_TOP`, `FLAG_ACTIVITY_SINGLE_TOP`을 유지한다.
- 예약 취소 경로의 `finishAffinity()` 동작을 유지한다.
- 라이브룸이 foreground인 상태에서 V2 메인이 받은 라이브룸 딥링크만 1초 지연하고, 대기 및 입장 처리 중 기존 로딩 다이얼로그를 표시한다.
- `live.reservation.status` package 파일의 물리 디렉터리를 package 경로와 일치시킨다.
## 4. Non-Goals
- 레거시 `MainActivity` 내부 기능 또는 Fragment를 수정하지 않는다.
- 다른 Activity/Fragment의 내비게이션 구조를 리팩터링하지 않는다.
- 새로운 라우팅 추상화나 의존성을 추가하지 않는다.
## 5. 핵심 사용자 흐름
1. 라이브룸에서 다른 라이브 푸시를 누르고 이동을 확인하면 V2 메인이 딥링크 bundle을 처리해 대상 라이브로 이동한다.
2. 이동한 라이브룸에서 나가면 V2 메인이 표시된다.
3. 라이브 예약 취소 화면에서 "다른 수다 예약"을 누르면 기존 task를 정리하고 V2 메인이 표시된다.
## 6. 기능 요구사항
| ID | 상태 | 요구사항 | 수용 기준 | 계획 연결 |
|---|---|---|---|---|
| `ROUTE-001` | 확정 | 라이브룸 딥링크 확인 경로는 `MainV2Activity`를 사용한다. | target이 `MainV2Activity`이고 기존 bundle/flags가 유지된다. | `P1-T1` |
| `ROUTE-002` | 확정 | 예약 취소의 다른 수다 예약 경로는 `MainV2Activity`를 사용한다. | target이 `MainV2Activity`이고 `finishAffinity()`가 유지된다. | `P1-T1` |
| `ROUTE-003` | 확정 | 대상 두 파일에서 레거시 `MainActivity` 직접 참조를 제거한다. | source test에서 import와 Intent target 부재를 확인한다. | `P1-T1` |
| `ROUTE-004` | 확정 | `MainV2Activity.onNewIntent()`는 foreground 라이브룸에서 전달된 라이브룸 딥링크만 1초 지연한다. | 채팅·오디오·일반 딥링크는 기존 즉시 처리 경로를 유지하고 `SINGLE_TOP`도 유지된다. | `P2-T1` |
| `ROUTE-005` | 확정 | 지연 중에는 기존 `LoadingDialog`를 표시하고 라이브 입장 loading 상태로 표시 책임을 인계한다. | deferred 또는 live entry loading 중에만 다이얼로그가 표시되고 두 상태가 모두 끝나면 해제된다. | `P2-T1` |
| `ROUTE-006` | 확정 | Activity 종료 후 지연된 딥링크가 실행되지 않는다. | `onDestroy()`에서 해당 callback을 제거하고 다이얼로그를 해제한다. | `P2-T1` |
| `ROUTE-007` | 확정 | `live.reservation.status` package 파일은 `live/reservation/status/`에 위치한다. | 대상 6개 파일이 새 경로에 있고 기존 `live/reservation_status/` 경로는 존재하지 않는다. | `P3-T1` |
## 7. 오류 처리와 보안
- 기존 Intent extra와 Activity flag를 그대로 유지하므로 신규 오류 처리는 추가하지 않는다.
- 인증 정보, 토큰, URL을 새로 저장하거나 로그로 출력하지 않는다.
## 8. 테스트 및 수용 기준
- source test가 두 경로의 `MainV2Activity` target과 레거시 target 부재를 검증한다.
- focused unit test와 Kotlin compile이 성공한다.
- 사용자 요청에 따라 기기·에뮬레이터 UI 테스트는 수행하지 않는다.
- source test가 foreground 라이브룸 딥링크의 선택적 지연, 기존 loading 결합 조건, 종료 정리를 검증한다.
## 9. Decision Log
| 날짜 | 결정 | 이유 |
|---|---|---|
| 2026-09-18 | 두 직접 진입 경로를 함께 V2로 전환 | 사용자가 두 곳 모두 수정을 확정했다. |
| 2026-09-18 | 기존 flags, extras, `finishAffinity()` 유지 | 메인 target 외 동작 변경을 피한다. |
| 2026-09-18 | foreground 라이브룸 딥링크에만 1초 지연과 기존 로딩 다이얼로그를 적용 | 레거시 진입 경로가 제공하던 정리 시간을 재현하면서 다른 딥링크와 `MainV2Activity` 단일 인스턴스 동작을 유지한다. |
| 2026-09-18 | `reservation_status` 물리 디렉터리를 `reservation/status`로 이동 | package 선언과 소스 경로를 일치시키고 ktlint 허용 package명을 유지한다. |
@@ -0,0 +1,53 @@
# Phase 3 예약 status package 경로 코드 리뷰
## 1. 리뷰 정보
| 항목 | 내용 |
|---|---|
| 리뷰 대상 | Phase 3 / Task 3.1 |
| 기준 commit 또는 working tree | 현재 working tree |
| 리뷰 일자 | 2026-09-18 |
| 리뷰어 | Oracle 3명, 자동 QA, 저장소 맥락 검토 agent |
| 기준 문서 | `prd.md`, `plan-task.md` |
| 리뷰 상태 | 판정 완료 |
## 2. 목적과 범위
- `live.reservation.status` package 선언과 Kotlin 파일의 물리 디렉터리가 일치하는지 확인한다.
- 이동 대상 6개 파일, 경로 기반 source test, manifest·import·빌드 설정과 resource 보존 여부를 검토한다.
- 이전 Phase의 동작 변경과 기기·에뮬레이터 UI 검증은 범위에서 제외한다.
## 3. 실행한 검증
| 명령 또는 검증 | 결과 | 핵심 증거 |
|---|---|---|
| focused source test RED | 실패 확인 | 3개 중 신규 경로 정합성 test 1개가 새 디렉터리 부재로 실패 |
| focused source test GREEN | 성공 | package test 3개와 direct-entry test 2개 통과 |
| `:app:compileDebugKotlin` | 성공 | QA 재실행을 포함해 exit code 0 |
| `:app:ktlintCheck` | 성공 | 기존 `.editorconfig` deprecation warning만 존재 |
| 전체 `:app:testDebugUnitTest` | 기존 실패 | 1,269개 중 무관한 chat source test 2개 실패 |
| `git diff --check` | 성공 | 출력 없음 |
| 경로·참조·resource 조사 | 성공 | 새 경로에 6개, 구 디렉터리 없음, production 구 참조와 resource diff 없음 |
## 4. 발견 사항 요약
확정 발견 사항 없음.
목표 적합성, 자동 QA, 코드 품질, 보안, 저장소 맥락 검토 5개가 모두 통과했다. 코드 품질과 맥락 검토에서 Phase 3 완료 기록 미반영을 비차단 항목으로 확인했으며, 본 문서와 `plan-task.md`에 반영했다.
## 5. plan·goal 전환
전환 항목 없음.
## 6. 리뷰 종료 판정
| 판정 항목 | 결과 | 근거 |
|---|---|---|
| 리뷰 범위 전체 확인 | 충족 | 5개 병렬 리뷰 완료 |
| 후보 항목 판정 완료 | 충족 | 차단 및 확정 발견 사항 없음 |
| 확정 항목 plan 반영 | 해당 없음 | 확정 발견 사항 없음 |
| 검증 명령과 결과 기록 | 충족 | 본 문서와 `plan-task.md`에 누적 |
**최종 결론:** 확정 발견 사항 없음. 변경 승인.
**남은 항목:** 이번 변경과 무관한 전체 unit test 2건.