diff --git a/.claude/commands/purge-cache.md b/.claude/commands/purge-cache.md new file mode 100644 index 0000000..139b198 --- /dev/null +++ b/.claude/commands/purge-cache.md @@ -0,0 +1,35 @@ +--- +description: 로컬(.next)과 Vercel의 Data/CDN 캐시를 제거해 stale 데이터를 해소 +--- + +로컬과 Vercel의 캐시를 제거한다. Sanity Studio에서 직접 편집한 내용이 사이트에 반영 안 될 때 사용. + +**인자**: `$ARGUMENTS` — `local` | `vercel` | 비어있으면 둘 다. + +## 절차 + +### 로컬 (`local` 또는 인자 없음) + +1. `rm -rf .next` 실행 — Next.js Data Cache는 `.next/cache/fetch-cache`에 파일로 저장되므로 dev 서버 재시작만으로는 사라지지 않는다. +2. 실행 후 사용자에게 안내: **dev 서버가 돌고 있었다면 재시작 필요** (`pnpm dev`). Claude가 띄운 서버가 아니면 직접 재시작을 요청한다. + +### Vercel (`vercel` 또는 인자 없음) + +프로젝트는 `.vercel/project.json`으로 이미 연결되어 있다 (`ram-blog-jul5`). + +```bash +vercel cache purge --type data --yes +vercel cache purge --type cdn --yes +``` + +- Data Cache: `client.fetch`의 `force-cache` 결과 (Sanity 응답) — stale의 주범 +- CDN: Full Route Cache로 페이지 자체가 남아있을 수 있어 함께 제거 + +### 마무리 + +- 두 명령의 성공 출력(`Successfully purged ...`)을 확인하고 결과를 한 줄로 보고 +- 실패 시(로그인 만료 등) `vercel whoami`로 인증 상태를 확인하고 사용자에게 `vercel login` 안내 + +## 배경 + +읽기 서비스가 전부 태그 기반 `force-cache`인데, `revalidateTag`는 앱 API mutation에서만 호출된다. Sanity Studio 직접 편집은 어떤 revalidate도 트리거하지 않아 환경별 캐시가 stale해진다 (근본 해결은 Sanity 웹훅 → `/api/revalidate`, 백로그). diff --git a/sanity-studio/schemas/user.js b/sanity-studio/schemas/user.js index cce077c..6548f09 100644 --- a/sanity-studio/schemas/user.js +++ b/sanity-studio/schemas/user.js @@ -33,11 +33,6 @@ export default { name: 'image', type: 'string', }, - { - title: 'Blog Name', - name: 'blogName', - type: 'string', - }, { title: 'Profile Title', name: 'title', diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx index 6a8231d..279f70f 100644 --- a/src/components/common/Header.tsx +++ b/src/components/common/Header.tsx @@ -59,7 +59,7 @@ export default function Header() { {params.user ? (

- {userProfile?.blogName} + {userProfile?.title}

) : ( diff --git a/src/components/setting/ProfileForm.tsx b/src/components/setting/ProfileForm.tsx index 83e8579..0b66002 100644 --- a/src/components/setting/ProfileForm.tsx +++ b/src/components/setting/ProfileForm.tsx @@ -85,6 +85,7 @@ export default function ProfileForm({ userData }: Props) { .then(() => { router.refresh(); mutate(`/api/${userData.slug}/me`); + mutate(`/api/${userData.slug}`); router.push(`/${userData.slug}`); }) .catch((err) => setError(err.toString())) diff --git a/src/model/user.ts b/src/model/user.ts index ff3b964..722954e 100644 --- a/src/model/user.ts +++ b/src/model/user.ts @@ -16,9 +16,7 @@ export type AuthUser = { image?: string; }; -export type UserData = AuthUser & { - blogName: string; -}; +export type UserData = AuthUser; export type SimpleUser = { id: string; @@ -32,7 +30,6 @@ export type SimpleUser = { export type ProfileUser = AuthUser & { following: number; followers: number; - blogName: string; title: string; introduce: string; links: Links; @@ -40,7 +37,6 @@ export type ProfileUser = AuthUser & { }; export type HomeUser = AuthUser & { - blogName: string; title: string; introduce: string; links: Links; diff --git a/src/sanity/types.ts b/src/sanity/types.ts index 453ce00..2ded9cd 100644 --- a/src/sanity/types.ts +++ b/src/sanity/types.ts @@ -245,7 +245,6 @@ export type User = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -884,7 +883,6 @@ export type CheckUsernameValidQueryResult = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -931,7 +929,6 @@ export type CheckSlugValidQueryResult = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -978,7 +975,6 @@ export type CheckEmailValidQueryResult = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -1026,14 +1022,13 @@ export type LoginWithEmailQueryResult = { // Query: *[_type=='user' && _id == $id][0].slug export type UserSlugByIdQueryResult = string | null; // Variable: userDataQuery -// Query: *[_type=='user' && _id == $userId][0] {"id":_id,name,username,email,image,blogName} +// Query: *[_type=='user' && _id == $userId][0] {"id":_id,name,username,email,image} export type UserDataQueryResult = { id: string; name: string | null; username: string | null; email: string | null; image: string | null; - blogName: string | null; } | null; // Variable: userBySlugQuery // Query: *[_type == "user" && slug == $slug][0]{ ..., "id":_id, following[]->{"id":_id,username,slug,name,image,title}, followers[]->{"id":_id,username,slug,name,image,title}, "bookmarks":bookmarks[]->_id } @@ -1049,7 +1044,6 @@ export type UserBySlugQueryResult = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -1093,7 +1087,6 @@ export type UserForProfileQueryResult = { email?: string; password?: string; image?: string; - blogName?: string; title?: string; introduce?: string; links?: { @@ -1143,7 +1136,7 @@ declare module '@sanity/client' { "\n *[_type=='user' && email == $email][0]\n": CheckEmailValidQueryResult; '\n *[_type==\'user\' && email == $email][0]{\n "id":_id,\n email,\n name,\n username,\n slug,\n password,\n image,\n }\n': LoginWithEmailQueryResult; "\n *[_type=='user' && _id == $id][0].slug\n": UserSlugByIdQueryResult; - '\n *[_type==\'user\' && _id == $userId][0]\n {"id":_id,name,username,email,image,blogName}\n': UserDataQueryResult; + '\n *[_type==\'user\' && _id == $userId][0]\n {"id":_id,name,username,email,image}\n': UserDataQueryResult; '\n *[_type == "user" && slug == $slug][0]{\n ...,\n "id":_id,\n following[]->{"id":_id,username,slug,name,image,title},\n followers[]->{"id":_id,username,slug,name,image,title},\n "bookmarks":bookmarks[]->_id\n }\n': UserBySlugQueryResult; '\n *[_type==\'user\' && slug == $slug][0]{\n ...,\n "id":_id,\n "following":count(following),\n "followers":count(followers),\n "posts":count(*[_type=="post" && author->slug == $slug])\n }\n': UserForProfileQueryResult; } diff --git a/src/service/user.ts b/src/service/user.ts index 26b434e..413a375 100644 --- a/src/service/user.ts +++ b/src/service/user.ts @@ -33,7 +33,7 @@ const userSlugByIdQuery = defineQuery(` const userDataQuery = defineQuery(` *[_type=='user' && _id == $userId][0] - {"id":_id,name,username,email,image,blogName} + {"id":_id,name,username,email,image} `); const userBySlugQuery = defineQuery(` @@ -72,7 +72,6 @@ export async function addUser({ email, name, image, - blogName: name, title: `${name}`, introduce: `안녕하세요 ${name}의 멋진 블로그입니다 :)`, following: [], @@ -101,7 +100,6 @@ export async function addEmailUser({ password, name, image: '', - blogName: name, title: `${name}`, introduce: `안녕하세요 ${name}의 멋진 블로그입니다 :)`, following: [], @@ -200,7 +198,7 @@ export async function getUserBySlug(slug: string): Promise { { slug }, { cache: 'force-cache', - next: { tags: ['following', 'bookmark'] }, + next: { tags: ['following', 'bookmark', `profile/${slug}`] }, } ); @@ -213,7 +211,6 @@ export async function getUserBySlug(slug: string): Promise { slug: user.slug ?? '', email: user.email ?? '', image: user.image, - blogName: user.blogName ?? '', title: user.title ?? '', introduce: user.introduce ?? '', links: user.links ?? defaultLinks, @@ -246,7 +243,6 @@ export async function getUserForProfile( image: user.image, following: user.following ?? 0, followers: user.followers ?? 0, - blogName: user.blogName ?? '', title: user.title ?? '', introduce: user.introduce ?? '', links: user.links ?? defaultLinks,