byuckchon-frontend-cli 1.9.2 → 1.9.3

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/README.md CHANGED
@@ -180,10 +180,10 @@ FE 전반의 규칙(폴더 구조, 네이밍, 스웨거 → 코드 변환 규칙
180
180
  | 프레임워크 | 위치 |
181
181
  | --- | --- |
182
182
  | React (Vite/CRA 등) | `src/api/api-codegen.md` |
183
- | Next.js | `lib/api/api-codegen.md` |
183
+ | Next.js | `src/lib/api/api-codegen.md` |
184
184
 
185
185
  - 이 파일은 `bc.config.json` 의 `docs` 에 자동 등록되어 **chat 시작 시 주입**됩니다.
186
- - 하나의 .md React/Next 모두 다루며(차이는 위치뿐), 규칙에 맞게 직접 다듬어 쓰면 됩니다.
186
+ - 하나의 문서에 React axios 규칙과 Next.js의 fetch Server/Client 경계 규칙이 함께 들어갑니다.
187
187
  - 이미 파일이 있으면 덮어쓰지 않습니다.
188
188
 
189
189
  ### OpenAPI / 코드 컨텍스트 — 자동 주입 (v1.4+)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "byuckchon-frontend-cli",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "description": "Byuckchon Frontend Workbench — project starter + AI chat + codebase RAG + OpenAPI codegen",
5
5
  "type": "module",
6
6
  "engines": {
@@ -10,12 +10,6 @@
10
10
  "byuckchon-frontend-cli": "./bin/index.js",
11
11
  "bc": "./bin/index.js"
12
12
  },
13
- "scripts": {
14
- "start": "node bin/index.js",
15
- "chat": "node bin/index.js chat",
16
- "init": "node bin/index.js init",
17
- "config": "node bin/index.js config show"
18
- },
19
13
  "dependencies": {
20
14
  "@ai-sdk/anthropic": "^3.0.85",
21
15
  "@ai-sdk/openai": "^3.0.73",
@@ -65,5 +59,11 @@
65
59
  "repository": {
66
60
  "type": "git",
67
61
  "url": "git+https://github.com/RevolutionaryWarrior/byuckchon-frontend-cli.git"
62
+ },
63
+ "scripts": {
64
+ "start": "node bin/index.js",
65
+ "chat": "node bin/index.js chat",
66
+ "init": "node bin/index.js init",
67
+ "config": "node bin/index.js config show"
68
68
  }
69
- }
69
+ }
@@ -145,7 +145,7 @@ export async function adoptCommand(opts = {}) {
145
145
  console.log(chalk.green(`\n ✓ ${CONFIG_PATHS.projectFileName} 작성 완료.`));
146
146
  console.log(chalk.dim(` ${targetFile}`));
147
147
 
148
- // API 코드 컨벤션 .md 를 API 루트(src/api | lib/api)에 깐다 (이미 있으면 유지).
148
+ // API 코드 컨벤션 .md 를 API 루트(src/api | src/lib/api)에 깐다 (이미 있으면 유지).
149
149
  try {
150
150
  const { relPath, written } = await scaffoldApiConventionDoc({
151
151
  projectRoot: cwd,
@@ -9,15 +9,15 @@ const TEMPLATE_PATH = path.resolve(
9
9
 
10
10
  /**
11
11
  * 프레임워크에 맞는 API 루트 폴더.
12
- * - Next.js → lib/api
12
+ * - Next.js → src/lib/api
13
13
  * - 그 외 React 계열 → src/api
14
14
  */
15
15
  export function apiRootForFramework(framework) {
16
- return framework === 'next' ? 'lib/api' : 'src/api';
16
+ return framework === 'next' ? 'src/lib/api' : 'src/api';
17
17
  }
18
18
 
19
19
  /**
20
- * API 코드 컨벤션 .md 를 프로젝트의 API 루트(`src/api` 또는 `lib/api`)에 깐다.
20
+ * API 코드 컨벤션 .md 를 프로젝트의 API 루트(`src/api` 또는 `src/lib/api`)에 깐다.
21
21
  *
22
22
  * @param {object} args
23
23
  * @param {string} args.projectRoot
@@ -26,7 +26,7 @@ export async function createProject(config) {
26
26
  await createPackageJson(rootDir, config);
27
27
  await createBaseFiles(rootDir, config);
28
28
  await createReadme(rootDir, config);
29
- // API 코드 컨벤션 .md 를 프레임워크에 맞는 API 루트(src/api | lib/api)에 깐다.
29
+ // API 코드 컨벤션 .md 를 프레임워크에 맞는 API 루트(src/api | src/lib/api)에 깐다.
30
30
  await scaffoldApiConventionDoc({ projectRoot: rootDir, framework: config.framework });
31
31
  await createBcConfig(rootDir, config);
32
32
 
@@ -4,7 +4,8 @@
4
4
  > 사용자가 Swagger JSON(또는 엔드포인트)을 제공하면, AI 는 이 문서의 규칙에 맞춰
5
5
  > `api / zod / type / service / index` 파일을 생성한다.
6
6
  >
7
- > 스택: `@tanstack/react-query` + `axios`. **React / Next.js 공용**이며, 차이는 §0 의 "위치"뿐이다.
7
+ > 대상: React(Vite/CRA 등), Next.js App Router
8
+ > 스택: `@tanstack/react-query` + React는 `axios`, Next.js는 native `fetch`.
8
9
 
9
10
  ---
10
11
 
@@ -16,30 +17,25 @@
16
17
  2. **기존 코드 우선.** 공용 유틸(`cacheConfig`, `queryKey`, `captureSentryError`, `metaSchema`,
17
18
  `PaginationParams` 등)은 새로 만들지 말고 그대로 가져다 쓴다. import 경로가 확실치 않으면
18
19
  `search_code` 로 실제 export 위치를 확인한다.
19
- 3. **5파일 세트는 항상 함께.** `api / zod / type / service / index` 한 번에 생성한다.
20
+ 3. **계약 파일은 항상 함께.** React는 `api / zod / type / service / index`를 한 번에 생성한다.
21
+ Next.js는 공용 `zod / type`, 서버 기본 호출 파일, 필요한 경우 Client 호출 파일과 service를 생성한다.
22
+ 이미 같은 리소스가 있으면 기존 파일 구성과 이름을 우선한다.
20
23
 
21
24
  ---
22
25
 
23
- ## 0. 위치 & 폴더 구조 ⚠️ (React vs Next 차이는 여기뿐)
26
+ ## 0. 위치 & 폴더 구조
24
27
 
25
- API 코드의 **루트 위치는 프레임워크마다 다르다.**
26
-
27
- | 프레임워크 | API 루트 |
28
- | --- | --- |
29
- | **React** (Vite/CRA 등) | `src/api` |
30
- | **Next.js** | `lib/api` |
31
-
32
- > 그 외 폴더/파일 구조와 규칙은 **완전히 동일**하다. 아래 예시는 `src/api` 기준이며,
33
- > Next 면 `src/api` 를 `lib/api` 로 바꿔 읽으면 된다.
28
+ API 루트는 React에서 `src/api`, Next.js에서 `src/lib/api`다.
34
29
 
35
30
  리소스(도메인) 하나당 폴더 하나. 폴더명은 **소문자**(여러 단어는 kebab-case: `favorite-stores`).
36
31
 
37
32
  ```
38
- src/api/ # (Next: lib/api/)
39
- ├── instance.ts # axios 인스턴스 (interceptor 포함)
33
+ src/api/ # Next.js: src/lib/api/
34
+ ├── instance.ts # React: axios 인스턴스
40
35
  ├── index.ts # 모든 API 모듈 export
41
36
  └── user/ # 리소스 폴더 (소문자)
42
- ├── user.api.ts # axios 호출 함수 (순수 함수)
37
+ ├── user.api.ts # React: axios / Next.js: Server fetch 호출
38
+ ├── user.api.client.ts # Next.js: Client 호출이 필요할 때만 생성
43
39
  ├── user.zod.ts # 응답/요청 zod 스키마
44
40
  ├── user.type.ts # zod 로부터 추론한 타입 + 입력 타입
45
41
  ├── user.service.ts # react-query 훅 (use~) — 비즈니스 로직
@@ -51,6 +47,67 @@ src/api/ # (Next: lib/api/)
51
47
 
52
48
  ---
53
49
 
50
+ ## Next.js 전용 규칙 — Server/Client 경계
51
+
52
+ > 이 섹션의 **Next.js**는 프레임워크다. 문서의 **next(무한스크롤)**와는 관련이 없다.
53
+
54
+ Next.js App Router는 API 코드를 생성하기 전에 실행 환경을 분류한다.
55
+
56
+ | 환경 | 사용처 | 허용 의존성 |
57
+ | --- | --- | --- |
58
+ | Server | Server Component, Route Handler, Server Action | `cookies`, `headers`, 서버 토큰, `serverAuthHttp` |
59
+ | Client | `'use client'`, React Query hook | 브라우저 API, `clientAuthHttp` |
60
+ | 공용 | type, 순수 zod schema, 직렬화 가능한 상수 | 서버/브라우저 전용 의존성 없음 |
61
+
62
+ ### 공용 barrel에서 Server 모듈을 내보내지 않는다
63
+
64
+ Client Component가 공용 `index.ts`를 가져올 때 그 barrel이 Server 모듈까지 export하면 다음 오류가 발생할 수 있다.
65
+
66
+ ```text
67
+ This module cannot be imported from a Client Component module.
68
+ It should only be used from a Server Component.
69
+ ```
70
+
71
+ ```ts
72
+ // src/lib/api/http/index.ts
73
+ export * from './httpBase';
74
+ export * from './publicHttp';
75
+ export * from './clientAuthHttp';
76
+ // 금지: export * from './serverAuthHttp';
77
+ ```
78
+
79
+ `serverAuthHttp`와 Server API는 barrel을 거치지 않고 절대경로로 직접 import한다.
80
+
81
+ ```ts
82
+ import { serverAuthHttp } from '@/lib/api/http/serverAuthHttp';
83
+ ```
84
+
85
+ - Server 파일은 가능하면 `import 'server-only';`로 경계를 표시한다.
86
+ - 인증 호출은 Server를 기본으로 하고 Client에서도 호출해야 할 때만 `*.client.ts`를 만든다.
87
+ - Client service는 Client 호출 파일만 import하며 Server 파일을 간접 참조하지 않는다.
88
+ - `useQuery`/`useMutation`을 export하는 service는 Client 전용이다.
89
+ - type과 zod schema만 Server/Client가 공유한다.
90
+
91
+ ### HTTP와 인증 컨텍스트를 분리한다
92
+
93
+ - `serverAuthHttp`: 서버 쿠키·헤더·비공개 토큰 사용. 공용 barrel export 금지.
94
+ - `clientAuthHttp`: 브라우저에 노출 가능한 인증 상태만 사용.
95
+ - `publicHttp`: 양쪽에서 안전할 때만 공용 사용.
96
+ - 기존 fetch wrapper를 먼저 찾는다. 없으면 자동 생성하지 말고 fetch/auth 방식을 사용자에게 묻는다.
97
+ - `cookies()`/`headers()`는 모듈 최상위가 아니라 요청 함수 내부에서 호출한다.
98
+ - 서버 비밀값에 `NEXT_PUBLIC_`을 붙이지 않는다.
99
+ - 기존 Route Handler/BFF, base URL, CORS, Edge Runtime 정책을 확인하고 재사용한다.
100
+
101
+ ### 캐시와 전달 값을 구분한다
102
+
103
+ - React Query 무효화는 Client 캐시만 갱신한다.
104
+ - Next.js의 `fetch` cache/`next`, `revalidatePath`/`revalidateTag`, Cache Components는 기존 정책을 따른다.
105
+ - 사용자별 인증 응답을 전역 캐시하지 않는다.
106
+ - Server에서 Client로 응답 wrapper, `Headers`, `Error`, 함수 등을 전달하지 않고 JSON 직렬화 가능한 data만 전달한다.
107
+ - Server Action은 요청받았거나 기존 프로젝트가 같은 패턴을 사용할 때만 만든다.
108
+
109
+ ---
110
+
54
111
  ## 1. 가장 먼저 — "next(무한스크롤)" 인지 판단하라
55
112
 
56
113
  > 여기서 말하는 "next" 는 **프레임워크 Next.js 가 아니라**, **커서 기반 무한스크롤 패턴**을 가리킨다.
@@ -76,13 +133,17 @@ src/api/ # (Next: lib/api/)
76
133
 
77
134
  ### 2-1. `user.api.ts` — 순수 호출 함수
78
135
 
79
- - `import baseInstance from '../instance';` 사용 (axios 인스턴스).
80
- - 함수는 `async`, 내부에서 `const { data } = await baseInstance.X(...)` 후 `return data;`.
81
- - 응답 본문이 없는 경우(`204 No Content`) `return data` 생략 가능.
82
- - 쿼리스트링은 `{ params }`, path 파라미터는 템플릿 리터럴.
136
+ - React는 `baseInstance`(axios)를 사용한다.
137
+ - Next.js는 Server 파일에서 기존 `serverAuthHttp`를 절대경로로 import한다. Client 호출이 필요할 때만
138
+ `*.client.ts`를 만들고 기존 `clientAuthHttp`를 사용한다. wrapper가 없으면 자동 생성하지 않는다.
139
+ - React는 `const { data } = await baseInstance.X(...)` 후 `data`를 반환한다.
140
+ - Next.js는 fetch wrapper의 generic에 응답 타입을 전달하고 JSON data를 반환받는다.
141
+ - 쿼리스트링은 기존 wrapper 규칙(axios의 `{ params }`, fetch wrapper의 `searchParams` 등)을 따른다.
142
+ path 파라미터는 템플릿 리터럴을 사용한다.
83
143
  - **여기서는 zod 파싱을 하지 않는다.** (파싱은 service 의 queryFn 책임)
84
144
 
85
145
  ```ts
146
+ // React
86
147
  import baseInstance from '../instance';
87
148
 
88
149
  const RESOURCE = '/api/user';
@@ -106,6 +167,18 @@ export const deleteUser = async (userId: string) => {
106
167
  };
107
168
  ```
108
169
 
170
+ ```ts
171
+ // Next.js Server
172
+ import { serverAuthHttp } from '@/lib/api/http/serverAuthHttp';
173
+ import type { UserItem } from './user.type';
174
+
175
+ const RESOURCE = '/api/user';
176
+
177
+ export const getUser = async (userId: string) => {
178
+ return serverAuthHttp<UserItem>(`${RESOURCE}/${userId}`);
179
+ };
180
+ ```
181
+
109
182
  ### 2-2. `user.zod.ts` — 스키마
110
183
 
111
184
  - `import { z } from 'zod';`
@@ -213,6 +286,7 @@ export const useDeleteUser = () => {
213
286
  ### 2-5. `index.ts` (리소스) — 노출
214
287
 
215
288
  - **`service` 와 `type` 만** 재노출 (`api`, `zod` 는 노출하지 않음).
289
+ - Next.js에서는 Server 호출 파일을 Client가 접근 가능한 barrel에서 재노출하지 않는다.
216
290
 
217
291
  ```ts
218
292
  export * from './user.service';
@@ -230,6 +304,7 @@ export * from './user';
230
304
 
231
305
  ### 2-7. `src/api/instance.ts` — axios 인스턴스
232
306
 
307
+ - React 전용 규칙이다.
233
308
  - 이미 있으면 **건드리지 않는다.** baseURL/interceptor 설정이 여기 모여 있다.
234
309
  - 새 리소스는 항상 이 `baseInstance` 를 import 해서 쓴다.
235
310
 
@@ -374,7 +449,7 @@ export const useGetUser = (options?: Record<string, any>) => {
374
449
 
375
450
  ## 7. 생성 시 체크리스트 ✅
376
451
 
377
- 1. [ ] 위치를 맞췄다 (React `src/api` / Next `lib/api`).
452
+ 1. [ ] 위치를 맞췄다 (React `src/api` / Next.js `src/lib/api`).
378
453
  2. [ ] 엔드포인트가 **next(무한스크롤)** 인지 판단했다. (§1)
379
454
  3. [ ] `api / zod / type / service / index` 5파일을 모두 만들었다.
380
455
  4. [ ] Swagger 응답을 zod 로 정확히 매핑(nullable/optional/enum/date/$ref).
@@ -385,6 +460,8 @@ export const useGetUser = (options?: Record<string, any>) => {
385
460
  9. [ ] 리소스 `index.ts` + 루트 `index.ts` 노출 추가.
386
461
  10. [ ] next 면 `useInfiniteQuery` + `metaSchema` + `select` 평탄화 적용.
387
462
  11. [ ] 공용 유틸을 재사용하고, Swagger 에 없는 필드를 추측으로 만들지 않았다.
463
+ 12. [ ] Next.js면 Server/Client 환경을 분류하고 Server 모듈을 공용 barrel에서 내보내지 않았다.
464
+ 13. [ ] Next.js면 기존 fetch/auth wrapper와 캐시·runtime 정책을 확인했다.
388
465
 
389
466
  ---
390
467
 
@@ -397,4 +474,5 @@ export const useGetUser = (options?: Record<string, any>) => {
397
474
  - ❌ Swagger 의 `nullable` 무시하고 필수로 선언.
398
475
  - ❌ 일반 목록인데 `useInfiniteQuery` 사용 (또는 그 반대).
399
476
  - ❌ 공용 타입/유틸(`metaSchema`, `PaginationParams`)을 중복 재정의.
400
- - ❌ React 인데 `lib/api`, Next 인데 `src/api` 만드는 위치 실수.
477
+ - ❌ React Next.js의 API 루트 또는 axios/fetch 규칙을 혼용.
478
+ - ❌ Next.js 공용 `index.ts`에서 Server 전용 API나 `serverAuthHttp`를 export.