Files

43 KiB

수정 요청 변경 필드 전송 구현 계획

문서 항목 내용
상태 구현·검증 완료
작성일 2026-08-06
요구사항 기준 prd.md
API 기준 api-contract.md
현재 Phase Phase 1 변경 필드 전송 통합
현재 활성 Goal 없음

목표

AI 캐릭터, 오디오 콘텐츠, 커뮤니티 게시글, 시리즈, FanTalk 답글 수정 시 최종 변경 필드와 교체 파일만 전송하고, 변경이 없으면 저장과 API 요청을 차단한다.

현재 상태

Phase 상태 완료 Task 활성/다음 Goal 차단 또는 남은 조건
1 완료 7/7 없음 없음
  • 동시에 하나의 미완료 goal만 운용한다.
  • 완료된 Task와 검증 기록은 삭제하거나 되돌리지 않는다. 후속 수정은 회귀 수정 Task와 새 goal ID를 추가한다.
  • 사용자가 token budget을 지정하지 않았으므로 goal에 token budget을 설정하지 않는다.

범위

포함

  • AI 캐릭터, 오디오 콘텐츠, 커뮤니티 게시글, 시리즈, FanTalk 답글의 일반 수정 payload
  • 현재 form 값과 조회 기준값의 field별 비교, nullable 삭제와 미변경 생략 구분
  • 이미지·cover·post image만 변경한 multipart의 빈 request: {}
  • 변경 없음·변경 후 원복 상태의 native disabled 저장 control과 mutation 0건
  • 기능별 contract·component test와 mock Chromium E2E 회귀, typecheck·lint·build

제외

  • 생성 payload, backend DTO와 HTTP method 변경
  • 캐릭터·오디오 콘텐츠·시리즈 비활성화 payload
  • 커뮤니티 게시글 고정 전환·soft delete, 시리즈 순서 변경, FanTalk 원글 삭제
  • 수정 화면 신규 field, image 삭제, 원작 연결 해제 기능
  • 새 dependency, 전역 form store, 범용 deep-diff abstraction

기술적 제약

  • 기술 스택: React 19, TypeScript 6, Zod 4, Vitest 4, Testing Library, Playwright 1.61
  • 코드 스타일: TypeScript strict를 유지하고 as any, @ts-ignore, @ts-expect-error로 오류를 숨기지 않는다.
  • 아키텍처: API helper의 transport·schema 책임은 유지하고, 변경 판정은 각 feature의 form serializer 또는 component에 둔다.
  • 데이터: 현재 form과 기준 DTO에 같은 trim·nullable·array serialization을 적용하고 false, 0, null을 유효한 변경값으로 보존한다.
  • 파일: multipart request part는 항상 유지한다. 파일만 변경하면 {}, 파일 미변경이면 file part를 생략한다.
  • UX: payload와 저장 disabled가 서로 다른 판정을 사용하지 않도록 같은 request 결과에서 hasChanges를 계산한다.
  • 보안: 인증·권한·resource ownership·민감정보 비기록 정책을 변경하지 않는다.
  • 호환성: 기존 기능별 desktop·tablet·mobile capability와 지원 browser를 유지한다.
  • 의존성: 새 package를 추가하지 않고 언어·플랫폼 기능과 기존 Zod schema를 사용한다.
  • 계약: 제공되지 않은 field, endpoint, response, 오류 status/key를 만들지 않는다.
  • mock: 기존 explicit mock mode만 사용하고 production 자동 fallback을 추가하지 않는다.
  • 구현: 각 Task는 RED → GREEN → REFACTOR 순서로 진행하고 실제 결과를 Progress에 누적한다.

Phase 1. 변경 필드 전송 통합

Phase 결과: 다섯 수정 기능에서 단일 field 변경, nullable 삭제, file-only 변경과 무변경 상태가 동일한 계약으로 동작한다.

선행조건: prd.mdDIFF-001~005api-contract.md 확정.

Phase 완료 조건: P1-T1~P1-T5P1-GATE 완료, 검증 결과와 실제 request 증거를 Progress에 누적.

구현 항목

Task 1.1 AI 캐릭터 변경 field 직렬화

Goal 실행 P1-T1: 캐릭터 수정 request가 변경된 profile·optional·repeated field만 포함하고 무변경 저장을 차단한다.

  • 시작 조건: CHAR-001, DIFF-001~005, DATA-001~002, API Contract §1·§2.1 확정.
  • 완료 증거: direct field·nullable·repeated·image-only·무변경 test, focused 회귀, Progress 기록.
  • 범위 밖: create form, region 수정, isActive, 원작 연결 해제 backend 의미 변경.

Files:

  • Modify: src/features/characters/components/character-optional-field-serialization.ts
  • Modify: src/features/characters/pages/CharacterEditPage.tsx
  • Test: src/features/characters/tests/CharacterEditPage.test.tsx
  • Test: src/features/characters/tests/character-api.test.ts

Interfaces:

  • Consumes: CharacterDetail, CharacterOptionalFieldsValue, UpdateCharacterParams["request"], 기존 serializeCharacterRequest.
  • Produces: 현재 optional field와 초기 optional field를 비교해 변경 key만 반환하는 toUpdateCharacterOptionalRequest 계약, direct field와 합쳐진 변경 전용 request, 동일 request 기반 hasChanges.
  • 보존: 원작 미선택의 originalWorkId key 생략, file part 이름 image, region·일반 수정 isActive 제외.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인CharacterEditPage.test.tsx에 name 단일 변경, optional field null 삭제, 반복 field 변경, 최초·원복 disabled, image-only {} 시나리오를 추가하고 focused 명령에서 현재 전체 field payload 또는 enabled 저장 때문에 실패하는지 확인한다.
  • GREEN: 최소 구현/통과 확인 — optional serializer가 초기값과 달라진 key만 만들고 page가 direct field와 file을 같은 방식으로 판정하도록 최소 수정해 focused 명령을 통과시킨다.
  • REFACTOR: 정리/회귀 확인 — payload 계산 중복만 제거하고 create serializer·API multipart helper는 유지한 채 Character 전체 회귀를 실행한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/characters/tests/CharacterEditPage.test.tsx src/features/characters/tests/character-api.test.ts; npm run test:run -- src/features/characters; npm run typecheck; npm run lint.

  • 기대 결과: 모든 명령 exit 0, 신규 5개 변경 감지 test를 포함한 실행 test 전부 통과, name 단일 변경 key 1개, 무변경 PUT 0건, type·lint 오류 0건.

  • 수동 확인: 1280px에서 캐릭터 이름만 수정한 request part가 {name}이고, 원복 시 저장 disabled, image만 교체하면 image{}만 전송된다.

  • TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.

Task 1.2 오디오 콘텐츠 변경 field 직렬화

Goal 실행 P1-T2: 오디오 콘텐츠 수정 request가 변경된 title·detail·tags·price와 새 cover image만 포함한다.

  • 시작 조건: P1-T1 완료, AUDIO-001, DIFF-001~005, API Contract §1·§2.2 확정.
  • 완료 증거: 단일 field·price 0·cover-only·무변경 test, 기존 비활성화 회귀, Progress 기록.
  • 범위 밖: create options, audio 원본 교체, theme·release date 수정, 비활성화 payload.

Files:

  • Modify: src/features/audio-contents/components/audio-content-form-helpers.ts
  • Modify: src/features/audio-contents/components/AudioContentForm.tsx
  • Test: src/features/audio-contents/tests/audio-form-update.test.tsx
  • Test: src/features/audio-contents/tests/audio-contract.test.ts

Interfaces:

  • Consumes: AudioContentDetail, AudioContentUpdateRequest, 기존 toUpdateRequestcreateAudioContentUpdateBody.
  • Produces: 직렬화된 현재 값과 audio 기준값을 비교해 변경 key만 반환하는 toUpdateRequest, 같은 request와 coverImage 기반 hasChanges.
  • 보존: file part coverImage, request part, 일반 update isActive 금지, price 0 허용.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인 — detail 단일 변경, price 0, cover-only {}, 최초·원복 disabled와 화면에 없는 boolean 미전송 test를 작성하고 현재 전체 payload 때문에 실패하는지 확인한다.
  • GREEN: 최소 구현/통과 확인 — 기존 toUpdateRequest가 실제 수정 화면 field만 비교해 반환하고 edit button이 같은 결과로 disabled되도록 수정한다.
  • REFACTOR: 정리/회귀 확인 — create path와 비활성화 schema를 건드리지 않고 update serializer의 중복만 정리한 뒤 Audio 전체 회귀를 실행한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/audio-contents/tests/audio-form-update.test.tsx src/features/audio-contents/tests/audio-contract.test.ts; npm run test:run -- src/features/audio-contents; npm run typecheck; npm run lint.

  • 기대 결과: 모든 명령 exit 0, 신규 5개 변경 감지 test를 포함한 실행 test 전부 통과, detail 단일 변경 key 1개, cover-only request {}, 무변경 PUT 0건.

  • 수동 확인: 1280px에서 detail만 수정하고 multipart requestdetail만 있는지, 기존 cover 미선택 시 coverImage가 없는지 확인한다.

  • TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.

Task 1.3 커뮤니티 게시글 변경 field 직렬화

Goal 실행 P1-T3: 커뮤니티 게시글 수정 저장이 변경된 content·comment permission·adult flag와 새 image만 포함한다.

  • 시작 조건: P1-T2 완료, COMM-001, DIFF-001~005, API Contract §1·§2.3 확정.
  • 완료 증거: 단일 field·boolean false·image-only·무변경 test, 고정·soft delete 회귀, Progress 기록.
  • 범위 밖: create form, audio file, isFixed 전환, soft delete 계약.

Files:

  • Modify: src/features/community-posts/components/CommunityPostSheet.tsx
  • Test: src/features/community-posts/tests/community-sheet.test.tsx
  • Test: src/features/community-posts/tests/community-contract.test.ts

Interfaces:

  • Consumes: CommunityPostListItem, CommunityPostUpdateRequest, updateCommunityPost.
  • Produces: Sheet의 현재 수정 field와 post 기준값을 비교한 request, 같은 request와 postImage 기반 hasChanges.
  • 보존: toggleFixed{isFixed}, softDeleteCommunityPost의 기존 request, file part postImage.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인 — content 단일 변경, boolean true→false, image-only {}, 최초·원복 disabled test를 작성하고 현재 isFixed와 미변경 field가 함께 전송되는 실패를 확인한다.
  • GREEN: 최소 구현/통과 확인 — Sheet의 일반 수정 저장 request만 field별 비교하고 저장 button이 같은 hasChanges를 사용하도록 수정한다.
  • REFACTOR: 정리/회귀 확인 — 고정·비활성화 action의 전용 함수와 pending guard를 유지하고 일반 수정 계산만 읽기 쉽게 정리한 뒤 Community 전체 회귀를 실행한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/community-posts/tests/community-sheet.test.tsx src/features/community-posts/tests/community-contract.test.ts; npm run test:run -- src/features/community-posts; npm run typecheck; npm run lint.

  • 기대 결과: 모든 명령 exit 0, 신규 4개 변경 감지 test를 포함한 실행 test 전부 통과, content 단일 변경 key 1개, 일반 수정 isFixed 0건, 무변경 PUT 0건.

  • 수동 확인: 지원 viewport에서 Sheet를 열어 content만 수정했을 때 request에 content만 있고 고정 button과 비활성화 button 동작이 유지되는지 확인한다.

  • TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.

Task 1.4 시리즈 변경 field 직렬화

Goal 실행 P1-T4: 시리즈 수정 request가 변경된 기본·enum·nullable field와 새 image만 포함한다.

  • 시작 조건: P1-T3 완료, SERIES-001, DIFF-001~005, API Contract §1·§2.4 확정.
  • 완료 증거: title 단일 변경, nullable 삭제, 배열·enum 변경, image-only·무변경 test, 비활성화 회귀, Progress 기록.
  • 범위 밖: create-only keyword, 연결 콘텐츠, 순서 변경, 비활성화 payload.

Files:

  • Modify: src/features/series/components/SeriesForm.tsx
  • Test: src/features/series/tests/series-form.test.tsx
  • Test: src/features/series/tests/series-contract.test.ts
  • Test: src/features/series/tests/series-update-invariants.test.ts

Interfaces:

  • Consumes: SeriesListItem, SeriesUpdateRequest, 기존 textOrNull, editedState, updateSeries.
  • Produces: 모든 edit field에 editedState와 같은 원본 비교를 적용한 update request, 같은 request와 image 기반 hasChanges.
  • 보존: array field 변경 시 전체 새 배열, writer·studio 삭제 시 null, 일반 update의 isActive 금지.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인 — title 단일 변경, writer 삭제 null, published days 변경, image-only {}, 최초·원복 disabled test를 작성하고 state 외 미변경 field가 전송되는 실패를 확인한다.
  • GREEN: 최소 구현/통과 확인 — 기존 editedState 패턴을 edit field 전체에 적용해 request를 만들고 form disabled·dirty 판정이 같은 결과를 사용하게 한다.
  • REFACTOR: 정리/회귀 확인 — field 의미를 감추는 범용 abstraction 없이 local serializer 하나로 중복만 줄이고 Series 전체 회귀를 실행한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/series/tests/series-form.test.tsx src/features/series/tests/series-contract.test.ts src/features/series/tests/series-update-invariants.test.ts; npm run test:run -- src/features/series; npm run typecheck; npm run lint.

  • 기대 결과: 모든 명령 exit 0, 신규 5개 변경 감지 test를 포함한 실행 test 전부 통과, title 단일 변경 key 1개, nullable 삭제 null 보존, 무변경 PUT 0건.

  • 수동 확인: 1280px에서 title만 수정, writer 삭제, image-only 교체를 각각 실행해 request key와 file part를 확인한다.

  • TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.

Task 1.5 FanTalk 답글 무변경 수정 차단

Goal 실행 P1-T5: FanTalk 답글은 기존 답글과 다른 content가 있을 때만 수정 request를 전송한다.

  • 시작 조건: P1-T4 완료, FANTALK-001, DIFF-001, DIFF-004, API Contract §1.4·§2.5 확정.
  • 완료 증거: 최초 disabled, 변경 enabled·{content}, 원복 disabled·PUT 0건 test, FanTalk 회귀, Progress 기록.
  • 범위 밖: 답글 생성, FanTalk 원글 삭제, isActive, 답글 trim 정책 변경.

Files:

  • Modify: src/features/fan-talks/components/FanTalkReplySheet.tsx
  • Modify: src/features/fan-talks/components/FanTalkReplyForm.tsx
  • Test: src/features/fan-talks/tests/fan-talk-reply.test.tsx
  • Test: src/features/fan-talks/tests/fan-talk-contract.test.ts

Interfaces:

  • Consumes: FanTalkCreatorReply.content, FanTalkReplyForm, updateFanTalkReply.
  • Produces: edit mode에서 content !== existingReply.content를 나타내는 submit disabled prop, 변경 시 기존 {content} request.
  • 보존: create mode의 빈값 validation, pending single-flight, update response와 원글 DELETE.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인 — 기존 답글 open 직후 수정 button disabled, content 변경 후 enabled·PUT 1건, 원복 후 disabled·PUT 0건 test를 작성하고 현재 항상 enabled인 실패를 확인한다.
  • GREEN: 최소 구현/통과 확인 — Sheet가 edit 변경 여부를 계산해 Form submit button에 전달하고 기존 update body는 유지한다.
  • REFACTOR: 정리/회귀 확인 — create와 edit의 disabled 이유를 명확히 유지하고 새 serializer나 API helper 없이 FanTalk 전체 회귀를 실행한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/fan-talks/tests/fan-talk-reply.test.tsx src/features/fan-talks/tests/fan-talk-contract.test.ts; npm run test:run -- src/features/fan-talks; npm run typecheck; npm run lint.

  • 기대 결과: 모든 명령 exit 0, 신규 3개 변경 감지 test를 포함한 실행 test 전부 통과, 변경 update body {content} key 1개, 무변경·원복 PUT 0건.

  • 수동 확인: FanTalk 답글 Sheet를 열면 수정 button이 disabled이고, 내용 변경 시 enabled, 원복 시 다시 disabled인지 확인한다.

  • TDD 단계와 검증 기준의 실제 결과를 Progress에 기록한다.

Task R1.1 커뮤니티 content 직렬화 비교 일치

Goal 실행 P1-R1: REV-P1-001을 수정해 커뮤니티 게시글의 기존 원문 직렬화와 변경 판정을 일치시키고 공백 변경 누락을 방지한다.

  • 시작 조건: REV-P1-001 확정, P1-T3·P1-GATE 완료, DIFF-001~004, API Contract §1.2·§2.3 확정.
  • 완료 증거: 공백 변경 실패 재현 test, 최소 수정 후 focused·Community 전체·Phase Gate 통과, Progress와 review 수정 검증 기록.
  • 범위 밖: 커뮤니티 생성 payload, content trim 정책 신설, 고정 전환, soft delete, 다른 기능 serializer 변경.

Files:

  • Modify: src/features/community-posts/components/CommunityPostSheet.tsx
  • Test: src/features/community-posts/tests/community-sheet.test.tsx
  • Modify: docs/20260806_수정요청변경필드만전송/plan-task.md
  • Add: docs/20260806_수정요청변경필드만전송/reviews/phase1-changed-field-requests.md

Interfaces:

  • Consumes: 기존 Community update의 raw content, CommunityPostListItem.content, CommunityPostUpdateRequest.
  • Produces: 현재 content와 기준 post.content에 동일한 원문 직렬화 규칙을 적용한 변경 판정.
  • 보존: 전송하는 content 원문, file-only {}, isFixed·soft delete 전용 request.

TDD 절차:

  • RED: 실패 테스트 작성/실패 확인 — 기존 content 앞뒤에 공백을 추가하면 저장이 활성화되고 원문 {content}가 전송돼야 하는 test가 현재 trim 비교로 실패하는지 확인한다.
  • GREEN: 최소 구현/통과 확인content 비교의 trim()만 제거해 기존 update 직렬화와 일치시키고 focused test를 통과시킨다.
  • REFACTOR: 회귀 확인 — 별도 abstraction 없이 Community 전체와 Phase Gate를 실행한다.
  • review 상태와 Progress에 실제 명령·결과를 누적한다.

검증 기준:

  • 실행 명령: npm run test:run -- src/features/community-posts/tests/community-sheet.test.tsx src/features/community-posts/tests/community-contract.test.ts; npm run test:run -- src/features/community-posts; Phase 1 Gate 전체 명령.
  • 기대 결과: 모든 명령 exit 0, 공백 변경 request에 raw content만 존재, 기존 227개 회귀와 정적 Gate 오류 0건.
  • 수동 확인: 자동 component test로 동일 payload와 disabled 상태를 검증하며 별도 Network 확인은 대체 사유를 review에 기록한다.

Task R1.2 PRD 성공 기준 상태 동기화

Goal 실행 P1-R2: REV-P1-002를 수정해 검증 완료된 PRD 성공 기준과 Phase 1 완료 기록을 일치시킨다.

  • 시작 조건: REV-P1-002 확정, P1-R1과 Phase Gate 완료.
  • 완료 증거: PRD §14의 검증 완료 항목 체크, plan·review 기록, 문서 정적 검사.
  • 범위 밖: 요구사항·API Contract 의미 변경, 검증하지 않은 항목 완료 처리, 코드 변경.

Files:

  • Modify: docs/20260806_수정요청변경필드만전송/prd.md
  • Modify: docs/20260806_수정요청변경필드만전송/plan-task.md
  • Modify: docs/20260806_수정요청변경필드만전송/reviews/phase1-changed-field-requests.md

Interfaces:

  • Consumes: P1-T1~P1-R1 Progress와 Phase Gate 실제 검증 결과.
  • Produces: PRD §14 기능·UI/UX·추적성 성공 기준의 현재 완료 상태.
  • 보존: PRD 요구사항 본문, API Contract, §18 요구사항 변경 체크리스트.

검증 절차:

  • 기존 Phase Gate 증거와 PRD §14 각 항목을 대조한다.
  • 증거가 있는 §14 성공 기준만 완료 표시한다.
  • rggit diff --check로 미완료·공백 오류를 확인하고 Progress·review에 결과를 기록한다.

TDD 예외 사유: 실행 코드가 아닌 완료 상태 문서 동기화이며, 근거는 이미 통과한 component·E2E·정적 Gate다.

완료 조건

  • P1-T1~P1-T5의 체크박스와 완료 증거가 모두 충족됐다.
  • DIFF-001~005, 기능별 요구사항과 파일·데이터 요구사항이 구현 또는 명시적 제외로 추적된다.
  • 다섯 기능의 payload와 저장 disabled가 같은 변경 판정을 사용한다.
  • 기존 생성·비활성화·고정·순서·삭제 contract 회귀가 없다.
  • 문서, 구현, test와 실제 검증 기록의 차이가 없다.

검증 방법

Phase 1 Gate

Goal 실행 P1-GATE: 다섯 수정 기능의 변경 field payload, 무변경 차단과 기존 mutation 회귀를 통합 판정한다.

  • 시작 조건: P1-T1~P1-T5 완료.
  • 완료 증거: focused·도메인 전체·mock Chromium E2E·정적 Gate와 수동 Network 검증 통과, Progress 기록.
  • 범위 밖: Gate 통과를 위한 test 삭제·skip·완화와 관련 없는 refactor.

실행 명령:

npm run test:run -- src/features/characters src/features/audio-contents src/features/community-posts src/features/series src/features/fan-talks
npm run e2e:mock -- tests/e2e/character-workspace.spec.ts tests/e2e/audio-content.spec.ts tests/e2e/community.spec.ts tests/e2e/series.spec.ts tests/e2e/fan-talk.spec.ts --project=chromium
npm run typecheck
npm run lint
npm run build
git diff --check

기대 결과: 모든 명령 exit 0; 신규 변경 감지 test 최소 22개를 포함한 실행 test 전부 통과; 단일 field request key 1개; 무변경 mutation 0건; 기존 create·deactivate·fixed·order·delete E2E 실패 0건; type·lint·build 오류 0건.

Payload·회귀 확인:

  • focused/component test에서 캐릭터·오디오 콘텐츠·시리즈 단일 field와 file-only 수정 request를 확인한다.
  • focused/component test와 mock Chromium E2E에서 커뮤니티 게시글 단일 field와 image-only 수정 request, 지원 viewport 회귀를 확인한다.
  • focused/component test와 mock Chromium E2E에서 FanTalk 답글의 최초·변경·원복 button 상태와 update body를 확인한다.
  • focused/component test에서 nullable 삭제의 null, boolean false, price 0이 존재하고 미변경 key가 없는지 확인한다.
  • focused/component test와 mock Chromium E2E에서 기존 비활성화·고정·삭제 action payload/API 회귀가 없는지 확인한다.

실행 순서와 의존성

순서 Goal 선행조건 병행 가능 차단 시 다음 행동
1 P1-T1 PRD·API Contract 확정 아니요 Character 기준값·nullable 계약 보정
2 P1-T2 P1-T1 아니요 Audio 상세 DTO와 화면 field 대조
3 P1-T3 P1-T2 아니요 일반 수정과 전용 action request 분리 확인
4 P1-T4 P1-T3 아니요 Series nullable·array·enum 계약 대조
5 P1-T5 P1-T4 아니요 create/edit mode disabled 조건 분리 확인
6 P1-GATE Phase 1 Task 전체 아니요 실패 소유 Task의 회귀 수정 goal 생성
7 P1-R1 REV-P1-001, P1-GATE 아니요 Community 원문 직렬화 계약 재확인
8 P1-R2 REV-P1-002, P1-R1 아니요 PRD 성공 기준과 Gate 증거 재대조
P1-T1 → P1-T2 → P1-T3 → P1-T4 → P1-T5 → P1-GATE → P1-R1 → P1-R2

변경 금지 항목

  • 확정된 PRD·API Contract의 endpoint, method, field type과 nullable 의미를 근거 없이 바꾸지 않는다.
  • 기존 완료 체크박스와 Progress·Decision Log·검증 기록을 삭제하거나 덮어쓰지 않는다.
  • 생성, 비활성화, 고정, 순서, 삭제 payload를 이번 최적화에 합치지 않는다.
  • 수정 화면에 없는 server field를 기준값 보존 명목으로 request에 복사하지 않는다.
  • 범용 deep-equality dependency, form framework 또는 전역 diff store를 추가하지 않는다.
  • test를 삭제·skip·완화하거나 type assertion으로 오류를 우회하지 않는다.
  • token, 파일 본문, signed URL과 관리자 입력 전문을 log·fixture·문서에 기록하지 않는다.

의사결정 및 중단 규칙

  • PRD와 API Contract가 충돌하면 field type·nullable·transport는 정식 OpenAPI, payload 선택은 이 기능 API Contract를 따른다.
  • 현재 form이 기존 상세 DTO에 없는 수정 field를 노출하면 추정 비교하지 않고 해당 field의 근거를 문서화한 뒤 진행한다.
  • request schema가 빈 object를 거부하거나 backend가 optional field 생략을 현재 값 유지로 처리하지 않으면 구현을 중단하고 외부 의존을 PRD → API Contract → plan 순서로 기록한다.
  • 범위가 바뀌면 plan-task.md 체크박스와 Files·Interfaces를 먼저 갱신한 뒤 코드를 수정한다.
  • 같은 차단 사유가 최초 시도와 자동 후속을 포함해 3회 연속 반복되고 독립 작업도 불가능할 때만 goal을 blocked로 갱신한다.
  • 코드와 일부 test만 끝난 상태에서는 goal을 완료하지 않는다. TDD·검증·Progress 증거까지 충족한 뒤 complete로 갱신한다.
  • 완료된 Task의 후속 결함은 기존 Task를 다시 열지 않고 P1-R1부터 회귀 수정 goal을 추가한다.

Progress

기존 기록을 삭제하거나 덮어쓰지 않고 실제 실행 결과를 차수별로 누적한다.

문서 준비 — 2026-08-06

  • 상태: 완료
  • 무엇을: PRD, API Contract, 5개 구현 Task와 Phase Gate를 작성했다.
  • 왜: 다섯 수정 기능의 변경 field 전송과 무변경 A안의 실행 기준을 고정하기 위해서다.
  • TDD 예외 사유: 이 단계는 실행 코드를 변경하지 않는 요구사항·계약·계획 문서 작성이다.
  • 어떻게:
    • 코드 조사: 다섯 form·API·schema·test와 정식 OpenAPI의 update schema를 대조했다.
    • 인터뷰: 사용자가 무변경 상태의 저장 button disabled·request 0건인 A안을 확정했다.
    • 세 문서의 미정 표현·placeholder 정적 검사 — 출력 없음, exit 0.
    • 세 문서와 정식 OpenAPI 대상 test -f — 누락 없음, exit 0.
    • PRD 필수 18개 section과 plan 필수 12개 section rg 검사 — 모두 확인, exit 0.
    • git diff --check -- docs/20260806_수정요청변경필드만전송 — 출력 없음, exit 0.
  • 남은 항목: P1-T1~P1-T5, P1-GATE 구현·검증.
  • 다음 행동: P1-T1의 Character RED test 작성.

구현 Goal 기록 형식

각 Goal 실행 후 아래 항목을 복제하고 실제 값으로 채운다.

  • 상태: 진행 중 / 완료 / 차단 감사 중 / 차단
  • 무엇을: 완료한 체크박스와 산출물
  • 왜: Task objective와 요구사항 ID
  • TDD:
    • RED: 실패 test 명령, exit code와 의도한 assertion
    • GREEN: 같은 focused 명령, exit code와 통과 test 수
    • REFACTOR: focused·도메인 회귀 명령, exit code와 통과 test 수
  • 어떻게: typecheck·lint·build·수동 검증의 성공·실패·불가 사유
  • 남은 항목: 미완료 체크박스 또는 없음
  • 다음 행동: 같은 goal의 가장 작은 미완료 단계 또는 다음 Goal ID

P1-T1 Character 변경 field 직렬화 — 2026-08-06

  • 상태: 완료
  • 무엇을: 캐릭터 수정의 direct field, optional field, nullable 삭제, image-only {}, 무변경·원복 disabled를 변경 field 전송 계약으로 바꿨다.
  • 왜: CHAR-001, DIFF-001~005, FILE-001, DATA-001~002를 충족하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/characters/tests/CharacterEditPage.test.tsx src/features/characters/tests/character-api.test.ts — exit 1. 의도한 실패 7개: name 단일 변경에 미변경 key 포함, 최초·원복 저장 button enabled, optional 변경에 미변경 key 포함, image-only request가 {}가 아님.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 21 tests 통과.
    • REFACTOR: npm run test:run -- src/features/characters — exit 0, 6 files / 46 tests 통과.
  • 어떻게:
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
    • API multipart helper와 create serializer는 변경하지 않았다.
    • 수동 Network 확인은 P1-GATE에서 다섯 기능과 함께 진행한다.
  • 남은 항목: P1-T2~P1-T5, P1-GATE.
  • 다음 행동: P1-T2의 Audio RED test 작성.

P1-T1 Review Blocker 수정 — 2026-08-06

  • 상태: 완료
  • 무엇을: direct field 기준값에도 동일한 trim() 직렬화 규칙을 적용했다.
  • 왜: 리뷰에서 조회값이 공백을 포함하면 최초 진입부터 변경으로 판정되는 DIFF-002, DIFF-004 위반 가능성이 확인됐다.
  • TDD:
    • RED: npm run test:run -- src/features/characters/tests/CharacterEditPage.test.tsx src/features/characters/tests/character-api.test.ts — exit 1. CharacterEditPage compares direct fields after applying the same trim rule 1개 실패, 최초 저장 button enabled.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 22 tests 통과.
    • REFACTOR: npm run test:run -- src/features/characters — exit 0, 6 files / 47 tests 통과.
  • 어떻게:
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
    • direct field 변경 판정과 submit request 모두 같은 trim 기준값을 사용한다.
  • 남은 항목: P1-T2~P1-T5, P1-GATE.
  • 다음 행동: P1-T1 리뷰 재확인 후 P1-T2의 Audio RED test 작성.

P1-T2 Audio 변경 field 직렬화 — 2026-08-06

  • 상태: 완료
  • 무엇을: 오디오 콘텐츠 수정의 title, detail, tags, price 변경 field와 cover-only {} 전송, 무변경·cover 취소 disabled를 구현했다.
  • 왜: AUDIO-001, DIFF-001~005, FILE-001, DATA-001를 충족하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/audio-contents/tests/audio-form-update.test.tsx src/features/audio-contents/tests/audio-contract.test.ts — exit 1. 의도한 실패 5개: 무변경 저장 button enabled, 화면 밖 boolean과 미변경 tags 포함, cover-only request가 {}가 아님, cover 취소·오류 후 저장 enabled.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 24 tests 통과.
    • REFACTOR: npm run test:run -- src/features/audio-contents — exit 0, 9 files / 71 tests 통과.
  • 어떻게:
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
    • create upload path와 deactivate {isActive:false} 계약은 변경하지 않았다.
    • 수동 Network 확인은 P1-GATE에서 다섯 기능과 함께 진행한다.
  • 남은 항목: P1-T3~P1-T5, P1-GATE.
  • 다음 행동: P1-T2 리뷰 확인 후 P1-T3의 Community RED test 작성.

P1-T2 Review Blocker 수정 — 2026-08-06

  • 상태: 완료
  • 무엇을: edit 저장 가능 여부를 가격 문자열 비교가 아니라 정규화된 toUpdateRequest 결과와 cover 변경에서 계산하도록 보정했다.
  • 왜: 리뷰에서 01000처럼 파싱 결과가 기존 가격과 같은 입력이 빈 PUT을 만들 수 있는 DIFF-002, DIFF-004 위반 가능성이 확인됐다.
  • TDD:
    • RED: npm run test:run -- src/features/audio-contents/tests/audio-form-update.test.tsx src/features/audio-contents/tests/audio-contract.test.ts — exit 1. AudioContentFormPage disables edit save when price formatting normalizes to the original value 1개 실패, 저장 button enabled.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 25 tests 통과.
    • REFACTOR: npm run test:run -- src/features/audio-contents — exit 0, 9 files / 72 tests 통과.
  • 어떻게:
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
    • invalid price는 기존처럼 저장 button을 눌러 validation 오류를 표시할 수 있게 유지했다.
  • 남은 항목: P1-T3~P1-T5, P1-GATE.
  • 다음 행동: P1-T2 리뷰 재확인 후 P1-T3의 Community RED test 작성.

P1-T3 Community 변경 field 직렬화 — 2026-08-06

  • 상태: 완료
  • 무엇을: 커뮤니티 게시글 일반 수정의 content, isAdult, isCommentAvailable 변경 field와 image-only {} 전송, 무변경 저장 차단을 구현했다.
  • 왜: COMM-001, DIFF-001~005, FILE-001, DATA-001를 충족하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/community-posts/tests/community-sheet.test.tsx src/features/community-posts/tests/community-contract.test.ts — exit 1. 의도한 실패 3개: 일반 수정 request에 isFixed 포함, 최초 저장 button enabled, image-only request가 {}가 아님.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 26 tests 통과.
    • REFACTOR: npm run test:run -- src/features/community-posts — exit 0, 7 files / 51 tests 통과.
  • 어떻게:
    • CommunityPostSheet의 일반 저장 request와 저장 disabled가 같은 변경 판정을 사용한다.
    • 고정 전환과 soft delete 전용 request는 기존 계약대로 분리해 유지했다.
    • 수동 Network 확인은 P1-GATE에서 다섯 기능과 함께 진행한다.
  • 남은 항목: P1-T4, P1-T5, P1-GATE.
  • 다음 행동: P1-T4의 Series RED test 작성.

P1-T4 Series 변경 field 직렬화 — 2026-08-06

  • 상태: 완료
  • 무엇을: 시리즈 수정의 기본 field, nullable writer/studio, enum·요일 변경 field와 image-only {} 전송, 무변경 저장 차단을 구현했다.
  • 왜: SERIES-001, DIFF-001~005, FILE-001, DATA-001~002를 충족하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/series/tests/series-form.test.tsx src/features/series/tests/series-contract.test.ts src/features/series/tests/series-update-invariants.test.ts — exit 1. 의도한 실패 5개: enum 변경에 미변경 field 포함, title 단일 변경에 미변경 field 포함, 최초 저장 button enabled, writer 삭제 외 field 포함, image-only request가 {}가 아님.
    • GREEN: 같은 focused 명령 — exit 0, 3 files / 21 tests 통과.
    • REFACTOR: npm run test:run -- src/features/series — exit 0, 9 files / 42 tests 통과.
  • 어떻게:
    • SeriesForm의 edit request와 저장 disabled·dirty 판정이 같은 변경 결과를 사용한다.
    • create path, deactivate {isActive:false}, order/content APIs는 변경하지 않았다.
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
    • 수동 Network 확인은 P1-GATE에서 다섯 기능과 함께 진행한다.
  • 남은 항목: P1-T5, P1-GATE.
  • 다음 행동: P1-T5의 FanTalk RED test 작성.

P1-T5 FanTalk 답글 무변경 수정 차단 — 2026-08-06

  • 상태: 완료
  • 무엇을: FanTalk 답글 수정에서 기존 content와 동일하거나 원복된 상태의 저장 button을 disabled 처리하고, 변경된 content만 PUT으로 전송하게 했다.
  • 왜: FANTALK-001, DIFF-001, DIFF-004를 충족하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/fan-talks/tests/fan-talk-reply.test.tsx src/features/fan-talks/tests/fan-talk-contract.test.ts — exit 1. 의도한 실패 1개: 기존 답글 open 직후 답변 수정 button enabled.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 11 tests 통과.
    • REFACTOR: npm run test:run -- src/features/fan-talks — exit 0, 4 files / 15 tests 통과.
  • 어떻게:
    • FanTalkReplySheet가 edit 변경 여부를 계산하고 FanTalkReplyForm에 submit disabled prop으로 전달한다.
    • create mode의 빈값 validation, pending single-flight, {content} update body와 원글 삭제 계약은 유지했다.
    • npx tsc --noEmit --project tsconfig.test.json --pretty false — 출력 없음, exit 0.
  • 남은 항목: P1-GATE.
  • 다음 행동: Phase 1 Gate 검증 실행.

P1-GATE Phase 1 통합 검증 — 2026-08-06

  • 상태: 완료
  • 무엇을: 다섯 수정 기능의 변경 field payload, 무변경 저장 차단, 기존 mutation 회귀를 통합 검증했다.
  • 왜: Phase 1 완료 조건과 DIFF-001~005, FILE-001, DATA-001~002 적용 결과를 확인하기 위해서다.
  • TDD:
    • RED: P1-T1~P1-T5 각 Goal에서 focused 실패를 확인했다.
    • GREEN: 각 Goal의 focused 명령이 모두 exit 0으로 통과했다.
    • REFACTOR: 각 domain 회귀와 Phase Gate 통합 회귀를 실행했다.
  • 어떻게:
    • npm run test:run -- src/features/characters src/features/audio-contents src/features/community-posts src/features/series src/features/fan-talks — exit 0, 35 files / 227 tests 통과.
    • npm run e2e:mock -- tests/e2e/character-workspace.spec.ts tests/e2e/audio-content.spec.ts tests/e2e/community.spec.ts tests/e2e/series.spec.ts tests/e2e/fan-talk.spec.ts --project=chromium — exit 0, 39 tests 통과.
    • npm run typecheck — exit 0.
    • npm run lint — exit 0.
    • npm run build — exit 0, production build 완료.
    • git diff --check — 출력 없음, exit 0.
    • 수동 Network 항목은 동일 payload 계약을 검증하는 focused/component test와 mock Chromium E2E로 대체 확인했다.
  • 남은 항목: 없음.
  • 다음 행동: 최종 변경 요약과 검증 결과 보고.

P1-R1 Community content 직렬화 비교 일치 — 2026-08-06

  • 상태: 완료
  • 무엇을: Community update가 전송하는 raw content와 변경 판정을 일치시키고 공백 변경 회귀 test를 추가했다.
  • 왜: REV-P1-001DIFF-001, DIFF-002, DIFF-004, COMM-001 위반을 수정하기 위해서다.
  • TDD:
    • RED: npm run test:run -- src/features/community-posts/tests/community-sheet.test.tsx src/features/community-posts/tests/community-contract.test.ts — exit 1, 1 failed / 26 passed. Community Sheet preserves whitespace-only content changes에서 저장 button이 disabled인 의도한 실패를 확인했다.
    • GREEN: 같은 focused 명령 — exit 0, 2 files / 27 tests 통과.
    • REFACTOR: npm run test:run -- src/features/community-posts — exit 0, 7 files / 52 tests 통과. 새 abstraction 없이 비교식 1줄만 수정했다.
  • 어떻게:
    • npm run test:run -- src/features/characters src/features/audio-contents src/features/community-posts src/features/series src/features/fan-talks — exit 0, 35 files / 228 tests 통과.
    • npm run e2e:mock -- tests/e2e/character-workspace.spec.ts tests/e2e/audio-content.spec.ts tests/e2e/community.spec.ts tests/e2e/series.spec.ts tests/e2e/fan-talk.spec.ts --project=chromium — sandbox 실행은 127.0.0.1:8889 bind EPERM으로 불가했고, 승인된 동일 명령 재실행은 exit 0, 39 tests 통과.
    • npm run typecheck — exit 0.
    • npm run lint — exit 0.
    • npm run build — exit 0, production build 완료.
    • git diff --check — 출력 없음, exit 0.
    • 실서버 Network 확인은 인증 환경이 없어 실행하지 않았고 component multipart assertion과 mock Chromium E2E로 대체했다.
  • 남은 항목: 없음.
  • 다음 행동: 최종 리뷰 결과 보고.

P1-R2 PRD 성공 기준 상태 동기화 — 2026-08-06

  • 상태: 완료
  • 무엇을: Phase Gate와 회귀 검증 증거에 따라 PRD §14 성공 기준 13개를 완료 상태로 동기화했다.
  • 왜: REV-P1-002의 PRD·plan 완료 상태 불일치를 수정하기 위해서다.
  • TDD 예외 사유: 실행 코드가 아닌 완료 상태 문서 동기화다.
  • 어떻게:
    • rg -n "^- \\[x\\]" docs/20260806_수정요청변경필드만전송/prd.md — §14 완료 항목 13개 확인, exit 0.
    • rg -n "^- \\[ \\]" docs/20260806_수정요청변경필드만전송/prd.md — §18 요구사항 변경 체크리스트 5개만 유지, exit 0.
    • 대상 PRD·API Contract·plan·review test -f — 누락 없음, exit 0.
    • git diff --check — 출력 없음, exit 0.
  • 남은 항목: 없음.
  • 다음 행동: 최종 리뷰 결과 보고.

Decision Log

날짜 ID 상태 결정 근거 영향 Goal/문서
2026-08-06 DEC-001 확정 기존 PUT·multipart·JSON transport를 유지하고 최종 변경 field만 보낸다. 사용자 요청, OpenAPI update field optional 계약 P1-T1~P1-T5, API Contract §1
2026-08-06 DEC-002 확정 변경 field·file 0개면 저장 button disabled와 mutation 0건으로 처리한다. 사용자 인터뷰 A안 P1-T1~P1-T5
2026-08-06 DEC-003 확정 file-only multipart는 required request part를 {}로 보낸다. multipart contract와 빈 update object 허용 schema P1-T1~P1-T4
2026-08-06 DEC-004 확정 payload와 disabled 판정은 같은 domain request 결과를 사용한다. 원복·trim·nullable 판정 불일치 방지 P1-T1~P1-T5
2026-08-06 DEC-005 확정 domain-local serializer를 사용하고 새 공통 diff abstraction을 만들지 않는다. field별 nullable·array·file 의미 차이와 최소 변경 원칙 P1-T1~P1-T5
2026-08-06 DEC-006 확정 Community update의 content는 기존 원문 전송을 유지하고 현재 값과 기준값도 원문으로 비교한다. DIFF-002의 같은 기존 직렬화 적용, 기존 update가 trim 없이 전송한 코드 P1-R1, REV-P1-001

발견된 문제

ID 심각도 상태 발견 내용 영향 Goal 처리 계획
ISSUE-001 High 확정 Character edit가 direct·optional·repeated 현재값 전체를 request에 넣는다. P1-T1 기준값 비교 serializer와 no-op test 추가
ISSUE-002 High 확정 Audio edit가 수정 화면에 없는 boolean을 포함한 전체 update DTO를 보낸다. P1-T2 화면 field만 비교·전송
ISSUE-003 High 확정 Community 일반 수정이 미변경 isFixed와 나머지 편집값을 함께 보낸다. P1-T3 일반 수정 request와 전용 action 유지
ISSUE-004 High 확정 Series는 state만 미변경 생략하고 나머지 edit field는 모두 보낸다. P1-T4 기존 state 원본 비교를 전체 edit field로 확장
ISSUE-005 Medium 확정 FanTalk reply는 update field가 content 하나라 변경 payload는 이미 최소지만 무변경 수정 요청을 차단하지 않는다. P1-T5 edit submit disabled와 request 0건 test 추가
ISSUE-006 High 해결 Community update가 raw content를 전송하면서 변경 판정만 trim해 공백 변경을 누락한다. P1-R1 REV-P1-001 수정·검증 완료
ISSUE-007 Low 해결 Phase 1 구현·Gate 완료 후에도 PRD §14 성공 기준이 미완료로 남아 있다. P1-R2 REV-P1-002 수정·검증 완료

최종 보고 형식

구현 결과: 다섯 수정 기능의 변경 field 전송과 무변경 저장 차단

- 변경: domain별 serializer·form·test와 실제 payload
- 결정: DEC-001~005 적용 결과
- 검증:
  - focused·도메인 회귀·mock Chromium E2E — 성공/실패와 test 수
  - typecheck·lint·build·git diff --check — exit code와 핵심 결과
  - Network 수동 검증 — 단일 field, nullable, false·0, file-only, 무변경 결과
- 남은 항목: 외부 의존, 후속 회귀 또는 없음
- 문서: PRD, API Contract, plan, review 링크

최종 보고는 성공을 추정하지 않고 실제 최신 검증 결과와 완료되지 않은 범위를 함께 기록한다.