-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: apply Vercel React best practices improvements #268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
756d1db
81d5fff
270e0d8
124e12a
6406bbf
25d519d
4d983ef
ce21694
776f2ad
48aa553
91028de
985b027
50eab9a
9792ddb
d771d8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # Claude Command: Commit | ||
|
|
||
| This command helps you create well-formatted commits with conventional commit messages. | ||
|
|
||
| ## Usage | ||
|
|
||
| To create a commit, just type: | ||
|
|
||
| ``` | ||
| /commit | ||
| ``` | ||
|
|
||
| Or with options: | ||
|
|
||
| ``` | ||
| /commit --no-verify | ||
| ``` | ||
|
|
||
| ## What This Command Does | ||
|
|
||
| 1. Checks which files are staged with `git status` | ||
| 2. If 0 files are staged, automatically adds all modified and new files with `git add` | ||
| 3. Performs a `git diff` to understand what changes are being committed | ||
| 4. Analyzes the diff to determine if multiple distinct logical changes are present | ||
| 5. If multiple distinct changes are detected, suggests breaking the commit into multiple smaller commits | ||
| 6. For each commit (or the single commit if not split), creates a commit message using conventional commit format | ||
|
|
||
| ## Best Practices for Commits | ||
|
|
||
| - **Verify before committing**: Ensure code is linted, builds correctly, and documentation is updated | ||
| - **Atomic commits**: Each commit should contain related changes that serve a single purpose | ||
| - **Split large changes**: If changes touch multiple concerns, split them into separate commits | ||
| - **Conventional commit format**: Use the format `<type>: <description>` or `<type>(scope): <description>` where type is one of: | ||
| - `feat`: A new feature | ||
| - `fix`: A bug fix | ||
| - `docs`: Documentation changes | ||
| - `style`: Code style changes (formatting, etc) | ||
| - `refactor`: Code changes that neither fix bugs nor add features | ||
| - `test`: Adding or fixing tests | ||
| - `chore`: Changes to the build process, tools, etc. | ||
| - `ci`: CI/CD related changes | ||
| - `build`: Build system or external dependency changes | ||
| - `revert`: Reverting a previous commit | ||
| - `ai`: AI agent related changes (rules, skills, etc.) | ||
| - **Present tense, imperative mood**: Write commit messages as commands (e.g., "add feature" not "added feature") | ||
| - **Concise first line**: Keep the first line under 72 characters | ||
| - **No emoji**: Do not use emoji in commit messages | ||
|
|
||
| ## Guidelines for Splitting Commits | ||
|
|
||
| When analyzing the diff, consider splitting commits based on these criteria: | ||
|
|
||
| 1. **Different concerns**: Changes to unrelated parts of the codebase | ||
| 2. **Different types of changes**: Mixing features, fixes, refactoring, etc. | ||
| 3. **File patterns**: Changes to different types of files (e.g., source code vs documentation) | ||
| 4. **Logical grouping**: Changes that would be easier to understand or review separately | ||
| 5. **Size**: Very large changes that would be clearer if broken down | ||
|
|
||
| ## Examples | ||
|
|
||
| Good commit messages (single line): | ||
|
|
||
| - feat: add user authentication system | ||
| - fix: resolve memory leak in rendering process | ||
| - feat(auth): add OAuth2 login support | ||
| - fix(api): handle null response from server | ||
|
|
||
| Good commit messages with body (use '-' bullet points): | ||
|
|
||
| ``` | ||
| refactor(design): separate corner radius extraction from stroke pipeline | ||
|
|
||
| - Extract corner radius logic into dedicated CornerExtractor class | ||
| - Create CornerNormalizer for corner normalization | ||
| - Promote corner to top-level style property | ||
| - Fix paint-level alias to only apply to solid paints | ||
| ``` | ||
|
|
||
| - docs: update API documentation with new endpoints | ||
| - refactor: simplify error handling logic in parser | ||
| - chore: improve developer tooling setup process | ||
| - style: reorganize component structure for better readability | ||
| - ci: add GitHub Actions workflow for testing | ||
| - build: upgrade webpack to v5 | ||
| - revert: undo breaking change in user service | ||
| - ai: add claude command for commit automation | ||
|
|
||
| Example of splitting commits: | ||
|
|
||
| - First commit: feat: add new solc version type definitions | ||
| - Second commit: docs: update documentation for new solc versions | ||
| - Third commit: chore: update package.json dependencies | ||
| - Fourth commit: test: add unit tests for new solc version features | ||
| - Fifth commit: fix: update dependencies with security vulnerabilities | ||
|
|
||
| ## Command Options | ||
|
|
||
| - `--no-verify`: Skip running the pre-commit checks (lint, build, generate:docs) | ||
|
|
||
| ## Important Notes | ||
|
|
||
| - **lint-staged runs automatically**: When committing, lint-staged will run linting and formatting checks on staged files | ||
| - **If lint-staged fails, the commit will be rejected**: You cannot proceed with the commit until the issues are fixed | ||
| - **On failure, immediately explain the cause**: If the commit fails due to lint-staged, analyze the error output and explain what went wrong and how to fix it | ||
| - If specific files are already staged, the command will only commit those files | ||
| - If no files are staged, it will automatically stage all modified and new files | ||
| - The commit message will be constructed based on the changes detected | ||
| - Before committing, the command will review the diff to identify if multiple commits would be more appropriate | ||
| - If suggesting multiple commits, it will help you stage and commit the changes separately | ||
| - Always reviews the commit diff to ensure the message matches the changes | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,247 @@ | ||||||||||||||||||||||||||||||
| # JECT-5-FE 성능 리팩토링 계획 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **분석 대상**: `/Users/jh/JECT-5-FE/service/app` | ||||||||||||||||||||||||||||||
| **분석 기준**: Vercel React Best Practices Guide | ||||||||||||||||||||||||||||||
| **분석 일자**: 2026-01-17 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 📊 Executive Summary | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| 총 **7개 이슈** 발견 → **7개 적용 권장** | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 예상 성능 개선 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| | 지표 | 현재 | 개선 후 | 개선율 | | ||||||||||||||||||||||||||||||
| |------|------|---------|--------| | ||||||||||||||||||||||||||||||
| | **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% | | ||||||||||||||||||||||||||||||
| | **Store 조회 복잡도** | O(n) | O(1) | -99% | | ||||||||||||||||||||||||||||||
| | **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 | | ||||||||||||||||||||||||||||||
| | **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 | | ||||||||||||||||||||||||||||||
| | **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 | | ||||||||||||||||||||||||||||||
|
Comment on lines
+15
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 개선율 수치 표현을 명확히 해주세요. Line 17의 "Tree-shaking 효율" 개선율 "+100%"는 오해의 소지가 있습니다. Tree-shaking 효율은 정량적으로 측정 가능한 퍼센트 지표가 아닙니다. "번들 크기 감소" 또는 "최적화 완료" 같은 정성적 표현이나, 실제 번들 크기를 측정한 후 구체적인 KB/MB 감소량을 표시하는 것이 더 정확합니다. 제안하는 수정안 | 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
-| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | 최적화 완료 |또는 실제 번들 크기를 측정한 경우: -| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **번들 크기 (해당 파일들)** | 측정 필요 | 측정 필요 | -X KB |📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## ✅ 적용 필요 (7개) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### Priority: HIGH (즉시 적용) - 5개 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| | # | 이슈 | 파일 | Vercel Rule | 예상 개선 | 난이도 | | ||||||||||||||||||||||||||||||
| |---|------|------|-------------|-----------|--------| | ||||||||||||||||||||||||||||||
| | **1** | **Namespace imports 제거** | 9개 파일 | 2.1 tree-shaking | Bundle 최적화 | ⭐ 쉬움 | | ||||||||||||||||||||||||||||||
| | **2** | **findIndex → Map 구조** | Zustand stores | 7.2 lookup-optimization | O(n)→O(1) 조회 | ⭐⭐ 보통 | | ||||||||||||||||||||||||||||||
| | **5** | **Radix UI 패키지 통합** | design 컴포넌트들 | 2.1 tree-shaking | 번들 중복 제거 | ⭐ 쉬움 | | ||||||||||||||||||||||||||||||
| | **6** | **Dialog 코드 간소화** | `dialog/` | - | 코드량 감소 | ⭐⭐ 보통 | | ||||||||||||||||||||||||||||||
| | **7** | **useAuthGuard → AuthGuard** | 인증 필요 페이지들 | - | 선언적 패턴 | ⭐ 쉬움 | | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### Priority: MEDIUM (선택적) - 1개 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| | # | 이슈 | 파일 | Vercel Rule | 예상 개선 | 난이도 | | ||||||||||||||||||||||||||||||
| |---|------|------|-------------|-----------|--------| | ||||||||||||||||||||||||||||||
| | **3** | **Suspense fallback 개선** | `games/page.tsx` | 1.5 async-suspense-boundaries | UX 개선 | ⭐ 쉬움 | | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### Priority: 검증 필요 - 1개 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| | # | 이슈 | 파일 | Vercel Rule | 조건 | | ||||||||||||||||||||||||||||||
| |---|------|------|-------------|------| | ||||||||||||||||||||||||||||||
| | **4** | **React.cache() 추가** | `getGameDetail.ts` | 3.4 server-cache-react | 중복 요청 발생 시에만 | | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 🎯 상세 이슈 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## HIGH #1: Namespace imports 제거 📦 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일 (9개)**: | ||||||||||||||||||||||||||||||
| - `service/app/src/app/create/components/questionInputForm.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/create/components/createGameNavigation.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/game/[gameId]/components/teamInputForm.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/games/components/GamesNavigation.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/dashboard/components/dashboardGameSection.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/_components/GameSection/GameSectionClient.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/games/components/gamesLibrarySection.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/entities/game/ui/gameLibraryGrid.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/entities/game/ui/gamePreview.tsx` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ 전체 namespace 번들에 포함 | ||||||||||||||||||||||||||||||
| import * as TextField from "@ject-5-fe/design/components/textField" | ||||||||||||||||||||||||||||||
| import * as GameCard from "@/entities/game/ui/GameCard/gameCard" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| <TextField.Root> | ||||||||||||||||||||||||||||||
| <TextField.Label>질문*</TextField.Label> | ||||||||||||||||||||||||||||||
| <TextField.Input ... /> | ||||||||||||||||||||||||||||||
| </TextField.Root> | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - `import * as`는 tree-shaking 방해 | ||||||||||||||||||||||||||||||
| - 사용하지 않는 컴포넌트도 번들에 포함 | ||||||||||||||||||||||||||||||
| - 9개 파일에서 반복 사용으로 누적 영향 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## HIGH #2: findIndex → Map 구조 🔄 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: | ||||||||||||||||||||||||||||||
| - `service/app/src/app/create/store/useCreateGameStore.tsx` (6곳) | ||||||||||||||||||||||||||||||
| - `service/app/src/app/game/[gameId]/store/useGameStore.tsx` (2곳) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ 매 호출마다 O(n) 검색 | ||||||||||||||||||||||||||||||
| const addQuestion = () => { | ||||||||||||||||||||||||||||||
| const selectedIndex = state.questions.findIndex((q) => q.id === selectedQuestionId) | ||||||||||||||||||||||||||||||
| // ... | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| const moveQuestion = (id, direction) => { | ||||||||||||||||||||||||||||||
| const index = state.questions.findIndex((q) => q.id === id) | ||||||||||||||||||||||||||||||
| // ... | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // 질문 10개 추가 시: 1+2+3+...+10 = 55회 배열 스캔 | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - addQuestion, deleteQuestion, moveQuestion, updateQuestion 등에서 반복 호출 | ||||||||||||||||||||||||||||||
| - 빈번한 사용자 인터랙션마다 O(n) 연산 누적 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## MEDIUM #3: Suspense fallback 개선 💡 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: `service/app/src/app/games/page.tsx` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ null fallback → 로딩 중 빈 화면 | ||||||||||||||||||||||||||||||
| export default function GamesPage() { | ||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||
| <Suspense fallback={null}> | ||||||||||||||||||||||||||||||
| <GamesNavigation /> | ||||||||||||||||||||||||||||||
| </Suspense> | ||||||||||||||||||||||||||||||
| <Suspense fallback={null}> | ||||||||||||||||||||||||||||||
| <GamesLibrarySection /> | ||||||||||||||||||||||||||||||
| </Suspense> | ||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - 로딩 중 사용자에게 아무것도 보이지 않음 | ||||||||||||||||||||||||||||||
| - CLS (Cumulative Layout Shift) 발생 가능 | ||||||||||||||||||||||||||||||
| - UX 저하 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 검증 필요 #4: React.cache() 🔍 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: `service/app/src/entities/game/api/getGameDetail.ts` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ 동일 gameId로 여러 번 호출 시 중복 요청 가능성 | ||||||||||||||||||||||||||||||
| export const getGameDetail = async (gameId: UUID) => { | ||||||||||||||||||||||||||||||
| const response = await fetchClient.get<GameDetailData>(`games/${gameId}`) | ||||||||||||||||||||||||||||||
| return response.json() | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // generateMetadata와 컴포넌트에서 각각 호출 시 2번 요청 가능 | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - generateMetadata + Page 컴포넌트에서 동일 데이터 중복 fetch 가능성 | ||||||||||||||||||||||||||||||
| - 서버 사이드 요청 중복 제거 필요 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## HIGH #5: Radix UI 패키지 중복 사용 📦 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: | ||||||||||||||||||||||||||||||
| - `shared/design/src/components/checkBox/index.tsx` | ||||||||||||||||||||||||||||||
| - `shared/design/src/components/input/index.tsx` | ||||||||||||||||||||||||||||||
| - `shared/design/src/components/radioField/index.tsx` | ||||||||||||||||||||||||||||||
| - `shared/design/src/components/radio/RadioGroup.tsx` | ||||||||||||||||||||||||||||||
| - `shared/design/src/components/radio/RadioItem.tsx` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ 개별 @radix-ui/* 패키지와 통합 radix-ui 패키지 혼용 | ||||||||||||||||||||||||||||||
| import { useControllableState } from "@radix-ui/react-use-controllable-state" | ||||||||||||||||||||||||||||||
| import * as RadixRadioGroup from "@radix-ui/react-radio-group" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| // 다른 파일에서는 | ||||||||||||||||||||||||||||||
| import { Dialog } from "radix-ui" | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
Comment on lines
+172
to
+179
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes — the official unified package is published as radix-ui. Install with: npm install radix-ui. [1][2]
Sources:
Radix UI 패키지 혼용으로 인한 번들 중복 제거 필요 172-179번 줄에서 개별 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - 동일 기능의 코드가 번들에 중복 포함 | ||||||||||||||||||||||||||||||
| - 패키지 버전 불일치 가능성 | ||||||||||||||||||||||||||||||
| - 의존성 관리 복잡성 증가 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## HIGH #6: Dialog 코드가 verbose함 🗂️ | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: `shared/design/src/components/dialog/` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ 불필요한 variant 분기와 CustomDialog 중복 | ||||||||||||||||||||||||||||||
| // - 77줄짜리 customDialog.tsx 별도 존재 | ||||||||||||||||||||||||||||||
| // - variant prop으로 복잡한 조건부 렌더링 | ||||||||||||||||||||||||||||||
| // - 동일 기능의 코드 중복 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| export function Dialog({ variant, ... }) { | ||||||||||||||||||||||||||||||
| if (variant === "title") return <TitleDialog /> | ||||||||||||||||||||||||||||||
| if (variant === "onlyTitle") return <OnlyTitleDialog /> | ||||||||||||||||||||||||||||||
| if (variant === "onlyBody") return <OnlyBodyDialog /> | ||||||||||||||||||||||||||||||
| // ... | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - 불필요한 코드량 증가 (77줄 중복) | ||||||||||||||||||||||||||||||
| - variant별 분기로 유지보수 어려움 | ||||||||||||||||||||||||||||||
| - Compound Component 패턴 미활용 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## HIGH #7: useAuthGuard() hook 사용 방식 🔐 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **파일**: | ||||||||||||||||||||||||||||||
| - `service/app/src/app/create/page.tsx` | ||||||||||||||||||||||||||||||
| - `service/app/src/app/dashboard/page.tsx` | ||||||||||||||||||||||||||||||
| - 기타 인증 필요 페이지들 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ### 문제점 | ||||||||||||||||||||||||||||||
| ```tsx | ||||||||||||||||||||||||||||||
| // ❌ Hook 기반 imperative 패턴 | ||||||||||||||||||||||||||||||
| export default function CreateGamePage() { | ||||||||||||||||||||||||||||||
| useAuthGuard() // 컴포넌트 렌더링 후 리다이렉트 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||
| <div className="bg-background-primary"> | ||||||||||||||||||||||||||||||
| <CreateGameNavigation /> | ||||||||||||||||||||||||||||||
| {/* 인증 안 된 상태에서도 잠깐 렌더링됨 (깜빡임) */} | ||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| **왜 문제인가?** | ||||||||||||||||||||||||||||||
| - 컴포넌트가 먼저 렌더링된 후 리다이렉트 → UI 깜빡임 | ||||||||||||||||||||||||||||||
| - 인증 로직이 컴포넌트 내부에 숨어있어 가독성 저하 | ||||||||||||||||||||||||||||||
| - 선언적이지 않은 패턴 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## 📚 참고 자료 | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - [Vercel React Best Practices Guide](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js) | ||||||||||||||||||||||||||||||
| - [React.cache() 공식 문서](https://react.dev/reference/react/cache) | ||||||||||||||||||||||||||||||
| - [Next.js Bundle Analyzer](https://www.npmjs.com/package/@next/bundle-analyzer) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 블록에 언어 지정자가 누락되었습니다.
markdownlint에서 지적한 대로 펜스드 코드 블록에 언어를 지정해 주세요 (예:
bash또는shell).📝 수정 제안
In @.claude/commands/commit.md around lines 9 - 11, Update the fenced code
blocks in .claude/commands/commit.md to include a language specifier (e.g.,
bash) so markdownlint passes; specifically add "bash" after the opening fences
for the blocks containing "/commit" and "/commit --no-verify" (the code blocks
shown in the diff) so they become fenced as
bash ...; ensure all similarblocks (also the ones around lines 15-17) are updated consistently.