From f26713e0ea99e7de62a0d6dce92139d6bb479f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=9B=90=EC=97=BD?= Date: Fri, 29 May 2026 14:51:46 +0900 Subject: [PATCH] . --- LOG.md | 4 ++++ src/api/publicPosts.ts | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/LOG.md b/LOG.md index 6ec1f66..113bfcf 100644 --- a/LOG.md +++ b/LOG.md @@ -2,6 +2,10 @@ ## 2026-05-29 +- Fixed post detail 404s caused by double-encoding dynamic route slugs that already arrive percent-encoded from URLs such as `/posts/soft-delete%2C-hard-delete`. +- Updated the public post fetch helper to decode a route slug once before encoding it for the backend API path, preserving normal Korean/special-character slug requests while avoiding `%252C`-style API lookups. +- Validation: `npm run lint` passed and `npm run build` passed. A local production server with `NEXT_PUBLIC_API_URL=https://blogserver.wypark.me` returned real post titles for `/posts/soft-delete%2C-hard-delete`, `/posts/destructuring-%26-component%ED%95%A8%EC%88%98`, and `/posts/rtr-(refresh-token-rotation)`. +- Recommended next task: deploy the slug normalization fix before re-checking affected live post URLs in Search Console or the browser. - Added a category post-list page size control with 9/18/27 options, defaulting to 9 and persisting the user's choice in `localStorage` under `categoryPageSize`. - Wired the selected category page size into the React Query key and `getPostsByCategory` request size, resetting to page 1 when the size changes while preserving search behavior. - Adjusted the category page toolbar so search, page-size controls, and grid/list controls stack cleanly on narrow screens and align in one row on desktop. diff --git a/src/api/publicPosts.ts b/src/api/publicPosts.ts index 36635b7..41488b6 100644 --- a/src/api/publicPosts.ts +++ b/src/api/publicPosts.ts @@ -28,6 +28,14 @@ const buildPostListUrl = (params: PublicPostListParams = {}) => { return `${API_URL}/api/posts?${searchParams.toString()}`; }; +const encodeSlugPathSegment = (slug: string) => { + try { + return encodeURIComponent(decodeURIComponent(slug)); + } catch { + return encodeURIComponent(slug); + } +}; + export const fetchPublicPosts = async ( params: PublicPostListParams = {}, ): Promise => { @@ -50,7 +58,7 @@ export const fetchPublicPosts = async ( export const fetchPublicPost = async (slug: string): Promise => { try { - const response = await fetch(`${API_URL}/api/posts/${encodeURIComponent(slug)}`, { + const response = await fetch(`${API_URL}/api/posts/${encodeSlugPathSegment(slug)}`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, next: { revalidate: 60 }, @@ -65,4 +73,3 @@ export const fetchPublicPost = async (slug: string): Promise => { return null; } }; -