Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .claude/commands/commit.md
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
```
Comment on lines +9 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

코드 블록에 언어 지정자가 누락되었습니다.

markdownlint에서 지적한 대로 펜스드 코드 블록에 언어를 지정해 주세요 (예: bash 또는 shell).

📝 수정 제안
-```
+```bash
 /commit

```diff
-```
+```bash
 /commit --no-verify
</details>


Also applies to: 15-17

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.18.1)</summary>

9-9: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

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 similar
blocks (also the ones around lines 15-17) are updated consistently.


</details>

<!-- fingerprinting:phantom:poseidon:ocelot -->

<!-- This is an auto-generated comment by CodeRabbit -->


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
18 changes: 18 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,24 @@ Uses conventional commits with custom types including "rule" for Cursor Rules ch
- **Figma Integration**: MCP servers configured for Figma plugin and Playwright
- **Build**: Uses Vercel for deployment

## 주의사항

### Radix UI Import Convention

Use the unified `radix-ui` package instead of individual `@radix-ui/*` packages:

```tsx
// ✅ Correct - Components
import { RadioGroup, Dialog, Form } from "radix-ui"

// ✅ Correct - Internal hooks (use~ prefix)
import { useControllableState } from "radix-ui/internal"

// ❌ Wrong - Legacy individual packages
import * as RadioGroup from "@radix-ui/react-radio-group"
import { useControllableState } from "@radix-ui/react-use-controllable-state"
```

## Additional Rules & Guidelines

For comprehensive development guidelines and conventions, refer to the cursor rules in `.cursor/rules/`:
Expand Down
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
"ci",
"build",
"revert",
"rule", // Cursor Rules 관련 변경사항
"ai", // AI agent 관련 수정사항(rule,skill 등)
],
],
},
Expand Down
247 changes: 247 additions & 0 deletions refactor.md
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

개선율 수치 표현을 명확히 해주세요.

Line 17의 "Tree-shaking 효율" 개선율 "+100%"는 오해의 소지가 있습니다. Tree-shaking 효율은 정량적으로 측정 가능한 퍼센트 지표가 아닙니다.

"번들 크기 감소" 또는 "최적화 완료" 같은 정성적 표현이나, 실제 번들 크기를 측정한 후 구체적인 KB/MB 감소량을 표시하는 것이 더 정확합니다.

제안하는 수정안
 | 지표 | 현재 | 개선 후 | 개선율 |
 |------|------|---------|--------|
-| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | 최적화 완료 |

또는 실제 번들 크기를 측정한 경우:

-| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
+| **번들 크기 (해당 파일들)** | 측정 필요 | 측정 필요 | -X KB |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | +100% |
| **Store 조회 복잡도** | O(n) | O(1) | -99% |
| **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 |
| **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 |
| **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 |
| 지표 | 현재 | 개선 후 | 개선율 |
|------|------|---------|--------|
| **Tree-shaking 효율** | 9개 파일 비최적 | 완전 최적화 | 최적화 완료 |
| **Store 조회 복잡도** | O(n) | O(1) | -99% |
| **UX (Suspense)** | 로딩 중 빈 화면 | 스켈레톤 표시 | 향상 |
| **Bundle 중복** | radix-ui 패키지 혼용 | 통합 패키지 | 번들 감소 |
| **코드 품질** | verbose Dialog | 간결한 구조 | 유지보수성 향상 |
🤖 Prompt for AI Agents
In `@refactor.md` around lines 15 - 21, Update the "Tree-shaking 효율" row in the
markdown table (the table header/row containing "Tree-shaking 효율") to remove the
misleading "+100%" numeric improvement: either replace it with a qualitative
label such as "최적화 완료" or "정성적 개선 (정확한 수치 없음)", or if you have measured bundle
sizes, replace the "개선율" cell with a concrete delta like "-X KB" / "-Y MB" and
include the measurement method; ensure the table's "개선율" column value for that
row is changed accordingly.


---

## ✅ 적용 필요 (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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

official unified Radix UI package name npm "radix-ui"

💡 Result:

Yes — the official unified package is published as radix-ui. Install with: npm install radix-ui. [1][2]

  • Individual packages still exist under the @radix-ui scope (e.g. @radix-ui/react-dialog). [1]

Sources:

  1. Radix Primitives docs (installation shows npm install radix-ui). [radix-ui.com/docs]. [1]
  2. npm package page for radix-ui. [npmjs.com/package/radix-ui]. [2]

Radix UI 패키지 혼용으로 인한 번들 중복 제거 필요

172-179번 줄에서 개별 @radix-ui/react-* 패키지와 통합 radix-ui 패키지를 혼용하고 있습니다. 이는 동일한 컴포넌트가 서로 다른 패키지 소스에서 중복으로 번들에 포함되어 번들 크기 증가를 초래합니다. 한 가지 패키지 소스로 통일하세요.

🤖 Prompt for AI Agents
In `@refactor.md` around lines 172 - 179, The project mixes individual
`@radix-ui/react-`* imports (e.g., useControllableState, RadixRadioGroup from
"@radix-ui/react-use-controllable-state" and "@radix-ui/react-radio-group") with
the bundled "radix-ui" import (e.g., Dialog), causing duplicate bundle entries;
pick one source and make all imports consistent: either change all imports to
the scoped packages (e.g., import useControllableState from
"@radix-ui/react-use-controllable-state" and RadioGroup from
"@radix-ui/react-radio-group" and replace any "radix-ui" Dialog import with the
corresponding "@radix-ui/react-dialog" export) or migrate everything to the
single "radix-ui" package equivalents, updating all files that reference
useControllableState, RadixRadioGroup, and Dialog so the module specifier is
uniform across the codebase.


**왜 문제인가?**
- 동일 기능의 코드가 번들에 중복 포함
- 패키지 버전 불일치 가능성
- 의존성 관리 복잡성 증가

---

## 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)
Loading
Loading