Files

220 lines
12 KiB
Markdown

# 댓글 액션 버튼 표시 라벨 간소화 구현 계획
| 문서 항목 | 내용 |
|---|---|
| 상태 | 구현 완료 |
| 작성일 | 2026-08-06 |
| 요구사항 기준 | [prd.md](./prd.md) |
| API 기준 | 변경 불필요 — 기존 Audio·Community 댓글 계약 유지 |
| 현재 Phase | Phase 1 완료 |
| 현재 활성 Goal | 없음 |
## 목표
Audio·Community 댓글 액션은 짧은 동작명만 화면에 표시하고, 스크린 리더에는 대상 댓글 문맥을 유지한다.
## 현재 상태
| Phase | 상태 | 완료 Task | 활성/다음 Goal | 차단 또는 남은 조건 |
|---:|---|---:|---|---|
| 1 | 완료 | `1/1` | 없음 | 없음 |
- `CommentItem`은 화면에 답글·수정·삭제 동작명만 표시하고 명시적 `aria-label`로 댓글 문맥을 유지한다.
- focused test는 `9/9`, Comments unit은 `16/16`, Comments mock E2E는 `3/3` 통과했다.
- 1280px, 320px와 200% zoom 상당 환경에서 수평 overflow가 없고 axe critical·serious 위반이 0건이다.
## 범위
### 포함
- `CommentItem``답글 작성`, `답글 보기`, `수정`, `삭제` visible label 간소화
- 기존 `댓글 내용 + 동작` accessible name 유지
- Audio·Community 원댓글과 답글의 단위·mock E2E 회귀 검증
- 320px, 200% zoom, keyboard와 접근성 확인
### 제외
- API, model, pagination, 권한, mutation과 error handling 변경
- `수정 저장`, `취소`, `답글 등록`과 form·region label 변경
- FanTalk, Community 게시글 열기 등 `CommentItem` 밖의 버튼 변경
- 새 component, helper, dependency 또는 style 추가
## 기술적 제약
- 기술 스택: React 19.2.8, TypeScript 6.0.3, Vitest 4.1.10, Playwright 1.61.1.
- 아키텍처: 공유 `CommentItem`의 표시 책임 안에서만 변경한다.
- 접근성: visible label 전체가 accessible name에 포함되고 댓글 문맥으로 반복 버튼을 구분해야 한다.
- 데이터·보안: 댓글 값을 새로 저장·전송·log하지 않는다.
- 호환성: 기존 desktop·tablet·mobile과 최소 320px 지원 범위를 유지한다.
- 의존성: 추가하지 않는다.
- 구현: RED → GREEN → REFACTOR 순서와 실제 검증 결과를 Progress에 기록한다.
## Phase 1. 표시 라벨과 accessible name 분리
**Phase 결과:** 댓글 본문은 카드에서 한 번만 보이고, 액션 버튼에는 동작명만 보이면서 보조기술은 기존 문맥형 이름을 읽는다.
**선행조건:** `CLB-001~007`, `CLB-DEC-001~002` 확정.
**Phase 완료 조건:** `P1-T1``P1-GATE` 완료, PRD 성공 기준과 Progress 갱신.
### 구현 항목
#### Task 1.1 공유 댓글 액션 라벨 분리
**Goal 실행 `P1-T1`:** 공유 `CommentItem`의 visible label을 동작명으로 줄이고 기존 contextual accessible name을 보존한다.
- **시작 조건:** `prd.md`가 구현 기준 확정 상태이고 활성 goal이 없음.
- **완료 증거:** RED·GREEN·REFACTOR 체크박스, focused `9/9`, Comments E2E `3/3`, Progress 기록.
- **범위 밖:** 다른 component의 버튼 라벨, action 배치·style과 댓글 동작 변경.
**Files:**
- Create: 없음
- Modify: `src/features/comments/components/CommentItem.tsx`
- Test: `src/features/comments/tests/comment-thread.test.tsx`
- Test: `tests/e2e/comments.spec.ts`
**Interfaces:**
- Consumes: 기존 `CommentItem` props의 `comment.comment`, `replyActionLabel`, `canEdit`, `canDelete`, `onShowReplies`.
- Produces: 기존 props와 callback contract를 바꾸지 않는 짧은 visible label과 `댓글 내용 + 동작` accessible name.
**TDD 절차:**
- [x] **RED: 실패 테스트 작성/실패 확인**`comment-thread.test.tsx`에 답글·수정·삭제 버튼의 `textContent`가 동작명과 정확히 일치하고 role·name은 기존 `댓글 내용 + 동작`으로 조회되는 test 1개를 추가한다. `npm run test:run -- src/features/comments/tests/comment-thread.test.tsx`가 visible label 불일치로 `1 failed, 8 passed`인지 확인한다.
- [x] **GREEN: 최소 구현/통과 확인**`CommentItem.tsx`의 세 버튼에 기존 contextual `aria-label`을 명시하고 children에서는 댓글 내용만 제거한다. 같은 명령이 `exit 0`, `9/9`인지 확인한다.
- [x] **REFACTOR: 정리/회귀 확인** — 새 abstraction 없이 중복 변수만 최소화한 뒤 focused test와 `npm run e2e:mock -- tests/e2e/comments.spec.ts --project=chromium`이 각각 `9/9`, `3/3`으로 통과하는지 확인한다.
- [x] `comments.spec.ts`의 320px Community 흐름에서 visible label과 contextual accessible name을 함께 확인한다.
**검증 기준:**
- **실행 명령:** `npm run test:run -- src/features/comments/tests/comment-thread.test.tsx`; `npm run e2e:mock -- tests/e2e/comments.spec.ts --project=chromium`; `npm run typecheck`; `npm run lint`.
- **기대 결과:** 모든 명령 `exit 0`, focused `9/9`, Comments E2E `3/3`, type·lint 오류 0건.
- **수동 확인:** 1280px Audio와 320px Community에서 버튼에는 동작명만 보이고, 접근성 트리에는 `댓글 내용 + 동작`이 보이며 수평 overflow가 없다.
- [x] TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.
### 완료 조건
- [x] `P1-T1`의 체크박스와 완료 증거가 모두 충족됐다.
- [x] `CLB-001~007`이 구현 또는 Gate 증거로 추적된다.
- [x] PRD 성공 기준과 현재 상태를 실제 결과로 갱신했다.
- [x] 알려진 문서와 구현의 차이가 없다.
### 검증 방법
#### Phase 1 Gate
**Goal 실행 `P1-GATE`:** 짧은 표시 라벨, contextual accessible name과 기존 댓글 동작의 회귀 여부를 최종 판정한다.
- **시작 조건:** `P1-T1` 완료.
- **완료 증거:** 아래 자동·수동 검증 통과와 Progress 기록.
- **범위 밖:** test 삭제·완화, 관련 없는 UI·API 수정.
**실행 명령:**
```bash
npm run test:run -- src/features/comments
npm run e2e:mock -- tests/e2e/comments.spec.ts --project=chromium
npm run typecheck
npm run lint
npm run build:prod
git diff --check
```
**기대 결과:** 모든 명령 `exit 0`, Comments unit test `16/16` 이상, Comments E2E `3/3`, type·lint·build 오류 0건, whitespace 오류 0건.
**수동 확인:**
- [x] 1280px Audio 상세와 320px Community Sheet에서 답글·수정·삭제 visible label을 확인한다.
- [x] 접근성 트리에서 각 버튼의 `댓글 내용 + 동작` 이름과 keyboard focus 순서를 확인한다.
- [x] 200% zoom에서 수평 overflow와 가려진 action이 없는지 확인한다.
- [x] axe critical·serious 위반이 0건인지 확인한다.
## 실행 순서와 의존성
1. `P1-T1`에서 실패 test를 먼저 만들고 최소 UI 변경과 focused·E2E 회귀 검증을 완료한다.
2. `P1-GATE`에서 전체 Comments test와 공통 품질·수동 접근성 검증을 완료한다.
`P1-GATE``P1-T1` 완료 전 시작하지 않는다.
## 변경 금지 항목
- 기존 완료 기록과 관련 PRD의 Decision Log를 삭제하거나 덮어쓰지 않는다.
- 댓글 endpoint, payload, model과 권한 조건을 변경하지 않는다.
- `CommentItem` 밖의 action label을 함께 정리하지 않는다.
- 새 dependency, helper 또는 shared abstraction을 추가하지 않는다.
- test를 삭제·skip·완화하거나 타입 오류를 우회하지 않는다.
## 의사결정 및 중단 규칙
- visible label은 동작명만, accessible name은 `댓글 내용 + 동작`으로 유지한다.
- 구현 범위가 바뀌면 PRD Decision Log와 이 계획을 먼저 갱신한다.
- 기존 role·name selector가 깨지면 accessible name 유지 요구사항을 우선하고 visible text selector만 보정한다.
- 같은 차단 사유가 3회 연속 반복되고 독립 작업도 불가능할 때만 goal을 `blocked`로 갱신한다.
- 코드와 일부 test만 완료된 상태에서는 goal을 `complete`로 갱신하지 않는다.
## Progress
기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다.
### 계획 작성 — 2026-08-06
- 상태: 완료
- 무엇을: 사용자 선택 A를 `CLB-001~007`, 단일 구현 Task와 Phase Gate로 정규화했다.
- 왜: 현재 UI는 댓글 본문을 각 action에 반복하고 화면 표시와 accessible name을 분리하지 않는다.
- 어떻게:
- `npm run test:run -- src/features/comments/tests/comment-thread.test.tsx` — 성공, exit 0, baseline `8/8`.
- 코드·E2E 변경과 수동 UI 검증 — 미실행, 구현 요청 범위가 아님.
- 남은 항목: `P1-T1`, `P1-GATE`.
- 다음 행동: `P1-T1` RED test 작성.
### 1차 구현 — 2026-08-06
- 상태: 완료
- 무엇을: `CommentItem`의 답글·수정·삭제 visible label을 동작명으로 줄이고 `댓글 내용 + 동작` accessible name을 명시적으로 보존했다. 320px Community E2E에 visible label 검증을 추가했다.
- 왜: 댓글 본문과 액션 영역의 시각적 중복을 제거하면서 보조기술의 대상 식별 문맥을 유지하기 위해서다.
- 어떻게:
- RED `npm run test:run -- src/features/comments/tests/comment-thread.test.tsx` — 예상 실패, `1 failed, 8 passed`; `답글 보기` 기대값에 기존 `팬 루트 댓글 답글 보기`가 표시됨을 확인했다.
- GREEN 같은 명령 — 성공, exit 0, `9/9`.
- `npm run test:run -- src/features/comments` — 성공, exit 0, `16/16`.
- `npm run e2e:mock -- tests/e2e/comments.spec.ts --project=chromium` — 성공, exit 0, `3/3`.
- `npm run typecheck`; `npm run lint`; `npm run build:prod`; `git diff --check` — 모두 성공, exit 0. production build에는 기존 500kB 초과 chunk 경고만 있었고 오류는 없었다.
- Playwright 실제 화면 — 1280px Audio와 320px Community에서 visible label과 contextual accessible name 일치, keyboard로 댓글 액션 4개 도달, 수평 overflow 없음.
- 200% zoom 상당 검증 — 1280px의 유효 CSS 폭 640px로 확인, 수평 overflow와 가려진 action 없음.
- axe — 1280px Audio와 320px Community에서 critical·serious 위반 0건.
- 시각 QA — 기능·디자인 시스템 무결성 PASS/HIGH, 시각·CJK 정밀도 PASS/HIGH, 차단 항목 없음.
- 명세·코드 품질 review — 각각 무조건 승인, 발견 사항 없음.
- 남은 항목: 없음.
- 다음 행동: 현재 브랜치 변경 검토 후 통합 방식 결정.
## Decision Log
| 날짜 | ID | 상태 | 결정 | 근거 | 영향 Goal/문서 |
|---|---|---|---|---|---|
| 2026-08-06 | `CLB-PLAN-DEC-001` | 확정 | 화면 표시만 간소화하고 contextual accessible name을 유지한다. | 사용자 선택 A, `CLB-DEC-001` | `P1-T1`, `P1-GATE`, `prd.md` |
| 2026-08-06 | `CLB-PLAN-DEC-002` | 확정 | 공유 `CommentItem` 한 파일에서 최소 변경한다. | 모든 대상 UI가 같은 component를 사용한다. | `P1-T1` |
## 발견된 문제
| ID | 심각도 | 상태 | 발견 내용 | 영향 Goal | 처리 계획 |
|---|---|---|---|---|---|
| `CLB-ISSUE-001` | Medium | 완료 | 댓글 본문이 카드 본문과 답글·수정·삭제 버튼마다 반복된다. | `P1-T1` | visible label과 accessible name 분리 완료 |
## 최종 보고 형식
```markdown
구현 결과: Audio·Community 댓글 버튼은 동작명만 표시하고 보조기술에는 댓글 문맥을 유지한다.
- 변경: `CommentItem.tsx`의 visible label과 accessible name 분리
- 결정: `CLB-DEC-001` — 화면 표시만 간소화
- 검증:
- `npm run test:run -- src/features/comments`<실제 결과>
- `npm run e2e:mock -- tests/e2e/comments.spec.ts --project=chromium`<실제 결과>
- 1280px·320px·200% zoom·keyboard·접근성 트리 — <실제 결과>
- 남은 항목: <없음 또는 구체적인 항목>
- 문서: `docs/20260806_댓글액션버튼라벨/{prd.md,plan-task.md}`
```
최종 보고는 실제 실행한 최신 검증 결과와 완료되지 않은 범위를 함께 기록한다.