diff --git a/.agent/GEMINI.md b/.agent/GEMINI.md new file mode 100644 index 0000000..6eca5c4 --- /dev/null +++ b/.agent/GEMINI.md @@ -0,0 +1,87 @@ +# Project Guidelines & Rules + +## 1. Project Overview & Tech Stack +- **Description:** A modern blog application using a Notion database as a Headless CMS, built with Next.js. +- **Framework:** Next.js 16.1.1 (App Router) +- **UI Library:** React 19 +- **Language:** TypeScript (Strict Mode) +- **Styling:** Tailwind CSS 4, `class-variance-authority`, `clsx`, `lucide-react`. (**Do not create .css files**; use utility classes). +- **CMS/Database:** Notion API (`@notionhq/client`) serving as the primary data source. +- **Testing:** Vitest (Unit), Playwright (E2E), Testing Library. +- **Package Manager:** `pnpm` + +## 2. AI Behavior Guidelines +- **Language:** Always provide explanations and responses in **Korean**. However, write code comments, variable names, and commit messages in **English**. +- **Reasoning:** Before writing code, always output the **"Reasoning Process"**. If the modification is extensive, present a **"Plan"** first and await user approval. +- **Code Quality:** Prefer clear, multi-line code over unreadable one-liners. Remove unused variables and imports immediately. +- **Error Handling:** When an error occurs, do not just fix it; summarize the **root cause** in one line. If a terminal command fails, stop the process immediately. +- **Documentation:** Every works must be documented in /docs folder. There are several types of documents: + - **/architectures** (for used architecture of the project) + - **/fixes** (for fixing bugs) + - **/plans** (for planning features) + - **/tests** (for testing features) + - ... (if you need to add more documents, add them and save them in /docs folder) + - **CHANGELOG.md** (always update this file when you make a change) +- **Workflow Automation Rule:** + - When a task is deemed complete or the user indicates completion, do not commit immediately; **always initiate the `@finish` workflow.** + - Enforce the **[Code Review] -> [Request Approval] -> [Commit]** procedure. Do not suggest `git commit` directly. + +## 3. Server vs. Client Strategy (Architecture) +- **Default to Server:** All components are **Server Components** by default. +- **Client Boundary:** Use `'use client'` only when interactivity (`useState`, `onClick`, `useEffect`) is strictly needed. +- **Data Fetching:** - Fetch data directly in Server Components using `async/await`. + - **Do not** use `useEffect` for data fetching unless absolutely necessary. + - **Caching:** Notion API calls must be cached for **6 hours (21600s)** using `unstable_cache` to minimize API usage. +- **Mutations:** Use **Server Actions** for any data mutations or form submissions. Do not use `pages/api` (API Routes). + +## 4. Coding Standards + +### 4.1 Naming & Structure +- **Components:** `PascalCase` (e.g., `PostCard.tsx`). +- **Functions/Variables:** `camelCase` (e.g., `getPostData`). +- **Files:** Match the export name or use `kebab-case` for utility files/folders (e.g., `app/dashboard/settings`). +- **Imports:** Use **absolute imports** (`@/components/...`) instead of relative paths. +- **Co-location:** Group related components in subdirectories (e.g., `src/components/notion/`). + +### 4.2 Type Safety +- **No `any`:** The use of `any` is strictly prohibited. Use `unknown` with Type Guards (or Zod) for validation. +- **Strict Mode:** Assume `strict: true`; handle `null` or `undefined` rigorously. +- **Explicit Returns:** Explicitly define the return type for all functions (do not rely on inference). +- **Definitions:** Use `interface` for extensible objects; use `type` for unions/intersections. + +### 4.3 State Management Hierarchy +1. **Server State:** Prefer fetching fresh data on the server (React Query/SWR if client-side fetching is needed). +2. **URL State:** Store filter/search params in the URL (`useSearchParams`) for shareability. +3. **Global State:** Use Context API or Zustand only if prop drilling exceeds 3 levels. + +### 4.4 Error Handling Pattern +- **Server Actions:** Handle errors gracefully using `try/catch` and return structured objects: + ```ts + try { + // logic + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } + return { success: false, error: "Friendly error message" }; + } + ``` + +## 5. Workflows to AVOID ๐Ÿšซ +- No API Routes: Do not use pages/api; use Server Actions. +- No Hardcoded Secrets: Always use process.env. +- No Huge Components: Break down components if they exceed 200 lines. +- No New Packages: Do not install new npm packages without asking for approval first. +- No Prop Drilling: Avoid passing props more than 3 levels deep. + +## 6. Environment & Commands +- Environment Variables: NOTION_API_KEY, NOTION_POSTS_DATA_SOURCE_ID, NOTION_COMMENTS_DATA_SOURCE_ID. +- Commands: + - pnpm dev: Start dev server (http://localhost:3000). + - pnpm build: Production build. + - pnpm test: Run unit tests. + - pnpm test:e2e: Run E2E tests. + +## 7. Documentation Etiquette +- Code Comments: When writing complex logic, write a brief comment explaining "Why" (intent), not "What" (syntax). +- JSDoc: Mandatory for all utility functions. diff --git a/.agent/skills/SKILL.example.md b/.agent/skills/SKILL.example.md new file mode 100644 index 0000000..b726e97 --- /dev/null +++ b/.agent/skills/SKILL.example.md @@ -0,0 +1,23 @@ +--- +name: [์Šคํ‚ฌ ์ด๋ฆ„] +description: [์ด ์Šคํ‚ฌ์ด ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ํ•œ ์ค„ ์š”์•ฝ] +triggers: + - [ํŠธ๋ฆฌ๊ฑฐ ํ‚ค์›Œ๋“œ 1] (์˜ˆ: @notion, @db) + - [ํŠธ๋ฆฌ๊ฑฐ ํ‚ค์›Œ๋“œ 2] +--- + +# [Skill Name] Context +์ด ํŒŒ์ผ์€ [ํŠน์ • ์ž‘์—…]์„ ์ˆ˜ํ–‰ํ•  ๋•Œ AI๊ฐ€ ๋”ฐ๋ผ์•ผ ํ•  ๊ฐ€์ด๋“œ๋ผ์ธ์ž…๋‹ˆ๋‹ค. + +## 1. Goal (๋ชฉํ‘œ) +- ๋ฌด์—‡์„ ๋‹ฌ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ช…ํ™•ํžˆ ์ •์˜ + +## 2. Rules (์ œ์•ฝ ์‚ฌํ•ญ) +- ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ๋“ค + +## 3. Tool Usage (๋„๊ตฌ ์‚ฌ์šฉ๋ฒ•) +- ๊ด€๋ จ๋œ ํ•จ์ˆ˜๋‚˜ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๋ฐฉ๋ฒ• + +## 4. Examples (์˜ˆ์‹œ - ๊ฐ€์žฅ ์ค‘์š” โญ) +User: [์งˆ๋ฌธ ์˜ˆ์‹œ] +AI: [์ด์ƒ์ ์ธ ๋‹ต๋ณ€ ์˜ˆ์‹œ] diff --git a/.agent/skills/code-review/SKILL.md b/.agent/skills/code-review/SKILL.md new file mode 100644 index 0000000..7cffc1b --- /dev/null +++ b/.agent/skills/code-review/SKILL.md @@ -0,0 +1,47 @@ +--- +name: Senior Code Reviewer +description: Analyze code for performance, security, type safety, and Next.js best practices. +triggers: + - @review + - @cr + - "์ฝ”๋“œ ๋ฆฌ๋ทฐํ•ด์ค˜" + - "์ด ์ฝ”๋“œ ๋ฌธ์ œ์  ์žˆ์–ด?" +--- + +# Code Review Skill + +## 1. Context +You are a Senior Full-Stack Engineer reviewing a Pull Request. Your goal is to catch logical errors, performance bottlenecks, and maintainability issues before they merge. + +## 2. Review Checklist (Priority Order) +1. **Server vs Client Boundary:** + - Is `'use client'` used only when necessary? (Interactivity, Hooks) + - Are sensitive environment variables (`process.env.NOTION_API_KEY`) leaking into Client Components? +2. **Performance (Critical):** + - Are Notion API calls wrapped in `unstable_cache`? (Must be cached for 6 hours/21600s) + - Are images using `next/image` with proper `sizes` and `alt` tags? +3. **Type Safety:** + - Are there any explicit `any` types? (Flag immediately as Critical) + - Are API responses validated (e.g., checking if `properties` exist in Notion response)? +4. **Code Style:** + - Is the component too large (>200 lines)? Suggest splitting. + - Are tailwind classes organized? (Suggest `clsx` or `cn` usage for conditionals). + +## 3. Output Format +Provide feedback in the following format: + +- **๐Ÿ”ด Critical:** (Bugs, Security, `any` type, Infinite loops) +- **๐ŸŸก Improvement:** (Performance optimizations, better readability) +- **๐ŸŸข Good:** (Praise for good patterns) + +## 4. Example Interaction +**User:** "Review this code: `const data = await fetch(url).then(res => res.json())`" +**AI:** +"**๐Ÿ”ด Critical:** +1. **Promise Handling:** Use `async/await` instead of `.then()` chains for readability. +2. **Type Safety:** The return type is implicit `any`. Define an interface for the response. +3. **Error Handling:** There is no `try/catch` or error checking. + +**๐ŸŸก Improvement:** +- If this is a Server Component, consider wrapping `fetch` with `unstable_cache` if the data doesn't change often." + \ No newline at end of file diff --git a/.agent/skills/git-manager/SKILL.md b/.agent/skills/git-manager/SKILL.md new file mode 100644 index 0000000..a886e83 --- /dev/null +++ b/.agent/skills/git-manager/SKILL.md @@ -0,0 +1,42 @@ +--- +name: Git Commit Manager +description: Generate semantic commit messages and manage branches. +triggers: + - @git + - @wt + - @cm + - "commit message" + - "์ปค๋ฐ‹ํ•ด์ค˜" +--- + +# Git Manager Skill + +## 1. Context & Resources +- **Role:** You are an expert Git manager utilizing **Worktrees** for parallel development. +- **Reference:** For specific commands (especially Worktree & Bare Repo setup), **ALWAYS refer to `./cheatsheet.md`** located in the same directory. + +## 2. Commit Message Convention +Follow the **Conventional Commits** format: +- `feat`: New features (blog post rendering, new API route) +- `fix`: Bug fixes (hydration error, layout shift) +- `docs`: Documentation changes +- `style`: Code changes without logic change +- `refactor`: Code changes without logic change +- `test`: Test changes +- `chore`: Config changes, dependency updates + +## 3. Workflow +1. Analyze the `git diff` output. +2. Summarize changes in English (imperative mood). +3. If the change is huge, suggest splitting the commit. +4. Consult `./cheatsheet.md` for exact syntax. + +## 4. Example +**Input:** Changed the header background color and added a logo. +**Output:** +```bash +style: update header design with new logo + +- Change background color to neutral-900 +- Add Logo component to navigation +``` \ No newline at end of file diff --git a/.agent/skills/git-manager/cheatsheet.md b/.agent/skills/git-manager/cheatsheet.md new file mode 100644 index 0000000..de59287 --- /dev/null +++ b/.agent/skills/git-manager/cheatsheet.md @@ -0,0 +1,81 @@ +# ๋ช…๋ น์–ด,์„ค๋ช… + +```bash +git config --global user.name "์ด๋ฆ„" # ์‚ฌ์šฉ์ž ์ด๋ฆ„ ์„ค์ • +git config --global user.email "์ด๋ฉ”์ผ" # ์‚ฌ์šฉ์ž ์ด๋ฉ”์ผ ์„ค์ • +git init # ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ Git ์ €์žฅ์†Œ๋กœ ์ดˆ๊ธฐํ™” +git clone --bare .bare # ์›ŒํฌํŠธ๋ฆฌ ์‚ฌ์šฉ์„ ์œ„ํ•œ Bare Clone +``` + +## ์›ŒํฌํŠธ๋ฆฌ +์›ŒํฌํŠธ๋ฆฌ ์‚ฌ์šฉ์„ ์œ„ํ•œ ๋ช…๋ น์–ด +root ํด๋”์— .bare ํด๋”๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ทธ ์•ˆ์— ์›ŒํฌํŠธ๋ฆฌ ํด๋”๋ฅผ ์ƒ์„ฑ +**ํ•ญ์ƒ root ํด๋”์—์„œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•จ** + +```bash + +git -C .bare worktree list # ํ˜„์žฌ ์ƒ์„ฑ๋œ ๋ชจ๋“  ์›ŒํฌํŠธ๋ฆฌ(์ž‘์—… ํด๋”) ๋ชฉ๋ก ํ™•์ธ +git -C .bare worktree add ../<ํด๋”๋ช…> <๋ธŒ๋žœ์น˜๋ช…> # ์ƒˆ ๋ธŒ๋žœ์น˜๋ฅผ ๋”ฐ๋ฉด์„œ ์ƒˆ ํด๋” ์ƒ์„ฑ +git -C .bare worktree add ../<ํด๋”๋ช…> # ๊ธฐ์กด ๋ธŒ๋žœ์น˜๋ฅผ ์ƒˆ ํด๋”๋กœ ์ฒดํฌ์•„์›ƒ +git -C .bare worktree remove <ํด๋”๋ช…> # ์ž‘์—… ํด๋” ์‚ญ์ œ (Git ์—ฐ๊ฒฐ ํ•ด์ œ) +git -C .bare worktree prune # ํด๋”๋ฅผ ๊ฐ•์ œ ์‚ญ์ œ(rm -rf)ํ–ˆ์„ ๋•Œ ์ฐŒ๊บผ๊ธฐ ์ •๋ฆฌ +git -C .bare worktree move <๊ตฌํด๋”> ../<์‹ ํด๋”> # ์›ŒํฌํŠธ๋ฆฌ ํด๋” ๊ฒฝ๋กœ/์ด๋ฆ„ ๋ณ€๊ฒฝ +``` + +## ์ปค๋ฐ‹ + +```bash +git status # ํ˜„์žฌ ํŒŒ์ผ ์ƒํƒœ(๋ณ€๊ฒฝ๋จ, ์Šคํ…Œ์ด์ง•๋จ) ํ™•์ธ +git add . # ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์Šคํ…Œ์ด์ง• ์˜์—ญ(Staging Area)์— ์ถ”๊ฐ€ +git add <ํŒŒ์ผ> # ํŠน์ • ํŒŒ์ผ๋งŒ ์Šคํ…Œ์ด์ง• +git commit -m "๋ฉ”์‹œ์ง€" # ์Šคํ…Œ์ด์ง•๋œ ๋ณ€๊ฒฝ์‚ฌํ•ญ ํ™•์ •(์ €์žฅ) +git commit --amend # ๋ฐฉ๊ธˆ ํ•œ ์ปค๋ฐ‹์˜ ๋ฉ”์‹œ์ง€๋‚˜ ํŒŒ์ผ ์ˆ˜์ • (๋ฎ์–ด์“ฐ๊ธฐ) +``` + +## ๋ธŒ๋žœ์น˜ + +```bash +git branch # ๋กœ์ปฌ ๋ธŒ๋žœ์น˜ ๋ชฉ๋ก ํ™•์ธ +git branch -r # ์›๊ฒฉ ๋ธŒ๋žœ์น˜ ๋ชฉ๋ก ํ™•์ธ +git branch <์ด๋ฆ„> # ์ƒˆ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ (์ด๋™์€ ์•ˆ ํ•จ) +git branch -m <์ƒˆ์ด๋ฆ„> # ํ˜„์žฌ ๋ธŒ๋žœ์น˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ +git branch -d <์ด๋ฆ„> # ๋ธŒ๋žœ์น˜ ์‚ญ์ œ (๋ณ‘ํ•ฉ๋œ ๊ฒƒ๋งŒ) +git branch -D <์ด๋ฆ„> # ๋ธŒ๋žœ์น˜ ๊ฐ•์ œ ์‚ญ์ œ +git switch <๋ธŒ๋žœ์น˜> # ํ•ด๋‹น ๋ธŒ๋žœ์น˜๋กœ ์ด๋™ +git switch -c <์ด๋ฆ„> # ์ƒˆ ๋ธŒ๋žœ์น˜ ๋งŒ๋“ค๋ฉด์„œ ์ด๋™ +``` + +## ์›๊ฒฉ ์ €์žฅ์†Œ + +```bash +git fetch # ์›๊ฒฉ ์ €์žฅ์†Œ์˜ ์ตœ์‹  ์ด๋ ฅ๋งŒ ๊ฐ€์ ธ์˜ด (๋ณ‘ํ•ฉ X) +git pull origin <๋ธŒ๋žœ์น˜> # ์›๊ฒฉ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€์„œ ํ•ฉ์นจ (Fetch + Merge) +git push origin <๋ธŒ๋žœ์น˜> # ๋‚ด ์ปค๋ฐ‹์„ ์›๊ฒฉ ์ €์žฅ์†Œ์— ์˜ฌ๋ฆผ +git push -u origin <๋ธŒ๋žœ์น˜> # ์—…์ŠคํŠธ๋ฆผ ์„ค์ • (๋‹ค์Œ๋ถ€ํ„ด git push๋งŒ ํ•ด๋„ ๋จ) +git merge <๋ธŒ๋žœ์น˜> # ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜๋ฅผ ํ˜„์žฌ ๋ธŒ๋žœ์น˜๋กœ ํ•ฉ์นจ +git rebase <๋ธŒ๋žœ์น˜> # ๋‚ด ๋ธŒ๋žœ์น˜์˜ ์‹œ์ž‘์ ์„ ํƒ€๊ฒŸ ๋ธŒ๋žœ์น˜ ๋์œผ๋กœ ์˜ฎ๊น€ (๊น”๋”ํ•œ ํžˆ์Šคํ† ๋ฆฌ) +``` + +## ๋ณต๊ตฌ + +```bash +git restore <ํŒŒ์ผ> # ์ž‘์—… ์ค‘์ธ ํŒŒ์ผ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ทจ์†Œ (๋งˆ์ง€๋ง‰ ์ปค๋ฐ‹ ์ƒํƒœ๋กœ) +git restore --staged <ํŒŒ์ผ> # git add ์ทจ์†Œ (์Šคํ…Œ์ด์ง• ๋‚ด๋ฆฌ๊ธฐ) +git reset --soft HEAD~1 # ์ปค๋ฐ‹์€ ์ทจ์†Œํ•˜๋˜, ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ์Šคํ…Œ์ด์ง• ์ƒํƒœ๋กœ ๋ณด์กด +git reset --hard HEAD~1 # ์ปค๋ฐ‹๊ณผ ๋ณ€๊ฒฝ์‚ฌํ•ญ ๋ชจ๋‘ ๋‚ ๋ ค๋ฒ„๋ฆผ (๋ณต๊ตฌ ๋ถˆ๊ฐ€) +git revert <์ปค๋ฐ‹ID> # ํŠน์ • ์ปค๋ฐ‹์˜ ๋‚ด์šฉ์„ ๋ฐ˜๋Œ€๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ์ƒˆ ์ปค๋ฐ‹ ์ƒ์„ฑ (ํ˜‘์—… ์‹œ ์•ˆ์ „) +``` + +## ์ž„์‹œ ์ €์žฅ +์ž„์‹œ ์ €์žฅ ํ•˜์ง€ ์•Š์Œ. ์›ŒํฌํŠธ๋ฆฌ ์‚ฌ์šฉ. + +## ํžˆ์Šคํ† ๋ฆฌ + +```bash +git log # ์ปค๋ฐ‹ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ +git log --oneline --graph # ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๊ทธ๋ž˜ํ”„ ํ˜•ํƒœ๋กœ ํ•œ ์ค„ ์š”์•ฝํ•ด์„œ ๋ณด๊ธฐ +git diff # ์Šคํ…Œ์ด์ง•๋˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ์‚ฌํ•ญ ํ™•์ธ +git show <์ปค๋ฐ‹ID> # ํŠน์ • ์ปค๋ฐ‹์˜ ์ƒ์„ธ ๋ณ€๊ฒฝ ๋‚ด์šฉ ํ™•์ธ +git blame <ํŒŒ์ผ> # "ํŒŒ์ผ์˜ ๊ฐ ๋ผ์ธ์„ ๋ˆ„๊ฐ€, ์–ธ์ œ ์ˆ˜์ •ํ–ˆ๋Š”์ง€ ๋ฒ”์ธ(?) ์ฐพ๊ธฐ" +git bisect # ์ด์ง„ ํƒ์ƒ‰์œผ๋กœ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•œ ์ปค๋ฐ‹ ์ฐพ์•„๋‚ด๊ธฐ +``` diff --git a/.agent/skills/notion-cms/SKILL.md b/.agent/skills/notion-cms/SKILL.md new file mode 100644 index 0000000..6b4af05 --- /dev/null +++ b/.agent/skills/notion-cms/SKILL.md @@ -0,0 +1,47 @@ +--- +name: Notion CMS Handler +description: Manage Notion API interactions, database queries, and data fetching logic. +triggers: + - @notion + - @cms + - "fetch post" + - "๋…ธ์…˜" +--- + +# Notion CMS Skill + +## 1. Context +You are a Notion API expert for a Next.js blog. You handle data fetching from the Notion Database ID provided in `.env`. + +## 2. Critical Rules +1. **Caching:** Must use `unstable_cache` for all `notion.databases.query` calls. Revalidate time is 3600s. +2. **SDK:** Use `@notionhq/client`. +3. **Transformer:** Always transform the raw Notion response into a simplified `Post` interface before returning it to the component. Do not leak raw Notion blocks to the UI. +4. **Filter:** Only fetch posts where `status` is 'Published'. +5. **Document:** Read the Notion API documentation for the latest information on properties and methods. https://developers.notion.com/reference/intro +6. **Postman:** Use Postman to test the Notion API. https://postman.com/notionhq/workspace/notion-s-api-workspace + +## 3. Code Pattern (Copy this style) +```typescript +import { Client } from "@notionhq/client"; +import { unstable_cache } from "next/cache"; + +const notion = new Client({ auth: process.env.NOTION_API_KEY }); + +export const getPosts = unstable_cache( + async () => { + const response = await notion.databases.query({ + database_id: process.env.NOTION_POSTS_DATA_SOURCE_ID, + filter: { + property: "", + select: { + equals: "", + }, + }, + }); + return response.results; + }, + ["posts"], + { revalidate: 3600 } +); +``` diff --git a/.agent/skills/test-engineer/SKILL.md b/.agent/skills/test-engineer/SKILL.md new file mode 100644 index 0000000..06516a7 --- /dev/null +++ b/.agent/skills/test-engineer/SKILL.md @@ -0,0 +1,31 @@ +--- +name: Test Engineer +description: Write and debug Unit (Vitest) and E2E (Playwright) tests. +triggers: + - @test + - "ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•ด์ค˜" + - "์—๋Ÿฌ ๊ณ ์ณ์ค˜" +--- + +# Test Engineer Skill + +## 1. Role +You are a QA Engineer specialized in Vitest and Playwright. + +## 2. File Placement Rules +- **Unit Tests:** Place in `__tests__/unit/{filename}.test.tsx` +- **E2E Tests:** Place in `__tests__/e2e/{feature}.spec.ts` + +## 3. Writing Strategy +- **Unit:** Do not test implementation details. Test behaviors (inputs/outputs). Use `screen.getByRole` for accessibility compliance. +- **E2E:** Always capture screenshots on failure (`screenshot: 'only-on-failure'`). Mock external API calls (Notion) using `page.route` to avoid hitting real limits. +- **CI/CD:** Ensure `pnpm test` and `pnpm test:e2e` pass before committing. + +## 4. Command Reference +- Run Unit: `pnpm test` +- Run E2E: `pnpm test:e2e` +- Debug Mode: `pnpm test:ui` + +## 5. Example Interaction +User: "Create a test for the Comment component." +AI: "Created `__tests__/unit/Comment.test.tsx`. I mocked the submission API to prevent network requests. Would you like me to run `pnpm test` now?" diff --git a/.agent/skills/ui/SKILL.md b/.agent/skills/ui/SKILL.md new file mode 100644 index 0000000..7bca4a0 --- /dev/null +++ b/.agent/skills/ui/SKILL.md @@ -0,0 +1,25 @@ +--- +name: UI/UX Designer +description: Design accessible, responsive, and aesthetic components using Tailwind CSS v4. +triggers: + - @ui + - @design + - "๋””์ž์ธํ•ด์ค˜" + - "์˜ˆ์˜๊ฒŒ ๋งŒ๋“ค์–ด์ค˜" +--- + +# UI/UX Design Skill + +## 1. Context +You are a Product Designer specialized in Tailwind CSS v4 and Headless UI systems. You prioritize "Mobile First", "Dark Mode", and "Accessibility (a11y)". + +## 2. Design Principles +- **Mobile First:** Always write base classes for mobile, then `md:`, `lg:` for larger screens. +- **Dark Mode Support:** Every color class must have a `dark:` counterpart. (e.g., `bg-white dark:bg-neutral-950`). +- **Interaction:** Add states for `hover:`, `focus-visible:` (for keyboard nav), and `active:`. +- **Spacing:** Use consistent spacing (multiples of 4). `p-4`, `gap-6`, `my-8`. + +## 3. Tech Constraints +- **Library:** Use `lucide-react` for icons. +- **Utils:** Use `cn` (from `lib/utils`) for class merging. +- **Tailwind v4:** Do not use `config` based arbitrary values if possible. Use standard utility classes. diff --git a/.agent/skills/workflows/SKILL.md b/.agent/skills/workflows/SKILL.md new file mode 100644 index 0000000..feb6bc2 --- /dev/null +++ b/.agent/skills/workflows/SKILL.md @@ -0,0 +1,55 @@ +--- +name: Task Finisher Workflow +description: Automate the Code Review -> Confirmation -> Git Commit pipeline. +triggers: + - @finish + - @done + - "์ž‘์—… ์™„๋ฃŒ" + - "๋งˆ๋ฌด๋ฆฌํ•ด์ค˜" +--- + +# Task Finisher Workflow + +## 1. Context +You are the **Workflow Orchestrator**. Your goal is to ensure high code quality before finalizing any changes. You act as a bridge between the `@review` skill and the `@git` skill. + +## 2. Execution Steps (Follow Strictly) + +### Step 1: Pre-Commit Review (Invoke Code-Reviewer) +1. Read all modified files (`git diff`). +2. **Act as the `@review` agent:** + - Check for `any` types, missing `unstable_cache`, and logic errors. + - Check if strictly necessary `test` files exist (if logic was changed). +3. **Report:** + - If **Critical Issues** are found ๐Ÿ”ด -> **STOP** and ask the user to fix them. + - If only **Minor Issues** or **Good** ๐ŸŸข -> Present the summary and ask: + > "๋ฆฌ๋ทฐ ๊ฒฐ๊ณผ๊ฐ€ ์–‘ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ์ปค๋ฐ‹ ๋ฉ”์‹œ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ง„ํ–‰ํ• ๊นŒ์š”?" + +### Step 2: User Confirmation (Wait) +- **DO NOT** generate the commit command yet. +- Wait for the user to reply "Yes", "Proceed", or "ใ…‡ใ…‡". + +### Step 3: Commit Generation (Invoke Git-Manager) +1. Once confirmed, **Act as the `@git` agent:** + - Generate a specific `git commit` command following Conventional Commits. + - Provide the command for the user to run (or run it if permissions allow). + +## 3. Example Interaction + +**User:** `@finish` (Auth login implementation) + +**AI:** +"**๐Ÿ”Ž Code Review Summary:** +- ๐ŸŸข **Security:** `process.env` is properly used. +- ๐ŸŸก **Suggestion:** Consider extracting the validation logic to `lib/auth.ts`. +- โœ… **Tests:** Unit tests passed. + +์ฝ”๋“œ๊ฐ€ ์•ˆ์ •์ ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ปค๋ฐ‹์„ ์ง„ํ–‰ํ• ๊นŒ์š”?" + +**User:** "์‘ ์ง„ํ–‰ํ•ด" + +**AI:** +"**๐Ÿš€ Git Commit:** +```bash +git add . +git commit -m "feat: implement auth login with validation" diff --git a/.env.example b/.env.example index 26fcefe..49831fa 100644 --- a/.env.example +++ b/.env.example @@ -4,5 +4,5 @@ NOTION_POSTS_DATA_SOURCE_ID= NOTION_COMMENTS_DATA_SOURCE_ID= GA_ID= GTM_ID= -GOOGLE_ADSENSE_ID= GOOGLE_ADSENSE_ACCOUNT= +GOOGLE_ADSENSE_ID= diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index ad8e050..d10a8b7 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -13,6 +13,7 @@ env: NOTION_COMMENTS_DATA_SOURCE_ID: ${{ secrets.NOTION_COMMENTS_DATA_SOURCE_ID }} GA_ID: ${{ secrets.GA_ID }} GTM_ID: ${{ secrets.GTM_ID }} + GOOGLE_ADSENSE_ACCOUNT: ${{ secrets.GOOGLE_ADSENSE_ACCOUNT }} GOOGLE_ADSENSE_ID: ${{ secrets.GOOGLE_ADSENSE_ID }} VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/README.md b/README.md index 4b40589..004b5c9 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,8 @@ # Google Services (Optional) GA_ID=G-XXXXXX GTM_ID=GTM-XXXXXX - GOOGLE_ADSENSE_ID=ca-pub-XXXXXXXXXXXXXXXX + GOOGLE_ADSENSE_ACCOUNT=ca-pub-XXXXXXXXXXXXXXXX + GOOGLE_ADSENSE_ID=pub-XXXXXXXXXXXXXXXX ``` 4. **Run development server** diff --git a/package.json b/package.json index 8f07629..d8f2a09 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "@next/third-parties": "^16.1.1", "@notionhq/client": "^5.6.0", + "@vercel/analytics": "^1.6.1", "@vercel/speed-insights": "^1.3.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccd4d17..8fb897b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@notionhq/client': specifier: ^5.6.0 version: 5.6.0 + '@vercel/analytics': + specifier: ^1.6.1 + version: 1.6.1(next@16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) '@vercel/speed-insights': specifier: ^1.3.1 version: 1.3.1(next@16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) @@ -1325,6 +1328,32 @@ packages: cpu: [x64] os: [win32] + '@vercel/analytics@1.6.1': + resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==} + peerDependencies: + '@remix-run/react': ^2 + '@sveltejs/kit': ^1 || ^2 + next: '>= 13' + react: ^18 || ^19 || ^19.0.0-rc + svelte: '>= 4' + vue: ^3 + vue-router: ^4 + peerDependenciesMeta: + '@remix-run/react': + optional: true + '@sveltejs/kit': + optional: true + next: + optional: true + react: + optional: true + svelte: + optional: true + vue: + optional: true + vue-router: + optional: true + '@vercel/speed-insights@1.3.1': resolution: {integrity: sha512-PbEr7FrMkUrGYvlcLHGkXdCkxnylCWePx7lPxxq36DNdfo9mcUjLOmqOyPDHAOgnfqgGGdmE3XI9L/4+5fr+vQ==} peerDependencies: @@ -4099,6 +4128,11 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@vercel/analytics@1.6.1(next@16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)': + optionalDependencies: + next: 16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + '@vercel/speed-insights@1.3.1(next@16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)': optionalDependencies: next: 16.1.1(@babel/core@7.28.5)(@playwright/test@1.57.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index 097d6c3..09ec35c 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -5,12 +5,12 @@ import { getMessages } from 'next-intl/server'; import { notFound } from 'next/navigation'; import { routing } from '@/i18n/routing'; import { SpeedInsights } from "@vercel/speed-insights/next" -import { GoogleTagManager } from '@next/third-parties/google'; +import { GoogleTagManager, GoogleAnalytics } from '@next/third-parties/google'; +import { Analytics } from "@vercel/analytics/next" import { ThemeProvider } from "@/components/utils/ThemeProvider"; -import { GoogleAnalytics } from "@/components/delegator/GoogleAnalytics"; -import { GoogleAdSense } from "@/components/delegator/GoogleAdSense"; import { ErrorBoundary } from "@/components/ErrorBoundary"; +import { GoogleAdSense } from "@/components/delegator/GoogleAdSense"; import "../globals.css"; @@ -80,9 +80,9 @@ export default async function LocaleLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} suppressHydrationWarning > + + - - {children} - + + );