51 lines
6.9 KiB
Markdown
51 lines
6.9 KiB
Markdown
# 팬톡 작성/수정 응답 보강 Plan/Task
|
|
|
|
## 시나리오 계약
|
|
- Happy path: 인증 사용자가 `POST /explorer/profile/cheers`를 호출하면 HTTP 200과 함께 `$.data.fanTalkId`, `$.data.writerId`, `$.data.content`, `$.data.creatorReplies=[]`가 반환된다. Real surface: MockMvc controller test.
|
|
- Edge: 인증 사용자가 `PUT /explorer/profile/cheers`로 내용을 수정하면 HTTP 200과 함께 수정 후 content가 `$.data.content`에 반환된다. Real surface: MockMvc controller test.
|
|
- Adjacent regression: 기존 V2 FanTalk 탭 `CreatorChannelFanTalkResponse.from(domain)` 매핑과 조회 controller 응답은 유지된다. Real surface: 기존 `CreatorChannelFanTalkControllerTest`, DTO/facade unit test.
|
|
|
|
### Phase 1: 문서와 RED 테스트
|
|
- [x] **Task 1.1: PRD/plan-task 작성**
|
|
- 파일 경로: `docs/20260709_팬톡_작성수정_응답보강/prd.md`, `docs/20260709_팬톡_작성수정_응답보강/plan-task.md`
|
|
- RED: 문서 작업은 TDD 예외. TDD 예외 사유: 코드 동작 변경 전 요구사항 고정 문서다.
|
|
- GREEN: 요구사항, non-goal, 시나리오, 검증 명령을 문서화한다.
|
|
- REFACTOR: 문서 경로와 명령 유효성을 확인한다.
|
|
- 검증 기록: `docs/20260709_팬톡_작성수정_응답보강/prd.md`, `docs/20260709_팬톡_작성수정_응답보강/plan-task.md`를 생성해 endpoint 유지, legacy 직접 수정, `CreatorChannelFanTalkResponse` 반환 범위를 문서화했다.
|
|
- [x] **Task 1.2: 작성/수정 응답 RED 테스트 추가**
|
|
- 파일 경로: `src/test/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerControllerTest.kt`, `src/test/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/fantalk/application/CreatorChannelFanTalkFacadeTest.kt`
|
|
- RED: `./gradlew test --tests kr.co.vividnext.sodalive.explorer.ExplorerControllerTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.application.CreatorChannelFanTalkFacadeTest --no-daemon` 실행 시 신규 기대 동작이 미구현이라 실패해야 한다.
|
|
- GREEN: 구현 후 같은 명령이 통과해야 한다.
|
|
- REFACTOR: 기존 FanTalk 탭 controller 테스트를 함께 실행한다.
|
|
- RED 검증 기록: 신규 controller 테스트 추가 직후 같은 명령을 실행했고, `ExplorerService.writeCheers/modifyCheers`가 `Unit` 반환이라 `CannotStubVoidMethodWithReturnValue`로 2개 테스트 실패를 확인했다.
|
|
|
|
### Phase 2: 최소 구현
|
|
- [x] **Task 2.1: legacy cheers 응답 매핑 구현**
|
|
- 파일 경로: `src/main/kotlin/kr/co/vividnext/sodalive/v2/api/creator/channel/fantalk/dto/CreatorChannelFanTalkTabResponse.kt`
|
|
- RED: legacy `CreatorCheers`를 `CreatorChannelFanTalkResponse`로 만들 수 없어 테스트가 실패한다.
|
|
- GREEN: `CreatorChannelFanTalkResponse.from(CreatorCheers, cloudFrontHost)`를 추가한다.
|
|
- REFACTOR: 기존 domain 기반 `from` 매핑을 변경하지 않는다.
|
|
- GREEN 검증 기록: `CreatorChannelFanTalkResponse.from(CreatorCheers, cloudFrontHost)`를 추가해 작성자 ID/닉네임/프로필 URL/content/UTC 생성 시각/빈 creatorReplies를 매핑했다.
|
|
- [x] **Task 2.2: 작성/수정 서비스 반환값 변경**
|
|
- 파일 경로: `src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerService.kt`, `src/main/kotlin/kr/co/vividnext/sodalive/explorer/ExplorerController.kt`
|
|
- RED: controller/service가 `Unit`을 반환해 `data`가 비어 있다.
|
|
- GREEN: 저장/수정 후 `CreatorChannelFanTalkResponse`를 반환한다.
|
|
- REFACTOR: 메시지와 기존 검증 흐름을 유지한다.
|
|
- GREEN 검증 기록: `ExplorerService.writeCheers`, `ExplorerService.modifyCheers`가 저장/수정된 `CreatorCheers`를 `CreatorChannelFanTalkResponse`로 반환하도록 변경했고, `ExplorerController`의 기존 `ApiResponse.ok(service..., message)` 흐름은 유지했다.
|
|
|
|
## 전체 검증 기록
|
|
- 작업 시작 전 확인: `rg -n "explorer/profile/cheers|CreatorChannelFanTalkResponse|writeCheers|modifyCheers"`로 대상 controller/service/DTO 위치를 확인했다.
|
|
- RED: `./gradlew test --tests kr.co.vividnext.sodalive.explorer.ExplorerControllerTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.application.CreatorChannelFanTalkFacadeTest --no-daemon` 실행 결과 신규 테스트 2개가 `CannotStubVoidMethodWithReturnValue`로 실패해 기존 서비스 반환 타입이 `Unit`임을 확인했다.
|
|
- GREEN/인접 회귀: `./gradlew test --tests kr.co.vividnext.sodalive.explorer.ExplorerControllerTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.application.CreatorChannelFanTalkFacadeTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.adapter.in.web.CreatorChannelFanTalkControllerTest --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 1m`을 확인했다.
|
|
- Diff 검증: `git diff --check` 실행 결과 출력 없이 통과했다.
|
|
- 포맷 검증: `./gradlew ktlintCheck --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 1m 21s`를 확인했다.
|
|
- 문서 명령 검증: `./gradlew tasks --all --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 20s`를 확인했다.
|
|
- 전체 회귀 검증: `./gradlew test --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 8m 56s`를 확인했다.
|
|
- 리뷰 게이트 1차 결과: 목표 검증/QA/컨텍스트 마이닝은 PASS였지만, 보안 리뷰와 코드 품질 리뷰에서 `PUT /explorer/profile/cheers`가 작성자/크리에이터가 아닌 인증 사용자에게도 `cheersId` 기반 응답 데이터를 반환할 수 있다는 IDOR성 정보 노출 지적이 나왔다.
|
|
- 보안 RED: `ExplorerServiceTest.shouldRejectModifyCheersForNonWriterAndNonCreator` 추가 직후 `./gradlew test --tests kr.co.vividnext.sodalive.explorer.ExplorerServiceTest --no-daemon` 실행 결과 `AssertionFailedError`로 실패해 비작성자/비크리에이터 요청이 예외 없이 데이터를 받을 수 있음을 확인했다.
|
|
- 보안 GREEN: `ExplorerService.modifyCheers`에서 `isWriter`, `isCreator`를 계산하고 둘 다 아니면 `SodaException(messageKey = "common.error.invalid_request")`를 던지도록 수정했다. 이후 `./gradlew test --tests kr.co.vividnext.sodalive.explorer.ExplorerServiceTest --tests kr.co.vividnext.sodalive.explorer.ExplorerControllerTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.application.CreatorChannelFanTalkFacadeTest --tests kr.co.vividnext.sodalive.v2.api.creator.channel.fantalk.adapter.in.web.CreatorChannelFanTalkControllerTest --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 1m 12s`를 확인했다.
|
|
- 최종 포맷 검증: 보안 수정 후 `./gradlew ktlintCheck --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 1m 47s`를 확인했다.
|
|
- 최종 문서 명령 검증: 보안 수정 후 `./gradlew tasks --all --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 30s`를 확인했다.
|
|
- 최종 diff 검증: 보안 수정 후 `git diff --check` 실행 결과 출력 없이 통과했다.
|
|
- 최종 전체 회귀 검증: 보안 수정 후 `./gradlew test --no-daemon` 실행 결과 `BUILD SUCCESSFUL in 14m 53s`를 확인했다.
|