@blastlabs/utils 1.14.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/documentation.mdc +22 -0
- package/.cursor/rules/entities-layer.mdc +66 -0
- package/.cursor/rules/fsd-architecture.mdc +32 -0
- package/.cursor/rules/git-commit.mdc +30 -0
- package/.cursor/rules/project-overview.mdc +44 -0
- package/.cursor/rules/react-hooks.mdc +43 -0
- package/.cursor/rules/shared-layer.mdc +57 -0
- package/.cursor/rules/testing.mdc +29 -0
- package/.cursor/rules/typescript-standards.mdc +36 -0
- package/.cursor/rules/views-layer.mdc +71 -0
- package/bin/init-ai-rules.cjs +117 -110
- package/package.json +2 -2
- package/templates/base.md +0 -111
- package/templates/fsd.md +0 -150
- package/templates/monorepo.md +0 -61
- package/templates/nextjs.md +0 -67
- package/templates/project-specific.md +0 -21
- package/templates/testing.md +0 -66
- package/templates/vite.md +0 -53
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 문서화 가이드라인
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Documentation Guidelines
|
|
8
|
+
|
|
9
|
+
## JSDoc
|
|
10
|
+
|
|
11
|
+
- 모든 public API에 JSDoc 주석 사용
|
|
12
|
+
- 예제 코드 포함
|
|
13
|
+
- edge cases와 gotchas 문서화
|
|
14
|
+
|
|
15
|
+
## 문서 업데이트
|
|
16
|
+
|
|
17
|
+
- 기능 추가 시 README.md 업데이트
|
|
18
|
+
- 새 hooks 추가 시 관련 docs/*.md 업데이트
|
|
19
|
+
|
|
20
|
+
## 필수 사항
|
|
21
|
+
|
|
22
|
+
- **Always** update documentation when adding features
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Entities 레이어 구조 및 API 패턴"
|
|
3
|
+
globs: ["**/entities/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Entities Layer (엔티티)
|
|
7
|
+
|
|
8
|
+
엔티티는 도메인 모델과 API 로직을 관리합니다.
|
|
9
|
+
|
|
10
|
+
## 구조
|
|
11
|
+
```
|
|
12
|
+
entities/
|
|
13
|
+
└── (엔티티 이름)/ 예: inquiry, popular-product
|
|
14
|
+
├── api/
|
|
15
|
+
│ ├── mapper/
|
|
16
|
+
│ │ ├── map-(엔티티 이름).ts
|
|
17
|
+
│ │ └── map-(엔티티 이름)-detail.ts
|
|
18
|
+
│ ├── query/
|
|
19
|
+
│ │ ├── (엔티티 이름)-list-query.ts
|
|
20
|
+
│ │ └── (엔티티 이름)-detail-query.ts
|
|
21
|
+
│ ├── get-(엔티티 이름)-list.ts
|
|
22
|
+
│ ├── get-(엔티티 이름)-detail.ts
|
|
23
|
+
│ ├── (엔티티 이름)-queries.ts
|
|
24
|
+
│ ├── mutate.ts
|
|
25
|
+
│ ├── query.ts
|
|
26
|
+
│ └── index.ts
|
|
27
|
+
├── model/
|
|
28
|
+
│ ├── (엔티티 이름).ts
|
|
29
|
+
│ ├── (엔티티 이름)-detail.ts
|
|
30
|
+
│ └── (엔티티 이름)-update.ts
|
|
31
|
+
├── schema.ts
|
|
32
|
+
└── index.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 각 파일의 역할
|
|
36
|
+
- **mapper/**: API 응답을 도메인 모델로 변환
|
|
37
|
+
- **query/**: 쿼리 파라미터 타입 정의
|
|
38
|
+
- **get-*-list.ts**: 리스트 조회 API 함수
|
|
39
|
+
- **get-*-detail.ts**: 상세 조회 API 함수
|
|
40
|
+
- **\*-queries.ts**: TanStack Query queryOptions 정의 (all, lists, list, details, detail 등)
|
|
41
|
+
- **mutate.ts**: mutation 함수들
|
|
42
|
+
- **model/**: 도메인 모델 타입 정의
|
|
43
|
+
- **schema.ts**: zod 스키마 정의 및 변환 함수
|
|
44
|
+
- **index.ts**: `export * as (엔티티 이름)Api from "./api"`
|
|
45
|
+
|
|
46
|
+
## 예시
|
|
47
|
+
```
|
|
48
|
+
entities/inquiry/
|
|
49
|
+
├── api/
|
|
50
|
+
│ ├── mapper/
|
|
51
|
+
│ │ ├── map-inquiry.ts
|
|
52
|
+
│ │ └── map-inquiry-detail.ts
|
|
53
|
+
│ ├── query/
|
|
54
|
+
│ │ ├── inquiry-list-query.ts
|
|
55
|
+
│ │ └── inquiry-detail-query.ts
|
|
56
|
+
│ ├── get-inquiry-list.ts
|
|
57
|
+
│ ├── get-inquiry-detail.ts
|
|
58
|
+
│ └── inquiry-queries.ts
|
|
59
|
+
├── model/
|
|
60
|
+
│ ├── inquiry.ts
|
|
61
|
+
│ └── inquiry-detail.ts
|
|
62
|
+
└── schema.ts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## TanStack Query 패턴
|
|
66
|
+
서버 상태 관리는 TanStack Query의 queryOptions 패턴을 사용합니다.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Feature-Sliced Design 아키텍처 가이드"
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Feature-Sliced Design (FSD) 아키텍처
|
|
7
|
+
|
|
8
|
+
프로젝트는 [FSD 공식문서](https://feature-sliced.github.io/documentation/kr/docs/get-started/overview)를 참고하여 구조화되어 있습니다.
|
|
9
|
+
|
|
10
|
+
## apps 내 폴더 구조
|
|
11
|
+
```
|
|
12
|
+
src/
|
|
13
|
+
├── app/ # 앱 초기화, 프로바이더, 라우팅
|
|
14
|
+
├── entities/ # 도메인 모델, API
|
|
15
|
+
├── feature/ # 비즈니스 기능
|
|
16
|
+
├── shared/ # 공통 유틸, UI
|
|
17
|
+
├── views/ # 페이지 컴포넌트
|
|
18
|
+
└── widget/ # 독립적 UI 블록
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 레이어 의존성 규칙
|
|
22
|
+
- `views` → `widget`, `feature`, `entities`, `shared`
|
|
23
|
+
- `widget` → `feature`, `entities`, `shared`
|
|
24
|
+
- `feature` → `entities`, `shared`
|
|
25
|
+
- `entities` → `shared`
|
|
26
|
+
- `shared` → 외부 라이브러리만
|
|
27
|
+
|
|
28
|
+
## 개발 가이드라인
|
|
29
|
+
1. **FSD 아키텍처 준수**: 레이어 구조를 유지합니다
|
|
30
|
+
2. **명명 규칙**: 파일명과 폴더명은 케밥 케이스 사용
|
|
31
|
+
3. **타입 안정성**: TypeScript로 타입을 명확하게 정의
|
|
32
|
+
4. **코드 재사용**: 공통 로직은 shared, 도메인 로직은 entities에 배치
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Git 커밋 컨벤션
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Git Commit Convention
|
|
8
|
+
|
|
9
|
+
## Conventional Commits Format
|
|
10
|
+
|
|
11
|
+
- `feat(scope): description` - New features
|
|
12
|
+
- `fix(scope): description` - Bug fixes
|
|
13
|
+
- `refactor(scope): description` - Code refactoring
|
|
14
|
+
- `docs(scope): description` - Documentation changes
|
|
15
|
+
- `test(scope): description` - Test changes
|
|
16
|
+
- `chore(scope): description` - Build/tooling changes
|
|
17
|
+
|
|
18
|
+
## 예시
|
|
19
|
+
|
|
20
|
+
- `feat(hooks): add useAuth hook for authentication management`
|
|
21
|
+
- `fix(hooks): resolve SSR localStorage error in useAuth`
|
|
22
|
+
- `docs(auth): update useAuth documentation with examples`
|
|
23
|
+
|
|
24
|
+
## 커밋 전 체크리스트
|
|
25
|
+
|
|
26
|
+
1. Run tests: `npm test`
|
|
27
|
+
2. Build successfully: `npm run build`
|
|
28
|
+
3. Update documentation if needed
|
|
29
|
+
4. Follow conventional commit format
|
|
30
|
+
5. Add Co-Authored-By line for AI assistance
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: @blastlabs/utils 프로젝트 개요 및 기본 가이드라인
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# @blastlabs/utils Development Rules
|
|
8
|
+
|
|
9
|
+
React 애플리케이션을 위한 종합 TypeScript 유틸리티 라이브러리입니다.
|
|
10
|
+
|
|
11
|
+
## 제공 기능
|
|
12
|
+
|
|
13
|
+
- Authentication management (useAuth, AuthGuard)
|
|
14
|
+
- UI/UX hooks (useTabs, useMediaQuery, useWindowSize)
|
|
15
|
+
- Form management (useCRUDForm)
|
|
16
|
+
- Time utilities (useCountdown, useStopwatch, useInterval)
|
|
17
|
+
- Storage hooks (useLocalStorage, useSessionStorage, useCopyToClipboard)
|
|
18
|
+
- State management hooks (useToggle, usePrevious)
|
|
19
|
+
- Performance hooks (useDebounce, useThrottle, useIntersectionObserver)
|
|
20
|
+
- Event hooks (useEventListener, useClickOutside)
|
|
21
|
+
|
|
22
|
+
## 문서
|
|
23
|
+
|
|
24
|
+
- [Development Guide](docs/development-guide.md) - 개발 가이드 통합 문서
|
|
25
|
+
- [Authentication Hooks](docs/auth-hooks.md) - useAuth, AuthGuard
|
|
26
|
+
- [UI Hooks](docs/ui-hooks.md) - useTabs, useMediaQuery, useWindowSize
|
|
27
|
+
|
|
28
|
+
## 파일 구조
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
src/
|
|
32
|
+
├── hooks/
|
|
33
|
+
│ ├── auth/ # Authentication hooks
|
|
34
|
+
│ ├── ui/ # UI/UX hooks
|
|
35
|
+
│ ├── time/ # Time-related hooks
|
|
36
|
+
│ ├── form/ # Form management hooks
|
|
37
|
+
│ ├── storage/ # Storage hooks
|
|
38
|
+
│ ├── state/ # State management hooks
|
|
39
|
+
│ ├── performance/ # Performance hooks
|
|
40
|
+
│ └── event/ # Event hooks
|
|
41
|
+
└── components/
|
|
42
|
+
├── auth/ # Auth components (AuthGuard)
|
|
43
|
+
└── dev/ # Development components
|
|
44
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: React Hooks 개발 규칙 및 SSR 안전성
|
|
3
|
+
globs: "**/use*.ts,**/use*.tsx"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Hooks Rules
|
|
8
|
+
|
|
9
|
+
## 필수 규칙
|
|
10
|
+
|
|
11
|
+
- 모든 hooks는 SSR-safe 해야 함 (browser APIs 접근 시 `typeof window !== 'undefined'` 체크)
|
|
12
|
+
- `useCallback`, `useMemo`를 적절히 사용하여 성능 최적화
|
|
13
|
+
- `useEffect`에서 side effects 정리 필수
|
|
14
|
+
- React Hooks 규칙 준수 (조건부 호출 금지 등)
|
|
15
|
+
|
|
16
|
+
## SSR-Safe Hook Pattern
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
const [value, setValue] = useState(() => {
|
|
20
|
+
if (typeof window === 'undefined') return defaultValue;
|
|
21
|
+
return window.localStorage.getItem(key);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (typeof window === 'undefined') return;
|
|
26
|
+
// Browser-only code
|
|
27
|
+
}, []);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Storage Pattern
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
const setItem = useCallback((value: T) => {
|
|
34
|
+
if (typeof window === 'undefined') return;
|
|
35
|
+
|
|
36
|
+
const storage = rememberMe ? localStorage : sessionStorage;
|
|
37
|
+
storage.setItem(key, JSON.stringify(value));
|
|
38
|
+
}, [key, rememberMe]);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 중요
|
|
42
|
+
|
|
43
|
+
- **Always** check SSR compatibility for browser APIs
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Shared 레이어 구조 및 lib vs utils 구분"
|
|
3
|
+
globs: ["**/shared/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Shared Layer (공유 레이어)
|
|
7
|
+
|
|
8
|
+
프로젝트 전체에서 공유되는 코드를 관리합니다.
|
|
9
|
+
|
|
10
|
+
## 구조
|
|
11
|
+
```
|
|
12
|
+
shared/
|
|
13
|
+
├── api/ API 인스턴스 설정 (instance_v2.ts 등)
|
|
14
|
+
├── constant/ 공통 상수 (path.ts 등)
|
|
15
|
+
├── hooks/ 공통 React 훅
|
|
16
|
+
├── mock-data/ 개발용 목 데이터
|
|
17
|
+
├── lib/ 라이브러리 래퍼 및 설정
|
|
18
|
+
│ └── (라이브러리명)/
|
|
19
|
+
│ ├── provider.tsx 라이브러리 Provider 래퍼
|
|
20
|
+
│ └── config.ts 라이브러리 기본 설정
|
|
21
|
+
├── ui/
|
|
22
|
+
│ ├── component/ 재사용 가능한 UI 컴포넌트
|
|
23
|
+
│ │ └── (컴포넌트명)/
|
|
24
|
+
│ │ ├── index.tsx
|
|
25
|
+
│ │ ├── types.ts
|
|
26
|
+
│ │ ├── styles.ts
|
|
27
|
+
│ │ └── (컴포넌트명).tsx
|
|
28
|
+
│ ├── theme/ 테마 관련 CSS 파일
|
|
29
|
+
│ └── utils/ UI 관련 유틸리티 함수
|
|
30
|
+
├── util/ 일반 유틸리티 함수
|
|
31
|
+
└── utils/ 추가 유틸리티 함수
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## lib vs utils 구분 기준
|
|
35
|
+
|
|
36
|
+
### `shared/lib/`
|
|
37
|
+
외부 라이브러리를 프로젝트에 맞게 감싸거나 설정하는 코드
|
|
38
|
+
- 라이브러리 래퍼(wrapper) 컴포넌트
|
|
39
|
+
- 라이브러리 초기 설정 및 Provider
|
|
40
|
+
- 예시:
|
|
41
|
+
- `shared/lib/react-hook-form/form-provider.tsx`
|
|
42
|
+
- `shared/lib/tanstack-query/query-client.tsx`
|
|
43
|
+
- `shared/lib/suspense/suspense-wrapper.tsx`
|
|
44
|
+
|
|
45
|
+
### `shared/utils/` (또는 `shared/util/`)
|
|
46
|
+
순수 유틸리티 함수 및 비즈니스 로직 헬퍼
|
|
47
|
+
- 라이브러리와 무관한 순수 함수
|
|
48
|
+
- 라이브러리 기능을 사용하는 유틸 함수
|
|
49
|
+
- 예시:
|
|
50
|
+
- `shared/util/convert-price.ts` - 가격 변환 함수
|
|
51
|
+
- `shared/util/export-excel.ts` - 엑셀 내보내기 함수
|
|
52
|
+
- `shared/util/form-validation.ts` - 폼 검증 유틸
|
|
53
|
+
|
|
54
|
+
## 판단 기준
|
|
55
|
+
- 라이브러리를 감싸는가? → `lib/`
|
|
56
|
+
- 라이브러리와 무관한 로직인가? → `utils/`
|
|
57
|
+
- 라이브러리 기능을 사용하는 유틸인가? → `utils/`
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 테스트 작성 가이드라인
|
|
3
|
+
globs: "**/*.test.ts,**/*.test.tsx,**/__tests__/**"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Testing Guidelines
|
|
8
|
+
|
|
9
|
+
## 테스트 도구
|
|
10
|
+
|
|
11
|
+
- Vitest 사용
|
|
12
|
+
- @testing-library/react 사용
|
|
13
|
+
|
|
14
|
+
## 테스트 원칙
|
|
15
|
+
|
|
16
|
+
- 모든 hooks에 대해 포괄적인 테스트 작성
|
|
17
|
+
- SSR 시나리오 테스트 포함
|
|
18
|
+
- 높은 테스트 커버리지 목표
|
|
19
|
+
|
|
20
|
+
## 테스트 실행
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm test
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 필수 사항
|
|
27
|
+
|
|
28
|
+
- **Always** write tests for new features
|
|
29
|
+
- 새 기능 추가 시 반드시 테스트 포함
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: TypeScript 코딩 표준 및 타입 규칙
|
|
3
|
+
globs: "**/*.ts,**/*.tsx"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# TypeScript Standards
|
|
8
|
+
|
|
9
|
+
## 타입 규칙
|
|
10
|
+
|
|
11
|
+
- strict type checking 사용
|
|
12
|
+
- `any` 타입 대신 명시적 타입 사용
|
|
13
|
+
- public types와 interfaces는 모두 export
|
|
14
|
+
- 재사용 가능한 컴포넌트에는 generics 사용
|
|
15
|
+
|
|
16
|
+
## 예시
|
|
17
|
+
|
|
18
|
+
### Hook Return Pattern
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
export interface UseHookReturn {
|
|
22
|
+
// State
|
|
23
|
+
value: T;
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
error: Error | null;
|
|
26
|
+
|
|
27
|
+
// Actions
|
|
28
|
+
action: () => void;
|
|
29
|
+
reset: () => void;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 금지 사항
|
|
34
|
+
|
|
35
|
+
- **Never** use `any` type without justification
|
|
36
|
+
- **Always** provide TypeScript types for public APIs
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Views 레이어 (페이지) 구조"
|
|
3
|
+
globs: ["**/views/**"]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Views Layer (페이지 레이어)
|
|
7
|
+
|
|
8
|
+
페이지 단위의 UI와 로직을 관리합니다.
|
|
9
|
+
|
|
10
|
+
## 구조
|
|
11
|
+
```
|
|
12
|
+
views/
|
|
13
|
+
└── (페이지명)/ 예: inquiry, product, seller
|
|
14
|
+
├── page.tsx 메인 페이지 컴포넌트 (필수)
|
|
15
|
+
├── (페이지명)-list-page.tsx
|
|
16
|
+
├── (페이지명)-detail-page.tsx
|
|
17
|
+
├── detail/
|
|
18
|
+
│ ├── page.tsx
|
|
19
|
+
│ ├── ui/
|
|
20
|
+
│ ├── hooks/ use-(페이지명)-detail-data.tsx 등
|
|
21
|
+
│ └── components/
|
|
22
|
+
├── list/
|
|
23
|
+
│ ├── (컬럼명)-column.tsx
|
|
24
|
+
│ ├── search.tsx
|
|
25
|
+
│ └── (모달명)-modal.tsx
|
|
26
|
+
├── ui/
|
|
27
|
+
├── hooks/ use-(페이지명)-data.tsx, use-(페이지명)-actions.tsx 등
|
|
28
|
+
├── components/
|
|
29
|
+
├── utils/
|
|
30
|
+
├── schema/ zod 스키마
|
|
31
|
+
└── constants.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 예시
|
|
35
|
+
|
|
36
|
+
### 기본 페이지 구조
|
|
37
|
+
```
|
|
38
|
+
views/inquiry/
|
|
39
|
+
├── detail/
|
|
40
|
+
│ └── page.tsx
|
|
41
|
+
├── inquiry-list-page.tsx
|
|
42
|
+
└── list/
|
|
43
|
+
├── inquiry-column.tsx
|
|
44
|
+
├── inquiry-modal.tsx
|
|
45
|
+
└── search.tsx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 상세 페이지 구조
|
|
49
|
+
```
|
|
50
|
+
views/ai-management/agency/agency-detail/
|
|
51
|
+
├── page.tsx
|
|
52
|
+
├── hooks/
|
|
53
|
+
│ ├── use-agency-detail-data.tsx
|
|
54
|
+
│ └── use-agency-data-actions.tsx
|
|
55
|
+
├── components/
|
|
56
|
+
│ └── agency-basic-info-tab.tsx
|
|
57
|
+
└── schema/
|
|
58
|
+
└── agency-detail.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 관리 페이지 구조
|
|
62
|
+
```
|
|
63
|
+
views/product/category-management/
|
|
64
|
+
├── page.tsx
|
|
65
|
+
├── hooks/
|
|
66
|
+
│ ├── use-category-data.tsx
|
|
67
|
+
│ └── use-category-actions.tsx
|
|
68
|
+
└── ui/
|
|
69
|
+
├── category-management.tsx
|
|
70
|
+
└── category-modal.tsx
|
|
71
|
+
```
|
package/bin/init-ai-rules.cjs
CHANGED
|
@@ -3,53 +3,42 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
// 규칙 카테고리별 정의
|
|
7
|
+
const RULE_CATEGORIES = {
|
|
8
|
+
// 기본 규칙 (항상 포함)
|
|
9
|
+
base: [
|
|
10
|
+
"typescript-standards.mdc",
|
|
11
|
+
"react-hooks.mdc",
|
|
12
|
+
"testing.mdc",
|
|
13
|
+
"documentation.mdc",
|
|
14
|
+
"git-commit.mdc",
|
|
15
|
+
],
|
|
16
|
+
// FSD 아키텍처 규칙
|
|
17
|
+
fsd: [
|
|
18
|
+
"fsd-architecture.mdc",
|
|
19
|
+
"entities-layer.mdc",
|
|
20
|
+
"shared-layer.mdc",
|
|
21
|
+
"views-layer.mdc",
|
|
22
|
+
],
|
|
14
23
|
};
|
|
15
24
|
|
|
16
25
|
function parseArgs(args) {
|
|
17
26
|
const options = {
|
|
18
|
-
projectName: null,
|
|
19
|
-
projectDescription: "프로젝트 설명을 작성해주세요.",
|
|
20
|
-
framework: null, // 'nextjs' or 'vite'
|
|
21
27
|
fsd: false,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
packageManager: "pnpm",
|
|
28
|
+
all: false,
|
|
29
|
+
list: false,
|
|
25
30
|
help: false,
|
|
26
31
|
};
|
|
27
32
|
|
|
28
|
-
for (
|
|
29
|
-
const arg = args[i];
|
|
30
|
-
|
|
33
|
+
for (const arg of args) {
|
|
31
34
|
if (arg === "--help" || arg === "-h") {
|
|
32
35
|
options.help = true;
|
|
33
|
-
} else if (arg === "--next" || arg === "--nextjs") {
|
|
34
|
-
options.framework = "nextjs";
|
|
35
|
-
} else if (arg === "--vite") {
|
|
36
|
-
options.framework = "vite";
|
|
37
36
|
} else if (arg === "--fsd") {
|
|
38
37
|
options.fsd = true;
|
|
39
|
-
} else if (arg === "--
|
|
40
|
-
options.
|
|
41
|
-
} else if (arg === "--
|
|
42
|
-
options.
|
|
43
|
-
} else if (arg === "--npm") {
|
|
44
|
-
options.packageManager = "npm";
|
|
45
|
-
} else if (arg === "--yarn") {
|
|
46
|
-
options.packageManager = "yarn";
|
|
47
|
-
} else if (arg === "--pnpm") {
|
|
48
|
-
options.packageManager = "pnpm";
|
|
49
|
-
} else if (!arg.startsWith("--") && !options.projectName) {
|
|
50
|
-
options.projectName = arg;
|
|
51
|
-
} else if (!arg.startsWith("--") && options.projectName && !options.projectDescription) {
|
|
52
|
-
options.projectDescription = arg;
|
|
38
|
+
} else if (arg === "--all") {
|
|
39
|
+
options.all = true;
|
|
40
|
+
} else if (arg === "--list" || arg === "-l") {
|
|
41
|
+
options.list = true;
|
|
53
42
|
}
|
|
54
43
|
}
|
|
55
44
|
|
|
@@ -57,111 +46,129 @@ function parseArgs(args) {
|
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
function showHelp() {
|
|
60
|
-
console.log("@blastlabs/utils -
|
|
61
|
-
console.log("프로젝트에 .
|
|
62
|
-
console.log("사용법: npx blastlabs-init-ai-rules
|
|
49
|
+
console.log("@blastlabs/utils - Cursor Rules Installer\n");
|
|
50
|
+
console.log("프로젝트에 .cursor/rules/ 규칙 파일들을 설치합니다.\n");
|
|
51
|
+
console.log("사용법: npx blastlabs-init-ai-rules [options]\n");
|
|
63
52
|
console.log("Options:");
|
|
64
|
-
console.log(" --
|
|
65
|
-
console.log(" --
|
|
66
|
-
console.log(" --
|
|
67
|
-
console.log(" --
|
|
68
|
-
console.log(" --monorepo 모노레포 규칙 포함");
|
|
69
|
-
console.log(" --npm npm 사용 (기본값: pnpm)");
|
|
70
|
-
console.log(" --yarn yarn 사용");
|
|
71
|
-
console.log(" --pnpm pnpm 사용 (기본값)");
|
|
53
|
+
console.log(" --fsd FSD 아키텍처 규칙 포함");
|
|
54
|
+
console.log(" --all 모든 규칙 설치 (base + fsd)");
|
|
55
|
+
console.log(" --list, -l 설치 가능한 규칙 목록 보기");
|
|
56
|
+
console.log(" --help, -h 도움말 보기");
|
|
72
57
|
console.log("\n예시:");
|
|
73
|
-
console.log(
|
|
74
|
-
console.log(
|
|
75
|
-
console.log(
|
|
58
|
+
console.log(" npx blastlabs-init-ai-rules # 기본 규칙만");
|
|
59
|
+
console.log(" npx blastlabs-init-ai-rules --fsd # 기본 + FSD 규칙");
|
|
60
|
+
console.log(" npx blastlabs-init-ai-rules --all # 모든 규칙");
|
|
76
61
|
console.log("\n⚠️ 프로젝트 루트 디렉토리에서 실행해주세요!");
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.
|
|
85
|
-
|
|
64
|
+
function showList() {
|
|
65
|
+
console.log("📋 설치 가능한 규칙 목록\n");
|
|
66
|
+
|
|
67
|
+
console.log("📦 기본 규칙 (base) - 항상 포함:");
|
|
68
|
+
for (const rule of RULE_CATEGORIES.base) {
|
|
69
|
+
console.log(` ├── ${rule}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log("\n🏗️ FSD 아키텍처 규칙 (--fsd):");
|
|
73
|
+
for (const rule of RULE_CATEGORIES.fsd) {
|
|
74
|
+
console.log(` ├── ${rule}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log("\n💡 각 규칙은 globs 패턴으로 자동 적용됩니다.");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getRulesSourceDir() {
|
|
81
|
+
// 패키지 내 .cursor/rules 경로
|
|
82
|
+
return path.join(__dirname, "..", ".cursor", "rules");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function copyRule(ruleName, sourceDir, targetDir) {
|
|
86
|
+
const sourcePath = path.join(sourceDir, ruleName);
|
|
87
|
+
const targetPath = path.join(targetDir, ruleName);
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(sourcePath)) {
|
|
90
|
+
console.log(` ⚠️ ${ruleName} - 소스 파일 없음, 건너뜀`);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (fs.existsSync(targetPath)) {
|
|
95
|
+
console.log(` ⏭️ ${ruleName} - 이미 존재함, 건너뜀`);
|
|
96
|
+
return false;
|
|
86
97
|
}
|
|
98
|
+
|
|
99
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
100
|
+
console.log(` ✅ ${ruleName}`);
|
|
101
|
+
return true;
|
|
87
102
|
}
|
|
88
103
|
|
|
89
104
|
function main() {
|
|
90
105
|
const args = process.argv.slice(2);
|
|
91
106
|
const options = parseArgs(args);
|
|
92
107
|
|
|
93
|
-
if (options.help
|
|
108
|
+
if (options.help) {
|
|
94
109
|
showHelp();
|
|
95
110
|
process.exit(0);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let content = loadTemplate(TEMPLATES.base);
|
|
102
|
-
|
|
103
|
-
if (options.fsd) {
|
|
104
|
-
content += "\n" + loadTemplate(TEMPLATES.fsd);
|
|
113
|
+
if (options.list) {
|
|
114
|
+
showList();
|
|
115
|
+
process.exit(0);
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
const targetDir = process.cwd();
|
|
119
|
+
const rulesTargetDir = path.join(targetDir, ".cursor", "rules");
|
|
120
|
+
const rulesSourceDir = getRulesSourceDir();
|
|
121
|
+
|
|
122
|
+
// 소스 디렉토리 확인
|
|
123
|
+
if (!fs.existsSync(rulesSourceDir)) {
|
|
124
|
+
console.error("❌ 규칙 소스 디렉토리를 찾을 수 없습니다.");
|
|
125
|
+
console.error(` 경로: ${rulesSourceDir}`);
|
|
126
|
+
process.exit(1);
|
|
109
127
|
}
|
|
110
128
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
129
|
+
// .cursor/rules 디렉토리 생성
|
|
130
|
+
if (!fs.existsSync(rulesTargetDir)) {
|
|
131
|
+
fs.mkdirSync(rulesTargetDir, { recursive: true });
|
|
132
|
+
console.log("📁 .cursor/rules/ 디렉토리 생성됨\n");
|
|
115
133
|
}
|
|
116
134
|
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
// 설치할 규칙 목록 결정
|
|
136
|
+
let rulesToInstall = [...RULE_CATEGORIES.base];
|
|
137
|
+
|
|
138
|
+
if (options.all || options.fsd) {
|
|
139
|
+
rulesToInstall = [...rulesToInstall, ...RULE_CATEGORIES.fsd];
|
|
119
140
|
}
|
|
120
141
|
|
|
121
|
-
//
|
|
122
|
-
|
|
142
|
+
// 중복 제거
|
|
143
|
+
rulesToInstall = [...new Set(rulesToInstall)];
|
|
123
144
|
|
|
124
|
-
|
|
125
|
-
const frameworkName = options.framework === "nextjs" ? "Next.js" :
|
|
126
|
-
options.framework === "vite" ? "Vite" : "React";
|
|
127
|
-
|
|
128
|
-
content = content
|
|
129
|
-
.replace(/\{\{PROJECT_NAME\}\}/g, options.projectName)
|
|
130
|
-
.replace(/\{\{PROJECT_DESCRIPTION\}\}/g, options.projectDescription)
|
|
131
|
-
.replace(/\{\{PACKAGE_MANAGER\}\}/g, options.packageManager)
|
|
132
|
-
.replace(/\{\{FRAMEWORK\}\}/g, frameworkName);
|
|
133
|
-
|
|
134
|
-
// .cursorrules 파일 생성
|
|
135
|
-
const cursorrulesPath = path.join(targetDir, ".cursorrules");
|
|
136
|
-
if (fs.existsSync(cursorrulesPath)) {
|
|
137
|
-
console.log("⚠️ .cursorrules 파일이 이미 존재합니다. 덮어쓰기를 건너뜁니다.");
|
|
138
|
-
} else {
|
|
139
|
-
fs.writeFileSync(cursorrulesPath, content);
|
|
140
|
-
console.log("✅ .cursorrules 생성 완료");
|
|
141
|
-
}
|
|
145
|
+
console.log("📥 규칙 설치 중...\n");
|
|
142
146
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
let installedCount = 0;
|
|
148
|
+
let skippedCount = 0;
|
|
149
|
+
|
|
150
|
+
for (const rule of rulesToInstall) {
|
|
151
|
+
const installed = copyRule(rule, rulesSourceDir, rulesTargetDir);
|
|
152
|
+
if (installed) {
|
|
153
|
+
installedCount++;
|
|
154
|
+
} else {
|
|
155
|
+
skippedCount++;
|
|
156
|
+
}
|
|
150
157
|
}
|
|
151
158
|
|
|
152
|
-
console.log("\n
|
|
153
|
-
console.log(
|
|
154
|
-
console.log("└── CLAUDE.md");
|
|
159
|
+
console.log("\n" + "─".repeat(40));
|
|
160
|
+
console.log(`\n✨ 완료! ${installedCount}개 설치, ${skippedCount}개 건너뜀`);
|
|
155
161
|
|
|
156
|
-
console.log("\n📦
|
|
157
|
-
console.log("├── base (
|
|
158
|
-
if (options.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (options.monorepo) console.log("├── monorepo");
|
|
162
|
-
console.log("└── project-specific");
|
|
162
|
+
console.log("\n📦 설치된 규칙 카테고리:");
|
|
163
|
+
console.log(" ├── base (기본 규칙)");
|
|
164
|
+
if (options.all || options.fsd) {
|
|
165
|
+
console.log(" └── fsd (Feature-Sliced Design)");
|
|
166
|
+
}
|
|
163
167
|
|
|
164
|
-
console.log("\n💡 Tip:
|
|
168
|
+
console.log("\n💡 Tip:");
|
|
169
|
+
console.log(" - 각 .mdc 파일의 globs 패턴에 따라 자동 적용됩니다");
|
|
170
|
+
console.log(" - alwaysApply: true 규칙은 항상 적용됩니다");
|
|
171
|
+
console.log(" - 프로젝트에 맞게 규칙을 수정하세요!");
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blastlabs/utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"bin",
|
|
14
|
-
"
|
|
14
|
+
".cursor/rules"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
17
|
"prepare": "npm run build:tsc",
|
package/templates/base.md
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# {{PROJECT_NAME}} Development Rules
|
|
2
|
-
|
|
3
|
-
This file contains development guidelines for AI assistants (Cursor, Claude Code, etc.)
|
|
4
|
-
|
|
5
|
-
## Project Overview
|
|
6
|
-
|
|
7
|
-
{{PROJECT_DESCRIPTION}}
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# 프로젝트 기본 정보
|
|
12
|
-
|
|
13
|
-
- **패키지 매니저**: {{PACKAGE_MANAGER}}
|
|
14
|
-
- **프레임워크**: {{FRAMEWORK}}
|
|
15
|
-
|
|
16
|
-
## 기술 스택
|
|
17
|
-
|
|
18
|
-
- TypeScript
|
|
19
|
-
- React
|
|
20
|
-
- TanStack Query
|
|
21
|
-
- Tailwind CSS
|
|
22
|
-
- Zod
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
# Common Rules (공통 규칙)
|
|
27
|
-
|
|
28
|
-
## Code Style & Standards
|
|
29
|
-
|
|
30
|
-
### TypeScript
|
|
31
|
-
- Use strict type checking (`strict: true` in tsconfig)
|
|
32
|
-
- Prefer explicit types over `any`
|
|
33
|
-
- Export all public types and interfaces
|
|
34
|
-
- Use generics for reusable components
|
|
35
|
-
- Use `type` for unions/intersections, `interface` for object shapes
|
|
36
|
-
|
|
37
|
-
### Naming Conventions
|
|
38
|
-
- **Files/Folders**: kebab-case (`user-profile.tsx`, `use-auth.ts`)
|
|
39
|
-
- **Components**: PascalCase (`UserProfile`, `AuthGuard`)
|
|
40
|
-
- **Hooks**: camelCase with `use` prefix (`useAuth`, `useLocalStorage`)
|
|
41
|
-
- **Constants**: UPPER_SNAKE_CASE (`API_BASE_URL`, `MAX_RETRY_COUNT`)
|
|
42
|
-
- **Types/Interfaces**: PascalCase (`UserProfile`, `AuthState`)
|
|
43
|
-
|
|
44
|
-
### Import Order
|
|
45
|
-
1. React/Framework imports
|
|
46
|
-
2. Third-party libraries
|
|
47
|
-
3. Internal modules (absolute paths)
|
|
48
|
-
4. Relative imports
|
|
49
|
-
5. Types (with `type` keyword)
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
import React, { useState, useEffect } from 'react';
|
|
53
|
-
import { useQuery } from '@tanstack/react-query';
|
|
54
|
-
import { Button } from '@/components/ui';
|
|
55
|
-
import { formatDate } from '../utils';
|
|
56
|
-
import type { User } from '@/types';
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## React Best Practices
|
|
60
|
-
|
|
61
|
-
### Component Patterns
|
|
62
|
-
- Use functional components with hooks
|
|
63
|
-
- Keep components small and focused (single responsibility)
|
|
64
|
-
- Extract reusable logic into custom hooks
|
|
65
|
-
- Use composition over inheritance
|
|
66
|
-
|
|
67
|
-
### Hooks Rules
|
|
68
|
-
- Only call hooks at the top level
|
|
69
|
-
- Only call hooks from React functions
|
|
70
|
-
- Use `useCallback` for event handlers passed to children
|
|
71
|
-
- Use `useMemo` for expensive calculations
|
|
72
|
-
|
|
73
|
-
### State Management
|
|
74
|
-
- Keep state as local as possible
|
|
75
|
-
- Lift state up only when necessary
|
|
76
|
-
- Use TanStack Query for server state
|
|
77
|
-
- Use context or Zustand for client global state
|
|
78
|
-
|
|
79
|
-
## Error Handling
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
try {
|
|
83
|
-
const result = await someAsyncOperation();
|
|
84
|
-
return { data: result, error: null };
|
|
85
|
-
} catch (error) {
|
|
86
|
-
console.error('Operation failed:', error);
|
|
87
|
-
return { data: null, error: error instanceof Error ? error : new Error('Unknown error') };
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Git Commit Convention
|
|
92
|
-
|
|
93
|
-
Use conventional commits format:
|
|
94
|
-
- `feat(scope): description` - New features
|
|
95
|
-
- `fix(scope): description` - Bug fixes
|
|
96
|
-
- `refactor(scope): description` - Code refactoring
|
|
97
|
-
- `docs(scope): description` - Documentation changes
|
|
98
|
-
- `test(scope): description` - Test changes
|
|
99
|
-
- `chore(scope): description` - Build/tooling changes
|
|
100
|
-
|
|
101
|
-
Examples:
|
|
102
|
-
- `feat(auth): add login functionality`
|
|
103
|
-
- `fix(api): resolve timeout error`
|
|
104
|
-
- `docs(readme): update installation guide`
|
|
105
|
-
|
|
106
|
-
## Before Committing
|
|
107
|
-
|
|
108
|
-
1. Run tests: `{{PACKAGE_MANAGER}} test`
|
|
109
|
-
2. Build successfully: `{{PACKAGE_MANAGER}} build`
|
|
110
|
-
3. Lint check: `{{PACKAGE_MANAGER}} lint`
|
|
111
|
-
4. Format code: `{{PACKAGE_MANAGER}} format`
|
package/templates/fsd.md
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# Feature-Sliced Design (FSD)
|
|
5
|
-
|
|
6
|
-
프로젝트는 [FSD 공식문서](https://feature-sliced.github.io/documentation/kr/docs/get-started/overview)를 참고하여 구조화되어 있습니다.
|
|
7
|
-
|
|
8
|
-
## 폴더 구조
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
src/
|
|
12
|
-
├── app/ # 앱 설정, providers, 라우팅
|
|
13
|
-
├── entities/ # 도메인 모델, API
|
|
14
|
-
├── features/ # 사용자 기능 단위
|
|
15
|
-
├── shared/ # 공유 컴포넌트, 유틸
|
|
16
|
-
├── views/ # 페이지 컴포넌트
|
|
17
|
-
└── widgets/ # 복합 UI 블록
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Entities Layer (엔티티)
|
|
21
|
-
|
|
22
|
-
엔티티는 도메인 모델과 API 로직을 관리합니다.
|
|
23
|
-
|
|
24
|
-
### 구조
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
entities/
|
|
28
|
-
└── (엔티티 이름)/ 예: inquiry, popular-product
|
|
29
|
-
├── api/
|
|
30
|
-
│ ├── mapper/
|
|
31
|
-
│ │ ├── map-(엔티티 이름).ts
|
|
32
|
-
│ │ └── map-(엔티티 이름)-detail.ts
|
|
33
|
-
│ ├── query/
|
|
34
|
-
│ │ ├── (엔티티 이름)-list-query.ts
|
|
35
|
-
│ │ └── (엔티티 이름)-detail-query.ts
|
|
36
|
-
│ ├── get-(엔티티 이름)-list.ts
|
|
37
|
-
│ ├── get-(엔티티 이름)-detail.ts
|
|
38
|
-
│ ├── (엔티티 이름)-queries.ts
|
|
39
|
-
│ ├── mutate.ts
|
|
40
|
-
│ └── index.ts
|
|
41
|
-
├── model/
|
|
42
|
-
│ ├── (엔티티 이름).ts
|
|
43
|
-
│ ├── (엔티티 이름)-detail.ts
|
|
44
|
-
│ └── (엔티티 이름)-update.ts
|
|
45
|
-
├── schema.ts
|
|
46
|
-
└── index.ts
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 각 파일의 역할
|
|
50
|
-
|
|
51
|
-
- **mapper/**: API 응답을 도메인 모델로 변환
|
|
52
|
-
- **query/**: 쿼리 파라미터 타입 정의
|
|
53
|
-
- **get-\*-list.ts**: 리스트 조회 API 함수
|
|
54
|
-
- **get-\*-detail.ts**: 상세 조회 API 함수
|
|
55
|
-
- **\*-queries.ts**: TanStack Query queryOptions 정의 (all, lists, list, details, detail 등)
|
|
56
|
-
- **mutate.ts**: mutation 함수들
|
|
57
|
-
- **model/**: 도메인 모델 타입 정의
|
|
58
|
-
- **schema.ts**: zod 스키마 정의 및 변환 함수
|
|
59
|
-
- **index.ts**: `export * as (엔티티 이름)Api from "./api"`
|
|
60
|
-
|
|
61
|
-
### 예시
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
entities/inquiry/
|
|
65
|
-
├── api/
|
|
66
|
-
│ ├── mapper/
|
|
67
|
-
│ │ ├── map-inquiry.ts
|
|
68
|
-
│ │ └── map-inquiry-detail.ts
|
|
69
|
-
│ ├── query/
|
|
70
|
-
│ │ ├── inquiry-list-query.ts
|
|
71
|
-
│ │ └── inquiry-detail-query.ts
|
|
72
|
-
│ ├── get-inquiry-list.ts
|
|
73
|
-
│ ├── get-inquiry-detail.ts
|
|
74
|
-
│ └── inquiry-queries.ts
|
|
75
|
-
├── model/
|
|
76
|
-
│ ├── inquiry.ts
|
|
77
|
-
│ └── inquiry-detail.ts
|
|
78
|
-
└── schema.ts
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Shared Layer (공유 레이어)
|
|
82
|
-
|
|
83
|
-
프로젝트 전체에서 공유되는 코드를 관리합니다.
|
|
84
|
-
|
|
85
|
-
### 구조
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
shared/
|
|
89
|
-
├── api/ API 인스턴스 설정
|
|
90
|
-
├── constant/ 공통 상수 (path.ts 등)
|
|
91
|
-
├── hooks/ 공통 React 훅
|
|
92
|
-
├── lib/ 라이브러리 래퍼 및 설정
|
|
93
|
-
│ └── (라이브러리명)/
|
|
94
|
-
│ ├── provider.tsx
|
|
95
|
-
│ └── config.ts
|
|
96
|
-
├── ui/
|
|
97
|
-
│ ├── component/ 재사용 가능한 UI 컴포넌트
|
|
98
|
-
│ │ └── (컴포넌트명)/
|
|
99
|
-
│ │ ├── index.tsx
|
|
100
|
-
│ │ ├── types.ts
|
|
101
|
-
│ │ └── (컴포넌트명).tsx
|
|
102
|
-
│ ├── theme/ 테마 관련 CSS 파일
|
|
103
|
-
│ └── utils/ UI 관련 유틸리티 함수
|
|
104
|
-
└── utils/ 일반 유틸리티 함수
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### lib vs utils 구분 기준
|
|
108
|
-
|
|
109
|
-
#### `shared/lib/`
|
|
110
|
-
외부 라이브러리를 프로젝트에 맞게 감싸거나 설정하는 코드
|
|
111
|
-
- 라이브러리 래퍼(wrapper) 컴포넌트
|
|
112
|
-
- 라이브러리 초기 설정 및 Provider
|
|
113
|
-
- 예시: `react-hook-form/form-provider.tsx`, `tanstack-query/query-client.tsx`
|
|
114
|
-
|
|
115
|
-
#### `shared/utils/`
|
|
116
|
-
순수 유틸리티 함수 및 비즈니스 로직 헬퍼
|
|
117
|
-
- 라이브러리와 무관한 순수 함수
|
|
118
|
-
- 예시: `convert-price.ts`, `export-excel.ts`, `format-date.ts`
|
|
119
|
-
|
|
120
|
-
**판단 기준:**
|
|
121
|
-
- 라이브러리를 감싸는가? → `lib/`
|
|
122
|
-
- 순수 함수 또는 유틸인가? → `utils/`
|
|
123
|
-
|
|
124
|
-
## Views Layer (페이지 레이어)
|
|
125
|
-
|
|
126
|
-
페이지 단위의 UI와 로직을 관리합니다.
|
|
127
|
-
|
|
128
|
-
### 구조
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
views/
|
|
132
|
-
└── (페이지명)/
|
|
133
|
-
├── page.tsx 메인 페이지 컴포넌트 (필수)
|
|
134
|
-
├── (페이지명)-list-page.tsx
|
|
135
|
-
├── (페이지명)-detail-page.tsx
|
|
136
|
-
├── detail/
|
|
137
|
-
│ ├── page.tsx
|
|
138
|
-
│ ├── ui/
|
|
139
|
-
│ ├── hooks/ use-(페이지명)-detail-data.tsx 등
|
|
140
|
-
│ └── components/
|
|
141
|
-
├── list/
|
|
142
|
-
│ ├── (컬럼명)-column.tsx
|
|
143
|
-
│ ├── search.tsx
|
|
144
|
-
│ └── (모달명)-modal.tsx
|
|
145
|
-
├── hooks/
|
|
146
|
-
├── components/
|
|
147
|
-
├── utils/
|
|
148
|
-
├── schema/
|
|
149
|
-
└── constants.ts
|
|
150
|
-
```
|
package/templates/monorepo.md
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# Monorepo Rules
|
|
5
|
-
|
|
6
|
-
## Structure
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
root/
|
|
10
|
-
├── apps/
|
|
11
|
-
│ ├── web/ # 웹 앱
|
|
12
|
-
│ ├── admin/ # 어드민
|
|
13
|
-
│ └── mobile/ # 모바일 앱
|
|
14
|
-
├── packages/
|
|
15
|
-
│ ├── ui/ # 공유 UI 컴포넌트
|
|
16
|
-
│ ├── utils/ # 공유 유틸리티
|
|
17
|
-
│ ├── config/ # 공유 설정 (eslint, tsconfig)
|
|
18
|
-
│ └── types/ # 공유 TypeScript 타입
|
|
19
|
-
├── package.json
|
|
20
|
-
└── pnpm-workspace.yaml
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Package Management
|
|
24
|
-
- Use workspace protocol for internal packages (`workspace:*`)
|
|
25
|
-
- Keep shared dependencies in root `package.json`
|
|
26
|
-
- App-specific dependencies in app's `package.json`
|
|
27
|
-
|
|
28
|
-
```json
|
|
29
|
-
{
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"@repo/ui": "workspace:*",
|
|
32
|
-
"@repo/utils": "workspace:*"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Build Order
|
|
38
|
-
- Build packages before apps
|
|
39
|
-
- Use Turborepo for build orchestration
|
|
40
|
-
- Configure proper `dependsOn` in turbo.json
|
|
41
|
-
|
|
42
|
-
## Shared Configurations
|
|
43
|
-
- Extend base tsconfig in all packages
|
|
44
|
-
- Share ESLint config from `packages/config`
|
|
45
|
-
- Maintain consistent formatting across packages
|
|
46
|
-
|
|
47
|
-
## Commands
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# 전체 빌드
|
|
51
|
-
pnpm build
|
|
52
|
-
|
|
53
|
-
# 특정 앱만 빌드
|
|
54
|
-
pnpm --filter @repo/admin build
|
|
55
|
-
|
|
56
|
-
# 특정 앱 dev 서버
|
|
57
|
-
pnpm --filter @repo/web dev
|
|
58
|
-
|
|
59
|
-
# 전체 린트
|
|
60
|
-
pnpm lint
|
|
61
|
-
```
|
package/templates/nextjs.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# Next.js Rules
|
|
5
|
-
|
|
6
|
-
## App Router
|
|
7
|
-
- Use Server Components by default
|
|
8
|
-
- Add `'use client'` only when needed (hooks, browser APIs, interactivity)
|
|
9
|
-
- Colocate loading.tsx, error.tsx, not-found.tsx with pages
|
|
10
|
-
- Use route groups `(group)` for organization without affecting URL
|
|
11
|
-
|
|
12
|
-
## File Structure
|
|
13
|
-
```
|
|
14
|
-
app/
|
|
15
|
-
├── (auth)/
|
|
16
|
-
│ ├── login/page.tsx
|
|
17
|
-
│ └── register/page.tsx
|
|
18
|
-
├── (main)/
|
|
19
|
-
│ ├── dashboard/page.tsx
|
|
20
|
-
│ └── settings/page.tsx
|
|
21
|
-
├── api/
|
|
22
|
-
│ └── [...route]/route.ts
|
|
23
|
-
├── layout.tsx
|
|
24
|
-
└── page.tsx
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Data Fetching
|
|
28
|
-
- Prefer Server Components for data fetching
|
|
29
|
-
- Use `fetch` with caching options in Server Components
|
|
30
|
-
- Use React Query/SWR for client-side data fetching
|
|
31
|
-
- Implement proper loading and error states
|
|
32
|
-
|
|
33
|
-
## Environment Variables
|
|
34
|
-
- `NEXT_PUBLIC_*` for client-side variables
|
|
35
|
-
- Server-only variables without prefix
|
|
36
|
-
- Use `.env.local` for local development
|
|
37
|
-
|
|
38
|
-
## SSR Safety
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
// ❌ Wrong - breaks SSR
|
|
42
|
-
const token = localStorage.getItem('token');
|
|
43
|
-
|
|
44
|
-
// ✅ Correct - SSR safe
|
|
45
|
-
const token = typeof window !== 'undefined'
|
|
46
|
-
? localStorage.getItem('token')
|
|
47
|
-
: null;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## SSR-Safe Hook Pattern
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
const [value, setValue] = useState(() => {
|
|
54
|
-
if (typeof window === 'undefined') return defaultValue;
|
|
55
|
-
return window.localStorage.getItem(key);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (typeof window === 'undefined') return;
|
|
60
|
-
// Browser-only code
|
|
61
|
-
}, []);
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Image Optimization
|
|
65
|
-
- Always use `next/image` for images
|
|
66
|
-
- Provide width and height or use `fill`
|
|
67
|
-
- Use appropriate `sizes` prop for responsive images
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# Project Specific Rules (프로젝트별 추가 규칙)
|
|
5
|
-
|
|
6
|
-
<!-- 여기에 프로젝트 고유의 규칙을 추가하세요 -->
|
|
7
|
-
|
|
8
|
-
## Custom Rules
|
|
9
|
-
- Add your project-specific rules here
|
|
10
|
-
- Document any exceptions to common rules
|
|
11
|
-
- Include team agreements and decisions
|
|
12
|
-
|
|
13
|
-
## API Conventions
|
|
14
|
-
- Document API patterns used in this project
|
|
15
|
-
- Specify error handling strategies
|
|
16
|
-
- Define response/request formats
|
|
17
|
-
|
|
18
|
-
## State Management
|
|
19
|
-
- Document which state library is used
|
|
20
|
-
- Define when to use local vs global state
|
|
21
|
-
- Specify state structure conventions
|
package/templates/testing.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# 테스트 전략
|
|
5
|
-
|
|
6
|
-
## 테스트 작성 원칙
|
|
7
|
-
|
|
8
|
-
### 컴포넌트 (Component) - 단위 테스트
|
|
9
|
-
|
|
10
|
-
- **대상**: `shared/ui/component/`, 재사용 가능한 컴포넌트
|
|
11
|
-
- **목적**: 컴포넌트의 독립적인 동작 검증
|
|
12
|
-
- **방법**:
|
|
13
|
-
- Props와 사용자 상호작용에 집중
|
|
14
|
-
- 외부 의존성(API, 라우팅)은 mock 처리
|
|
15
|
-
- 렌더링, 이벤트 핸들링, 상태 변경 검증
|
|
16
|
-
|
|
17
|
-
### 페이지 (Page) - 기능 테스트
|
|
18
|
-
|
|
19
|
-
- **대상**: `views/*/page.tsx` 페이지 컴포넌트
|
|
20
|
-
- **목적**: 사용자 시나리오와 비즈니스 로직 검증
|
|
21
|
-
- **Mock 최소화 원칙**:
|
|
22
|
-
- 외부 의존성(API 호출, toast)만 mock
|
|
23
|
-
- 내부 컴포넌트는 실제로 렌더링하여 통합 테스트
|
|
24
|
-
- `data-testid` 대신 접근성 기반 쿼리 사용
|
|
25
|
-
|
|
26
|
-
## Testing Library 쿼리 우선순위
|
|
27
|
-
|
|
28
|
-
**1순위 (가장 권장):**
|
|
29
|
-
- `getByRole` - 접근성 역할 기반 (button, textbox, checkbox 등)
|
|
30
|
-
- `getByLabelText` - label과 연결된 폼 요소
|
|
31
|
-
- `getByPlaceholderText` - placeholder가 있는 입력 필드
|
|
32
|
-
- `getByText` - 텍스트 내용으로 찾기
|
|
33
|
-
- `getByDisplayValue` - 입력 필드의 현재 값
|
|
34
|
-
|
|
35
|
-
**2순위:**
|
|
36
|
-
- `getByAltText` - 이미지의 alt 텍스트
|
|
37
|
-
- `getByTitle` - title 속성
|
|
38
|
-
|
|
39
|
-
**3순위 (최후의 수단):**
|
|
40
|
-
- `getByTestId` - `data-testid` (가능한 한 피해야 함)
|
|
41
|
-
|
|
42
|
-
**예시:**
|
|
43
|
-
```typescript
|
|
44
|
-
// ✅ 좋은 예
|
|
45
|
-
screen.getByRole('button', { name: '저장' });
|
|
46
|
-
screen.getByRole('checkbox');
|
|
47
|
-
screen.getByText('셀러 정보를 찾을 수 없습니다.');
|
|
48
|
-
screen.getByLabelText(/대표자명/);
|
|
49
|
-
|
|
50
|
-
// ❌ 나쁜 예
|
|
51
|
-
screen.getByTestId('save-button');
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## 테스트 파일 위치
|
|
55
|
-
|
|
56
|
-
- 컴포넌트와 같은 디렉토리에 `*.test.tsx`
|
|
57
|
-
|
|
58
|
-
## 테스트 구조
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
describe('ComponentName', () => {
|
|
62
|
-
it('should render correctly', () => {});
|
|
63
|
-
it('should handle user interaction', () => {});
|
|
64
|
-
it('should show error state', () => {});
|
|
65
|
-
});
|
|
66
|
-
```
|
package/templates/vite.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
---
|
|
3
|
-
|
|
4
|
-
# Vite + React Rules
|
|
5
|
-
|
|
6
|
-
## Environment Variables
|
|
7
|
-
- Use `import.meta.env.VITE_*` for environment variables
|
|
8
|
-
- Only `VITE_` prefixed variables are exposed to client
|
|
9
|
-
|
|
10
|
-
```typescript
|
|
11
|
-
// ✅ Correct
|
|
12
|
-
const apiUrl = import.meta.env.VITE_API_URL;
|
|
13
|
-
|
|
14
|
-
// ❌ Wrong - won't work
|
|
15
|
-
const secret = import.meta.env.SECRET_KEY;
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## File Structure
|
|
19
|
-
```
|
|
20
|
-
src/
|
|
21
|
-
├── assets/ # Static assets
|
|
22
|
-
├── components/ # React components
|
|
23
|
-
├── hooks/ # Custom hooks
|
|
24
|
-
├── pages/ # Page components
|
|
25
|
-
├── routes/ # Routing configuration
|
|
26
|
-
├── services/ # API services
|
|
27
|
-
├── stores/ # State management
|
|
28
|
-
├── utils/ # Utility functions
|
|
29
|
-
└── main.tsx # Entry point
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Path Aliases
|
|
33
|
-
- Use `@/` alias for `src/` directory
|
|
34
|
-
- Configure in `vite.config.ts` and `tsconfig.json`
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// vite.config.ts
|
|
38
|
-
resolve: {
|
|
39
|
-
alias: {
|
|
40
|
-
'@': path.resolve(__dirname, './src'),
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Build Optimization
|
|
46
|
-
- Use dynamic imports for code splitting
|
|
47
|
-
- Lazy load routes and heavy components
|
|
48
|
-
- Analyze bundle size with `rollup-plugin-visualizer`
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
// Lazy loading example
|
|
52
|
-
const Dashboard = lazy(() => import('./pages/Dashboard'));
|
|
53
|
-
```
|