Skip to content

feat: PostListItem에 user_has_saved 계약 + 카드 소비 (#840)#872

Draft
thxforall wants to merge 2 commits into
devfrom
feat/840-user-has-saved
Draft

feat: PostListItem에 user_has_saved 계약 + 카드 소비 (#840)#872
thxforall wants to merge 2 commits into
devfrom
feat/840-user-has-saved

Conversation

@thxforall

Copy link
Copy Markdown
Contributor

What & why

user_has_saved 필드를 PostListItem API 계약(utoipa)에 추가한다 (PRD #825, map #789). 카드 저장상태가 서버 진실과 일치하도록 하는 contract-first 슬라이스 — Rust 계약+population은 kiyori 작성, philippe 정본 빌드/리뷰.

  • Rust: PostListItem.user_has_saved: Option<bool> (utoipa). list_postsviewer_id 있을 때만 saved_posts 배치 조회(IN 1쿼리, N+1 회피)로 채움. 게스트/admin 목록은 None. 시맨틱=기존 PostDetailResponse.user_has_saved와 동일(null=게스트/미계산, bool=서버진실).
  • openapi.json 재생성(dump_openapi) → generate:api로 FE 클라이언트에 필드 전파(생성물 gitignore, 수동편집 없음).
  • FE: SaveLookButtoninitialSaved prop → 리스트 모델의 user_has_saved로 카드 초기 저장상태 seed. HomeLookCard.userHasSaved + toLookCard 배선.

⚠️ 핵심 스코프 결정 — AC1은 authed 리스트 충족 / 홈은 partial (client-hydration 후속)

홈 룩 카드(SaveLookButton의 주 서식지)는 공유캐시(revalidate:60) + 비인증(serverApiGet이 쿠키/토큰 미전달) 피드로 렌더된다. 따라서 per-user user_has_saved는 홈 캐시드 응답에 실을 수 없다(항상 None이거나 유저간 누출).

→ 이 필드의 실제 near-term 소비처는 authed·uncached Library(#852, blocked-by #840). 홈 카드의 저장상태 반영은 **client-side hydration(별도 후속)**이 필요하며, 이 PR은 계약+population+최소 소비배선까지만 담고 홈 갭을 코드 주석 4곳에 명시했다(dto.rs 필드doc, SaveLookButton.tsx, types.ts, page.tsx). 무회귀 — 홈 카드는 오늘과 동일하게 미저장 fallback + optimistic.

Acceptance (#840)

  • [~] AC1 카드 저장 토글이 새로고침 후 서버상태 일치 — authed/uncached 리스트 충족, 홈(캐시드) partial(위 참조, 정직 문서화)
  • AC2 user_has_saved가 OpenAPI(utoipa) 계약에 명시 → 재생성 클라이언트 소비
  • AC3 Rust 변경 philippe 리뷰 — 리뷰 요청(process gate)
  • AC4 generated 코드는 generate:api로만 갱신(gitignore, 수동편집 없음)

Verification

  • Rust TDD@seam (GREEN): list_posts_user_has_saved_{true_when_viewer_saved, none_for_guest} — viewer→Some(bool) / 게스트→None. 기존 list_posts 테스트 전부 무회귀.
  • FE seam 테스트 (3 GREEN): SaveLookButton initialSaved→렌더/aria.
  • tsc --noEmit 0 · prettier 3.8.1 클린 · eslint 클린 (변경 파일).
  • mp-review 2축: Standards 하드위반 0(§4 no-unwrap · §5 KISS · N+1 회피 · utoipa leaf) · Spec 충실·블로킹 갭 0(AC1 partial 정직문서화 검증 · viewer 서버주입 무누출).

⚠️ 리뷰어 유의 (환경 / 사전존재 — #840 무관)

  1. 로컬 컴파일 우회: repo가 rustc 1.91 핀(rust-toolchain.toml)인데 현재 aws-sdk 의존성이 1.94.1 MSRV를 요구 + Cargo.lock gitignore라 fresh resolve 시 최신 aws-sdk를 끌어와 cargo check가 MSRV 게이트에서 실패한다. 로컬 검증은 --ignore-rust-version로 우회(코드는 1.91 호환, 게이트만 우회) — philippe 정본 빌드 검증 필요. 이 툴체인 vs dep 불일치는 별도 인프라 이슈.
  2. 사전존재 dev 테스트 실패(내 변경 무관): tests::architecture::files_should_not_exceed_line_budget(dto.rs 836>800, 변경 전부터) · domain_dependencies_match_allowed_list(새 도메인 엣지 없음, posts→saved_posts는 기존 import) · all_handlers_registered_in_openapi(핸들러 0 추가) · tests/integration_feed.rs(사전 E0061 시그니처 불일치) · grpc 테스트(라이브서버 필요).
  3. README 스탯 미갱신: AGENT.md §1 라인카운트 bump은 dev의 자체 README 유지(2 커밋)와 충돌하여 이 PR에서 제외.

Base = dev. Closes #840.

thxforall and others added 2 commits July 9, 2026 23:21
카드 저장상태 정합용 `user_has_saved: Option<bool>` 를 PostListItem(utoipa)에
추가하고, list_posts 에서 viewer_id 있을 때만 saved_posts 배치 조회(IN 1쿼리,
N+1 회피)로 채운다. 게스트/admin 목록은 None. openapi.json 재생성(dump_openapi).

- saved_posts::service::saved_post_ids_for_user 신규(post_ids IN + user 필터)
- 시맨틱=PostDetailResponse.user_has_saved 동일(null=게스트/미계산, bool=서버진실)
- TDD: list_posts_user_has_saved_{true_when_viewer_saved,none_for_guest} GREEN
- 공유캐시 공개 피드(홈)엔 per-user 못 실음 → authed/uncached 리스트(#852)용

⚠️ 로컬 컴파일=`--ignore-rust-version` 우회(repo rustc 1.91 핀 vs aws-sdk 1.94.1
MSRV, Cargo.lock gitignore) — Rust는 philippe 정본 빌드/리뷰 필요.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
SaveLookButton 에 initialSaved prop 추가 → 리스트 모델의 user_has_saved 로
카드 초기 저장상태를 seed. HomeLookCard.userHasSaved + toLookCard 배선.
공유캐시 공개 홈 피드엔 값이 없어(viewer_id 없음) 미저장 fallback —
홈 카드 저장상태 반영은 client-hydration 후속(#840). 무회귀.

- FE seam 테스트: SaveLookButton initialSaved → 렌더/aria 정합 (3 GREEN)
- generated 클라이언트(gitignore)는 generate:api 로만 갱신(수동편집 없음)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@thxforall thxforall added the bump:patch Bug fixes / internal refactor label Jul 9, 2026
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
decoded-app Ready Ready Preview, Comment Jul 9, 2026 2:24pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bump:patch Bug fixes / internal refactor

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant