Skip to content

feat(editorial,web): Public reader 페이지 /magazine/[slug] (Track 4 of #448) #550

@cocoyoon

Description

@cocoyoon

Parent: #448

Context

공개 매거진 reader UI 가 없음 — 백엔드 read endpoint 는 이미 있음:

  • packages/api-server/src/domains/editorial_articles_published/handlers.rs:134 (/api/v1/editorial-articles/{id_or_slug})
  • operation DB editorial_articles snapshot 을 SELECT

남은 작업은 Next.js route + SSR + OG meta + sitemap 추가. Track 1/2/3 와 독립적으로 진행 가능.

Scope

web (신규)

  • packages/web/app/magazine/[slug]/page.tsx — Server Component, Next.js fetch w/ cache: "force-cache" + revalidate: 300. slug 에 UUID 도 허용 (API 가 둘 다 받음)
  • packages/web/app/magazine/[slug]/not-found.tsx
  • packages/web/app/magazine/page.tsx — 목록 페이지, published 매거진 grid + 페이지네이션 (/api/v1/editorial-articles?page 활용)
  • packages/web/lib/server/published-articles.ts — server-only fetch helper
  • packages/web/lib/hooks/usePublishedArticle.ts — public Tanstack Query hook (admin variant 와 별개)
  • packages/web/app/sitemap.ts — 기존 sitemap 에 published articles 추가

web (이동/수정)

  • packages/web/lib/components/admin/editorial/magazine/MagazineRenderer.tsxpackages/web/lib/components/magazine/MagazineRenderer.tsx이동 — admin/public 양쪽이 동일 컴포넌트 import
  • packages/web/app/admin/editorial/magazine/drafts/[id]/page.tsx:23 — import 경로 수정

api-server

변경 없음.

재사용

  • MagazineRenderer 그대로 (이동만)
  • PublishedArticleDetail Rust struct — wire shape 동일
  • MagazineLayout TS 타입

DB migration

없음. editorial_articles_slug_idx 이미 존재.

slug 채움 보강 (별도 후속 PR): publish_to_operation 시 slug 가 NULL 로 들어감 (editorial_articles.rs:372). title → slugify 로직 추가 (Rust slug crate). 이번 issue 에서는 [slug] 가 UUID 도 받게 두고 enrichment 는 후속.

Sub-PR 분할

PR-3a-1: MagazineRenderer 이동 (refactor only)

admin 측 import 경로 변경. 동작 변경 X.

PR-3a-2: reader detail page

/magazine/[slug]/page.tsx + generateMetadata (OG meta) + not-found + fetch helper.

PR-3a-3: list + sitemap

/magazine 목록 + sitemap + 공유 헤더.

Test

  • Manual gstack: dev 에 published article 만들고 localhost:3000/magazine/{uuid} 접근, OG meta 는 view-source 로 확인
  • Unit: generateMetadata 결과 (Vitest, mocked fetch)
  • E2E: 별도 Playwright 셋업 확인 후 추가

Risk

  1. MagazineRenderer"use client" (MagazineRenderer.tsx:1) — Server Component 에서 import 가능하지만 hydration cost. VTON 버튼이 client-only 라 그대로 유지 (대안: VTON 분리 — 이번 trade-off 측면에서 유지)
  2. Cache invalidation on republish — 300s ISR 로 시작. 사용자 불만 시 webhook (revalidatePath) 추가
  3. Slug uniqueness — slug enrichment 는 후속 PR. 충돌 시 suffix

Out of scope

  • Slug enrichment (publish 시 title → slug 채움)
  • View / share 카운트 트래킹
  • Comment / like 기능

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions