@blastlabs/utils 1.17.0 โ†’ 1.19.0

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
@@ -255,6 +255,89 @@ function App() {
255
255
  import type { FormDevToolsProps, ApiLogEntry } from '@blastlabs/utils/components/dev';
256
256
  ```
257
257
 
258
+ ## ๐Ÿš€ CLI ๋„๊ตฌ
259
+
260
+ ์ด ํŒจํ‚ค์ง€๋Š” ํ”„๋กœ์ ํŠธ ์„ค์ •์„ ์ž๋™ํ™”ํ•˜๋Š” CLI ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
261
+
262
+ ### FSD Entity Generator
263
+
264
+ Feature-Sliced Design ์•„ํ‚คํ…์ฒ˜์šฉ ์—”ํ‹ฐํ‹ฐ ์Šค์บํด๋”ฉ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
265
+
266
+ ```bash
267
+ # entities ํด๋”๋กœ ์ด๋™
268
+ cd src/entities
269
+
270
+ # ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ
271
+ npx blastlabs-generate-entity user
272
+ npx blastlabs-generate-entity my-new-entity
273
+ ```
274
+
275
+ **์ƒ์„ฑ๋˜๋Š” ๊ตฌ์กฐ:**
276
+ ```
277
+ user/
278
+ โ”œโ”€โ”€ index.ts
279
+ โ”œโ”€โ”€ api/
280
+ โ”‚ โ”œโ”€โ”€ get-user-list.ts
281
+ โ”‚ โ”œโ”€โ”€ get-user-detail.ts
282
+ โ”‚ โ”œโ”€โ”€ user-queries.ts
283
+ โ”‚ โ”œโ”€โ”€ index.ts
284
+ โ”‚ โ”œโ”€โ”€ mapper/
285
+ โ”‚ โ”‚ โ”œโ”€โ”€ map-user.ts
286
+ โ”‚ โ”‚ โ””โ”€โ”€ map-user-detail.ts
287
+ โ”‚ โ””โ”€โ”€ query/
288
+ โ”‚ โ””โ”€โ”€ user-list-query.ts
289
+ โ””โ”€โ”€ model/
290
+ โ”œโ”€โ”€ user.ts
291
+ โ””โ”€โ”€ user-detail.ts
292
+ ```
293
+
294
+ **ํŠน์ง•:**
295
+ - TanStack Query queryOptions ํŒจํ„ด ์ง€์›
296
+ - Zod ์Šคํ‚ค๋งˆ ์ž๋™ ์ƒ์„ฑ
297
+ - API mapper ํŒจํ„ด ์ ์šฉ
298
+ - TypeScript ํƒ€์ž… ์•ˆ์ •์„ฑ ๋ณด์žฅ
299
+
300
+ ### AI Rules Installer
301
+
302
+ Cursor, Claude Code์šฉ AI ๊ทœ์น™์„ ํ”„๋กœ์ ํŠธ์— ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
303
+
304
+ ```bash
305
+ # ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์—์„œ ์‹คํ–‰
306
+ npx blastlabs-init-ai-rules [options]
307
+ ```
308
+
309
+ **์˜ต์…˜:**
310
+ - `--fsd` - FSD ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ํฌํ•จ
311
+ - `--all` - ๋ชจ๋“  ๊ทœ์น™ ์„ค์น˜ (base + fsd)
312
+ - `--list, -l` - ์„ค์น˜ ๊ฐ€๋Šฅํ•œ ๊ทœ์น™ ๋ชฉ๋ก ๋ณด๊ธฐ
313
+ - `--help, -h` - ๋„์›€๋ง ๋ณด๊ธฐ
314
+
315
+ **์˜ˆ์‹œ:**
316
+ ```bash
317
+ # ๊ธฐ๋ณธ ๊ทœ์น™๋งŒ ์„ค์น˜
318
+ npx blastlabs-init-ai-rules
319
+
320
+ # FSD ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ํฌํ•จ
321
+ npx blastlabs-init-ai-rules --fsd
322
+
323
+ # ๋ชจ๋“  ๊ทœ์น™ ์„ค์น˜
324
+ npx blastlabs-init-ai-rules --all
325
+ ```
326
+
327
+ **์„ค์น˜๋˜๋Š” ํŒŒ์ผ:**
328
+ ```
329
+ .cursor/rules/*.mdc # Cursor์šฉ ๊ทœ์น™
330
+ .claude/rules/*.md # Claude Code์šฉ ๊ทœ์น™
331
+ ```
332
+
333
+ **ํฌํ•จ๋œ ๊ทœ์น™:**
334
+ - TypeScript ํ‘œ์ค€
335
+ - React Hooks ๊ฐ€์ด๋“œ๋ผ์ธ
336
+ - ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๊ฐ€์ด๋“œ
337
+ - ๋ฌธ์„œํ™” ๊ฐ€์ด๋“œ๋ผ์ธ
338
+ - Git ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜
339
+ - FSD ์•„ํ‚คํ…์ฒ˜ (์„ ํƒ)
340
+
258
341
  ## ์ฃผ์˜์‚ฌํ•ญ
259
342
 
260
343
  - **๊ฐœ๋ฐœ์šฉ ์ปดํฌ๋„ŒํŠธ(`components/dev`)๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์ œ์™ธํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.**
@@ -7,10 +7,13 @@ const path = require("path");
7
7
  const RULE_CATEGORIES = {
8
8
  // ๊ธฐ๋ณธ ๊ทœ์น™ (ํ•ญ์ƒ ํฌํ•จ)
9
9
  base: [
10
+ "development-workflow.md",
10
11
  "typescript-standards.md",
12
+ "naming-convention.md",
11
13
  "react-hooks.md",
12
14
  "testing.md",
13
15
  "documentation.md",
16
+ "changelog.md",
14
17
  "git-commit.md",
15
18
  ],
16
19
  // FSD ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™
@@ -28,6 +31,7 @@ function parseArgs(args) {
28
31
  all: false,
29
32
  list: false,
30
33
  help: false,
34
+ force: false,
31
35
  };
32
36
 
33
37
  for (const arg of args) {
@@ -39,6 +43,8 @@ function parseArgs(args) {
39
43
  options.all = true;
40
44
  } else if (arg === "--list" || arg === "-l") {
41
45
  options.list = true;
46
+ } else if (arg === "--force" || arg === "-f") {
47
+ options.force = true;
42
48
  }
43
49
  }
44
50
 
@@ -52,12 +58,14 @@ function showHelp() {
52
58
  console.log("Options:");
53
59
  console.log(" --fsd FSD ์•„ํ‚คํ…์ฒ˜ ๊ทœ์น™ ํฌํ•จ");
54
60
  console.log(" --all ๋ชจ๋“  ๊ทœ์น™ ์„ค์น˜ (base + fsd)");
61
+ console.log(" --force, -f ๊ธฐ์กด ํŒŒ์ผ ๋ฎ์–ด์“ฐ๊ธฐ");
55
62
  console.log(" --list, -l ์„ค์น˜ ๊ฐ€๋Šฅํ•œ ๊ทœ์น™ ๋ชฉ๋ก ๋ณด๊ธฐ");
56
63
  console.log(" --help, -h ๋„์›€๋ง ๋ณด๊ธฐ");
57
64
  console.log("\n์˜ˆ์‹œ:");
58
65
  console.log(" npx blastlabs-init-ai-rules # ๊ธฐ๋ณธ ๊ทœ์น™๋งŒ");
59
66
  console.log(" npx blastlabs-init-ai-rules --fsd # ๊ธฐ๋ณธ + FSD ๊ทœ์น™");
60
67
  console.log(" npx blastlabs-init-ai-rules --all # ๋ชจ๋“  ๊ทœ์น™");
68
+ console.log(" npx blastlabs-init-ai-rules --force # ๊ธฐ์กด ํŒŒ์ผ ๋ฎ์–ด์“ฐ๊ธฐ");
61
69
  console.log("\n์ƒ์„ฑ๋˜๋Š” ํŒŒ์ผ:");
62
70
  console.log(" .cursor/rules/*.mdc - Cursor์šฉ ๊ทœ์น™ ํŒŒ์ผ๋“ค");
63
71
  console.log(" .claude/rules/*.md - Claude Code์šฉ ๊ทœ์น™ ํŒŒ์ผ๋“ค");
@@ -139,9 +147,14 @@ function serializeCursorFrontmatter(frontmatter) {
139
147
  if (frontmatter.description) {
140
148
  yaml += `description: ${frontmatter.description}\n`;
141
149
  }
142
- if (frontmatter.globs) {
143
- yaml += `globs: "${frontmatter.globs}"\n`;
150
+ // Cursor๋Š” globs ์‚ฌ์šฉ (paths๋ฅผ globs๋กœ ๋ณ€ํ™˜)
151
+ // ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ํ•œ ์ค„๋กœ ๋ณ€ํ™˜
152
+ const globs = frontmatter.globs || frontmatter.paths;
153
+ if (globs) {
154
+ const globsStr = Array.isArray(globs) ? globs.join(",") : globs;
155
+ yaml += `globs: "${globsStr}"\n`;
144
156
  }
157
+ // alwaysApply๋Š” Cursor ์ „์šฉ
145
158
  if (frontmatter.alwaysApply !== undefined) {
146
159
  yaml += `alwaysApply: ${frontmatter.alwaysApply}\n`;
147
160
  }
@@ -170,7 +183,7 @@ function serializeClaudeFrontmatter(frontmatter) {
170
183
  return yaml;
171
184
  }
172
185
 
173
- function generateCursorRules(ruleFiles, sourceDir, targetDir) {
186
+ function generateCursorRules(ruleFiles, sourceDir, targetDir, force = false) {
174
187
  const cursorDir = path.join(targetDir, ".cursor", "rules");
175
188
  if (!fs.existsSync(cursorDir)) {
176
189
  fs.mkdirSync(cursorDir, { recursive: true });
@@ -191,7 +204,7 @@ function generateCursorRules(ruleFiles, sourceDir, targetDir) {
191
204
  continue;
192
205
  }
193
206
 
194
- if (fs.existsSync(targetPath)) {
207
+ if (fs.existsSync(targetPath) && !force) {
195
208
  console.log(` โญ๏ธ ${targetName} - ์ด๋ฏธ ์กด์žฌํ•จ, ๊ฑด๋„ˆ๋œ€`);
196
209
  skipped++;
197
210
  continue;
@@ -202,14 +215,15 @@ function generateCursorRules(ruleFiles, sourceDir, targetDir) {
202
215
  const output = serializeCursorFrontmatter(frontmatter) + body;
203
216
 
204
217
  fs.writeFileSync(targetPath, output);
205
- console.log(` โœ… ${targetName}`);
218
+ const action = force && fs.existsSync(targetPath) ? "๐Ÿ”„" : "โœ…";
219
+ console.log(` ${action} ${targetName}`);
206
220
  installed++;
207
221
  }
208
222
 
209
223
  return { installed, skipped };
210
224
  }
211
225
 
212
- function generateClaudeRules(ruleFiles, sourceDir, targetDir) {
226
+ function generateClaudeRules(ruleFiles, sourceDir, targetDir, force = false) {
213
227
  const claudeDir = path.join(targetDir, ".claude", "rules");
214
228
  if (!fs.existsSync(claudeDir)) {
215
229
  fs.mkdirSync(claudeDir, { recursive: true });
@@ -229,7 +243,7 @@ function generateClaudeRules(ruleFiles, sourceDir, targetDir) {
229
243
  continue;
230
244
  }
231
245
 
232
- if (fs.existsSync(targetPath)) {
246
+ if (fs.existsSync(targetPath) && !force) {
233
247
  console.log(` โญ๏ธ ${ruleName} - ์ด๋ฏธ ์กด์žฌํ•จ, ๊ฑด๋„ˆ๋œ€`);
234
248
  skipped++;
235
249
  continue;
@@ -240,7 +254,8 @@ function generateClaudeRules(ruleFiles, sourceDir, targetDir) {
240
254
  const output = serializeClaudeFrontmatter(frontmatter) + body;
241
255
 
242
256
  fs.writeFileSync(targetPath, output);
243
- console.log(` โœ… ${ruleName}`);
257
+ const action = force && fs.existsSync(targetPath) ? "๐Ÿ”„" : "โœ…";
258
+ console.log(` ${action} ${ruleName}`);
244
259
  installed++;
245
260
  }
246
261
 
@@ -285,11 +300,11 @@ function main() {
285
300
 
286
301
  // Cursor ๊ทœ์น™ ์ƒ์„ฑ
287
302
  console.log("๐Ÿ”ง Cursor ๊ทœ์น™ ์ƒ์„ฑ:\n");
288
- const cursor = generateCursorRules(rulesToInstall, rulesSourceDir, targetDir);
303
+ const cursor = generateCursorRules(rulesToInstall, rulesSourceDir, targetDir, options.force);
289
304
 
290
305
  // Claude ๊ทœ์น™ ์ƒ์„ฑ
291
306
  console.log("\n๐Ÿค– Claude ๊ทœ์น™ ์ƒ์„ฑ:\n");
292
- const claude = generateClaudeRules(rulesToInstall, rulesSourceDir, targetDir);
307
+ const claude = generateClaudeRules(rulesToInstall, rulesSourceDir, targetDir, options.force);
293
308
 
294
309
  const totalInstalled = cursor.installed + claude.installed;
295
310
  const totalSkipped = cursor.skipped + claude.skipped;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blastlabs/utils",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,60 @@
1
+ ---
2
+ description: "Changelog ์ž‘์„ฑ ๊ทœ์น™"
3
+ globs: "CHANGELOG.md"
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Changelog Rules
8
+
9
+ ## ํ•„์ˆ˜ ๊ทœ์น™
10
+
11
+ - [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ํ˜•์‹ ์ค€์ˆ˜
12
+ - Semantic Versioning ์ค€์ˆ˜
13
+ - ์ตœ์‹  ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ `[Unreleased]` ์„น์…˜์— ์ถ”๊ฐ€
14
+
15
+ ## ์„น์…˜ ๊ตฌ์กฐ
16
+
17
+ ```markdown
18
+ # Changelog
19
+
20
+ ## [Unreleased]
21
+
22
+ ### Added
23
+ - ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ
24
+
25
+ ### Changed
26
+ - ๊ธฐ์กด ๊ธฐ๋Šฅ์˜ ๋ณ€๊ฒฝ
27
+
28
+ ### Deprecated
29
+ - ๊ณง ์ œ๊ฑฐ๋  ๊ธฐ๋Šฅ
30
+
31
+ ### Removed
32
+ - ์ œ๊ฑฐ๋œ ๊ธฐ๋Šฅ
33
+
34
+ ### Fixed
35
+ - ๋ฒ„๊ทธ ์ˆ˜์ •
36
+
37
+ ### Security
38
+ - ๋ณด์•ˆ ํŒจ์น˜
39
+
40
+ ## [1.0.0] - 2024-01-01
41
+ ```
42
+
43
+ ## ๋ฒ„์ „ ๋ฆด๋ฆฌ์ฆˆ ์‹œ
44
+
45
+ 1. `[Unreleased]` ๋‚ด์šฉ์„ ์ƒˆ ๋ฒ„์ „ ์„น์…˜์œผ๋กœ ์ด๋™
46
+ 2. ๋ฆด๋ฆฌ์ฆˆ ๋‚ ์งœ ์ถ”๊ฐ€
47
+ 3. ์ƒˆ๋กœ์šด `[Unreleased]` ์„น์…˜ ์ƒ์„ฑ
48
+
49
+ ```markdown
50
+ ## [Unreleased]
51
+
52
+ ## [1.1.0] - 2024-02-01
53
+ ### Added
54
+ - ์ด์ „์— Unreleased์— ์žˆ๋˜ ๋‚ด์šฉ
55
+ ```
56
+
57
+ ## ์ค‘์š”
58
+
59
+ - **Always** update CHANGELOG.md when making changes
60
+ - ๋ฐฐํฌ ์‹œ ๋ฐ˜๋“œ์‹œ changelog ํ™•์ธ
@@ -0,0 +1,96 @@
1
+ ---
2
+ description: "๊ฐœ๋ฐœ ์›Œํฌํ”Œ๋กœ์šฐ: PRD ์ฐธ๊ณ  ๋ฐ ํ…Œ์ŠคํŠธ ๊ธฐ๋ณธ ์ง„ํ–‰"
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Development Workflow
7
+
8
+ ## ํ•„์ˆ˜ ์›์น™
9
+
10
+ ### 1. PRD ๋จผ์ € ํ™•์ธ
11
+
12
+ ๋ชจ๋“  ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ์ „์— **๋ฐ˜๋“œ์‹œ** PRD(Product Requirement Document)๋ฅผ ๋จผ์ € ํ™•์ธํ•˜์„ธ์š”:
13
+
14
+ - **PRD๊ฐ€ ์žˆ๋‹ค๋ฉด** ๋‚ด์šฉ์„ ํ™•์ธํ•˜๊ณ  ์ŠคํŽ™ ํŒŒ์•…
15
+ - **PRD๊ฐ€ ์—†๋‹ค๋ฉด** ์‚ฌ์šฉ์ž์™€ ํ•จ๊ป˜ PRD๋ฅผ ์ž‘์„ฑ
16
+ - ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜์ง‘
17
+ - AI๊ฐ€ PRD ์ดˆ์•ˆ ์ž‘์„ฑ
18
+ - ์‚ฌ์šฉ์ž ํ™•์ธ ํ›„ ๊ฐœ๋ฐœ ์ง„ํ–‰
19
+ - PRD์—์„œ ์š”๊ตฌ์‚ฌํ•ญ, UI/UX ์ŠคํŽ™, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ช…ํ™•ํžˆ ํŒŒ์•…
20
+ - ๋ถˆํ™•์‹คํ•œ ๋ถ€๋ถ„์€ PRD๋ฅผ ํ†ตํ•ด ๋ช…ํ™•ํ™” ํ›„ ๊ฐœ๋ฐœ ์ง„ํ–‰
21
+
22
+ **PRD ํ™•์ธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:**
23
+ - [ ] ๊ธฐ๋Šฅ ๋ชฉ์ ๊ณผ ๋ชฉํ‘œ๊ฐ€ ๋ช…ํ™•ํ•œ๊ฐ€?
24
+ - [ ] UI/UX ์ŠคํŽ™์ด ์ •์˜๋˜์–ด ์žˆ๋Š”๊ฐ€?
25
+ - [ ] API ์ŠคํŽ™์ด ์ •์˜๋˜์–ด ์žˆ๋Š”๊ฐ€?
26
+ - [ ] ์˜ˆ์™ธ ์ƒํ™ฉ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š”๊ฐ€?
27
+
28
+ ### 2. ํ…Œ์ŠคํŠธ ๊ธฐ๋ณธ ์ง„ํ–‰
29
+
30
+ **๋ชจ๋“  ๊ธฐ๋Šฅ ๊ตฌํ˜„์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.**
31
+
32
+ #### ํ…Œ์ŠคํŠธ ์šฐ์„  ์›์น™
33
+
34
+ 1. **ํ…Œ์ŠคํŠธ ์—†์ด ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ธฐ**
35
+ - ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ „ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ๋จผ์ € ์ž‘์„ฑ
36
+ - ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํŒจํ•˜๋ฉด ๊ตฌํ˜„์œผ๋กœ ๊ฐ„์ฃผํ•˜์ง€ ์•Š์Œ
37
+
38
+ 2. **์ตœ์†Œ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ์ค€์ˆ˜**
39
+ - ์ƒˆ๋กœ์šด hook: 100% ์ปค๋ฒ„๋ฆฌ์ง€ ๊ถŒ์žฅ
40
+ - ์ปดํฌ๋„ŒํŠธ: ์ฃผ์š” ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ
41
+ - ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜: 100% ์ปค๋ฒ„๋ฆฌ์ง€
42
+
43
+ 3. **ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ›„ PR**
44
+ - `npm test` ํ†ต๊ณผ ํ™•์ธ
45
+ - ๋นŒ๋“œ ์„ฑ๊ณต ํ™•์ธ
46
+ - ํ…Œ์ŠคํŠธ ์‹คํŒจ ์‹œ PR ๋ถˆ๊ฐ€
47
+
48
+ ## ๊ฐœ๋ฐœ ์ˆœ์„œ
49
+
50
+ ### Step 1: PRD ํ™•์ธ/์ž‘์„ฑ
51
+ ```
52
+ ์‚ฌ์šฉ์ž: "๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ํ•ด์ค˜"
53
+ AI: "PRD๊ฐ€ ์žˆ๋‚˜์š”? ์—†๋‹ค๋ฉด ๋จผ์ € ํ•จ๊ป˜ ์ž‘์„ฑํ•ด์š”.
54
+
55
+ ๋จผ์ € ๋ช‡ ๊ฐ€์ง€ ์งˆ๋ฌธ๋“œ๋ฆด๊ฒŒ์š”:
56
+ 1. ๋กœ๊ทธ์ธ ๋ฐฉ๋ฒ• (์ด๋ฉ”์ผ/์†Œ์…œ)
57
+ 2. ํ•„์š”ํ•œ UI ์ปดํฌ๋„ŒํŠธ
58
+ 3. ์—ฐ๋™ํ•  API ์—”๋“œํฌ์ธํŠธ
59
+
60
+ ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ PRD ์ดˆ์•ˆ์„ ์ž‘์„ฑํ• ๊ฒŒ์š”."
61
+ ```
62
+
63
+ ### Step 2: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ
64
+ ```
65
+ AI: "ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ถ€ํ„ฐ ์ž‘์„ฑํ• ๊ฒŒ์š”."
66
+ โ†’ ๋กœ๊ทธ์ธ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ
67
+ โ†’ ์‹คํŒจํ•˜๋Š” ๊ฒƒ ํ™•์ธ
68
+ ```
69
+
70
+ ### Step 3: ๊ธฐ๋Šฅ ๊ตฌํ˜„
71
+ ```
72
+ AI: "์ด์ œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ• ๊ฒŒ์š”."
73
+ โ†’ ์ตœ์†Œํ•œ์˜ ๊ตฌํ˜„์œผ๋กœ ํ…Œ์ŠคํŠธ ํ†ต๊ณผ
74
+ ```
75
+
76
+ ### Step 4: ๋ฆฌํŒฉํ† ๋ง
77
+ ```
78
+ AI: "์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ• ๊ฒŒ์š”."
79
+ โ†’ ๋ฆฌํŒฉํ† ๋ง ํ›„ ํ…Œ์ŠคํŠธ ์žฌ์‹คํ–‰
80
+ โ†’ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํ†ต๊ณผ ํ™•์ธ
81
+ ```
82
+
83
+ ## ์˜ˆ์™ธ ์‚ฌํ•ญ
84
+
85
+ **ํ…Œ์ŠคํŠธ๋ฅผ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ:**
86
+ - ํ”„๋กœํ† ํƒ€์ดํ•‘ (์ฝ”๋“œ ํ๊ธฐ ์˜ˆ์ •)
87
+ - ์Šคํƒ€์ผ๋งŒ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ
88
+ - ๋ช…์‹œ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ์ œ์™ธ ์š”์ฒญ ์‹œ
89
+
90
+ **๊ทธ ์™ธ์—๋Š” ๋ฌด์กฐ๊ฑด ํ…Œ์ŠคํŠธ ์ž‘์„ฑ**
91
+
92
+ ## ์ค‘์š”
93
+
94
+ - **Always** check PRD first before implementation
95
+ - **Always** write tests before or with implementation
96
+ - **Never** skip tests without explicit reason
@@ -1,6 +1,8 @@
1
1
  ---
2
- description: "๋ฌธ์„œํ™” ๊ฐ€์ด๋“œ๋ผ์ธ"
2
+ description: '๋ฌธ์„œํ™” ๊ฐ€์ด๋“œ๋ผ์ธ'
3
3
  alwaysApply: false
4
+ paths:
5
+ - '**/docs/**'
4
6
  ---
5
7
 
6
8
  # Documentation Guidelines
@@ -14,8 +16,17 @@ alwaysApply: false
14
16
  ## ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ
15
17
 
16
18
  - ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์‹œ README.md ์—…๋ฐ์ดํŠธ
17
- - ์ƒˆ hooks ์ถ”๊ฐ€ ์‹œ ๊ด€๋ จ docs/*.md ์—…๋ฐ์ดํŠธ
19
+ - ์ƒˆ hooks ์ถ”๊ฐ€ ์‹œ ๊ด€๋ จ docs/\*.md ์—…๋ฐ์ดํŠธ
20
+ - ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ๋ฐ˜๋“œ์‹œ CHANGELOG.md์— ๊ธฐ๋ก
21
+
22
+ ## CHANGELOG ์ž‘์„ฑ
23
+
24
+ - [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ํ˜•์‹ ์ค€์ˆ˜
25
+ - ์ตœ์‹  ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ `[Unreleased]` ์„น์…˜์— ์ถ”๊ฐ€
26
+ - ์„น์…˜: Added, Changed, Deprecated, Removed, Fixed, Security
27
+ - ๋ฒ„์ „ ๋ฆด๋ฆฌ์ฆˆ ์‹œ `[Unreleased]`๋ฅผ ํ•ด๋‹น ๋ฒ„์ „์œผ๋กœ ์ด๋™
18
28
 
19
29
  ## ํ•„์ˆ˜ ์‚ฌํ•ญ
20
30
 
21
31
  - **Always** update documentation when adding features
32
+ - **Always** update CHANGELOG.md for any changes
@@ -1,14 +1,17 @@
1
1
  ---
2
- description: "Entities ๋ ˆ์ด์–ด ๊ตฌ์กฐ ๋ฐ API ํŒจํ„ด"
2
+ description: 'Entities ๋ ˆ์ด์–ด ๊ตฌ์กฐ ๋ฐ API ํŒจํ„ด'
3
3
  paths:
4
- - "**/entities/**"
4
+ - '**/entities/**'
5
5
  ---
6
6
 
7
7
  # Entities Layer (์—”ํ‹ฐํ‹ฐ)
8
8
 
9
9
  ์—”ํ‹ฐํ‹ฐ๋Š” ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ API ๋กœ์ง์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
10
+ mapper๋Š” ๋ฐ›์€๋ฐ์ดํ„ฐ๋ฅผ model์— ์„ค๊ณ„ํ•œ ๋ฐฉ์‹์œผ๋กœ map์„ ํ•ฉ๋‹ˆ๋‹ค.
11
+ query๋Š” params๋กœ ๋ณดํ†ต get์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
10
12
 
11
13
  ## ๊ตฌ์กฐ
14
+
12
15
  ```
13
16
  entities/
14
17
  โ””โ”€โ”€ (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)/ ์˜ˆ: inquiry, popular-product
@@ -22,29 +25,27 @@ entities/
22
25
  โ”‚ โ”œโ”€โ”€ get-(์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)-list.ts
23
26
  โ”‚ โ”œโ”€โ”€ get-(์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)-detail.ts
24
27
  โ”‚ โ”œโ”€โ”€ (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)-queries.ts
25
- โ”‚ โ”œโ”€โ”€ mutate.ts
26
- โ”‚ โ”œโ”€โ”€ query.ts
27
28
  โ”‚ โ””โ”€โ”€ index.ts
28
29
  โ”œโ”€โ”€ model/
29
30
  โ”‚ โ”œโ”€โ”€ (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„).ts
30
31
  โ”‚ โ”œโ”€โ”€ (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)-detail.ts
31
32
  โ”‚ โ””โ”€โ”€ (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)-update.ts
32
- โ”œโ”€โ”€ schema.ts
33
33
  โ””โ”€โ”€ index.ts
34
34
  ```
35
35
 
36
36
  ## ๊ฐ ํŒŒ์ผ์˜ ์—ญํ• 
37
+
37
38
  - **mapper/**: API ์‘๋‹ต์„ ๋„๋ฉ”์ธ ๋ชจ๋ธ๋กœ ๋ณ€ํ™˜
38
39
  - **query/**: ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ ํƒ€์ž… ์ •์˜
39
- - **get-*-list.ts**: ๋ฆฌ์ŠคํŠธ ์กฐํšŒ API ํ•จ์ˆ˜
40
- - **get-*-detail.ts**: ์ƒ์„ธ ์กฐํšŒ API ํ•จ์ˆ˜
40
+ - **get-\*-list.ts**: ๋ฆฌ์ŠคํŠธ ์กฐํšŒ API ํ•จ์ˆ˜
41
+ - **get-\*-detail.ts**: ์ƒ์„ธ ์กฐํšŒ API ํ•จ์ˆ˜
42
+ - **post-\*.ts** : post API ํ•จ์ˆ˜ (delete, put๊ฐ™์€๊ฒƒ๋„ ์•ž์— ์ ‘๋‘์‚ฌ๊ฐ€ ๋ณ€ํ•จ)
41
43
  - **\*-queries.ts**: TanStack Query queryOptions ์ •์˜ (all, lists, list, details, detail ๋“ฑ)
42
- - **mutate.ts**: mutation ํ•จ์ˆ˜๋“ค
43
44
  - **model/**: ๋„๋ฉ”์ธ ๋ชจ๋ธ ํƒ€์ž… ์ •์˜
44
- - **schema.ts**: zod ์Šคํ‚ค๋งˆ ์ •์˜ ๋ฐ ๋ณ€ํ™˜ ํ•จ์ˆ˜
45
45
  - **index.ts**: `export * as (์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„)Api from "./api"`
46
46
 
47
47
  ## ์˜ˆ์‹œ
48
+
48
49
  ```
49
50
  entities/inquiry/
50
51
  โ”œโ”€โ”€ api/
@@ -60,8 +61,12 @@ entities/inquiry/
60
61
  โ”œโ”€โ”€ model/
61
62
  โ”‚ โ”œโ”€โ”€ inquiry.ts
62
63
  โ”‚ โ””โ”€โ”€ inquiry-detail.ts
63
- โ””โ”€โ”€ schema.ts
64
64
  ```
65
65
 
66
66
  ## TanStack Query ํŒจํ„ด
67
+
67
68
  ์„œ๋ฒ„ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” TanStack Query์˜ queryOptions ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
69
+
70
+ ## model์˜ ๊ทœ์น™
71
+
72
+ zod๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ export const (์—”ํ‹ฐํ‹ฐ)Schema์™€ export type (์—”ํ‹ฐํ‹ฐ)Type ์˜ ๋„ค์ด๋ฐ ๊ทœ์น™์„ ํ™œ์šฉํ•œ๋‹ค
@@ -0,0 +1,212 @@
1
+ ---
2
+ description: "ํŒŒ์ผ ๋ฐ ๋ณ€์ˆ˜ ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜"
3
+ alwaysApply: false
4
+ paths:
5
+ - "**/*.ts"
6
+ - "**/*.tsx"
7
+ - "**/*.js"
8
+ - "**/*.jsx"
9
+ ---
10
+
11
+ # Naming Convention
12
+
13
+ ## ํŒŒ์ผ ๋„ค์ด๋ฐ
14
+
15
+ ### ๊ธฐ๋ณธ ๊ทœ์น™
16
+
17
+ **๋ชจ๋“  ํŒŒ์ผ/ํด๋”๋Š” `kebab-case` ์‚ฌ์šฉ**
18
+
19
+ ```
20
+ โœ… good
21
+ user-profile/
22
+ use-user-data/
23
+ button-group.tsx
24
+
25
+ โŒ bad
26
+ UserProfile/
27
+ useUserData/
28
+ buttonGroup.tsx
29
+ ```
30
+
31
+ ### ์˜ˆ์™ธ: React ์ปดํฌ๋„ŒํŠธ
32
+
33
+ **์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์€ `PascalCase` ์‚ฌ์šฉ**
34
+
35
+ ```
36
+ components/
37
+ โœ… ButtonGroup.tsx
38
+ โœ… UserProfile.tsx
39
+ โœ… FormInput.tsx
40
+
41
+ โŒ button-group.tsx
42
+ โŒ user-profile.tsx
43
+ ```
44
+
45
+ **๋‹จ, ํด๋”๋Š” `kebab-case`**
46
+
47
+ ```
48
+ components/
49
+ button-group/
50
+ ButtonGroup.tsx
51
+ ButtonGroup.test.tsx
52
+ ```
53
+
54
+ ### Hooks
55
+
56
+ **`use-` prefix + `camelCase`**
57
+
58
+ ```
59
+ hooks/
60
+ โœ… use-user-data.ts
61
+ โœ… use-form-state.ts
62
+ โœ… use-debounce.ts
63
+
64
+ โŒ userData.ts
65
+ โŒ useUserData.ts (ํŒŒ์ผ๋ช…)
66
+ โŒ form-state.ts
67
+ ```
68
+
69
+ ### ํƒ€์ž… ํŒŒ์ผ
70
+
71
+ **`.types.ts` ๋˜๋Š” `.type.ts` ์ ‘๋ฏธ์‚ฌ**
72
+
73
+ ```
74
+ model/
75
+ โœ… user.types.ts
76
+ โœ… product.type.ts
77
+ โœ… api.types.ts
78
+
79
+ โŒ UserTypes.ts
80
+ โท user-type.ts (๋‹จ์ˆ˜ๅž‹)
81
+ ```
82
+
83
+ ### ํ…Œ์ŠคํŠธ ํŒŒ์ผ
84
+
85
+ **๋Œ€์ƒ ํŒŒ์ผ๋ช… + `.test.` + ํ™•์žฅ์ž**
86
+
87
+ ```
88
+ ButtonGroup.tsx โ†’ ButtonGroup.test.tsx
89
+ useUserData.ts โ†’ useUserData.test.ts
90
+ api.ts โ†’ api.test.ts
91
+ ```
92
+
93
+ ### ํด๋” ๊ตฌ์กฐ
94
+
95
+ **FSD ์•„ํ‚คํ…์ฒ˜ ์˜ˆ์‹œ**
96
+
97
+ ```
98
+ src/
99
+ โ”œโ”€โ”€ entities/
100
+ โ”‚ โ””โ”€โ”€ user/ # kebab-case
101
+ โ”‚ โ”œโ”€โ”€ model/
102
+ โ”‚ โ”‚ โ”œโ”€โ”€ user.types.ts
103
+ โ”‚ โ”‚ โ””โ”€โ”€ user-detail.types.ts
104
+ โ”‚ โ”œโ”€โ”€ api/
105
+ โ”‚ โ”‚ โ”œโ”€โ”€ get-user-list.ts
106
+ โ”‚ โ”‚ โ”œโ”€โ”€ use-user-query.ts
107
+ โ”‚ โ”‚ โ””โ”€โ”€ user.test.ts
108
+ โ”‚ โ””โ”€โ”€ ui/
109
+ โ”‚ โ”œโ”€โ”€ UserCard.tsx # PascalCase
110
+ โ”‚ โ””โ”€โ”€ UserCard.test.tsx
111
+ โ”œโ”€โ”€ features/
112
+ โ”‚ โ””โ”€โ”€ auth-login/ # kebab-case
113
+ โ”‚ โ”œโ”€โ”€ model/
114
+ โ”‚ โ”œโ”€โ”€ api/
115
+ โ”‚ โ””โ”€โ”€ ui/
116
+ โ”‚ โ””โ”€โ”€ LoginForm.tsx # PascalCase
117
+ โ”œโ”€โ”€ shared/
118
+ โ”‚ โ””โ”€โ”€ ui/
119
+ โ”‚ โ””โ”€โ”€ button/ # kebab-case
120
+ โ”‚ โ”œโ”€โ”€ Button.tsx # PascalCase
121
+ โ”‚ โ””โ”€โ”€ Button.test.tsx
122
+ ```
123
+
124
+ ## ๋ณ€์ˆ˜/ํ•จ์ˆ˜ ๋„ค์ด๋ฐ
125
+
126
+ ### ๋ณ€์ˆ˜
127
+
128
+ **`camelCase` ์‚ฌ์šฉ**
129
+
130
+ ```typescript
131
+ โœ… good
132
+ const userName = '...';
133
+ const isActive = true;
134
+ const maxCount = 10;
135
+
136
+ โŒ bad
137
+ const user_name = '...';
138
+ const IsActive = true;
139
+ const MAX_COUNT = 10; (const๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด)
140
+ ```
141
+
142
+ ### ์ƒ์ˆ˜
143
+
144
+ **`UPPER_SNAKE_CASE` ์‚ฌ์šฉ**
145
+
146
+ ```typescript
147
+ โœ… good
148
+ const MAX_RETRY_COUNT = 3;
149
+ const API_BASE_URL = 'https://...';
150
+ const DEFAULT_TIMEOUT = 5000;
151
+
152
+ โŒ bad
153
+ const maxRetryCount = 3;
154
+ const apiBaseUrl = 'https://...';
155
+ ```
156
+
157
+ ### ํ•จ์ˆ˜
158
+
159
+ **`camelCase` + ๋™์‚ฌ๋กœ ์‹œ์ž‘**
160
+
161
+ ```typescript
162
+ โœ… good
163
+ function getUserData() { }
164
+ function handleSubmit() { }
165
+ function isValidEmail() { }
166
+
167
+ โŒ bad
168
+ function userData() { }
169
+ function form_submit() { }
170
+ function ValidEmail() { }
171
+ ```
172
+
173
+ ### ์ธํ„ฐํŽ˜์ด์Šค/ํƒ€์ž…
174
+
175
+ **`PascalCase` ์‚ฌ์šฉ**
176
+
177
+ ```typescript
178
+ โœ… good
179
+ interface UserProfile { }
180
+ type LoginFormData = { };
181
+ interface ApiResponse<T> { }
182
+
183
+ โŒ bad
184
+ interface userProfile { }
185
+ type loginFormData = { }
186
+ ```
187
+
188
+ ### Enum
189
+
190
+ **`PascalCase` + ๋ฉค๋ฒ„๋Š” `UPPER_SNAKE_CASE`**
191
+
192
+ ```typescript
193
+ โœ… good
194
+ enum UserRole {
195
+ ADMIN = 'ADMIN',
196
+ USER = 'USER',
197
+ GUEST = 'GUEST',
198
+ }
199
+
200
+ โŒ bad
201
+ enum userRole {
202
+ admin = 'admin',
203
+ USER = 'user',
204
+ }
205
+ ```
206
+
207
+ ## ์ค‘์š”
208
+
209
+ - **Always** use kebab-case for files and folders
210
+ - **Always** use PascalCase for component files
211
+ - **Always** use camelCase for variables and functions
212
+ - **Always** use UPPER_SNAKE_CASE for constants
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: "Shared ๋ ˆ์ด์–ด ๊ตฌ์กฐ ๋ฐ lib vs utils ๊ตฌ๋ถ„"
2
+ description: 'Shared ๋ ˆ์ด์–ด ๊ตฌ์กฐ ๋ฐ lib vs utils ๊ตฌ๋ถ„'
3
3
  paths:
4
- - "**/shared/**"
4
+ - '**/shared/**'
5
5
  ---
6
6
 
7
7
  # Shared Layer (๊ณต์œ  ๋ ˆ์ด์–ด)
@@ -9,11 +9,10 @@ paths:
9
9
  ํ”„๋กœ์ ํŠธ ์ „์ฒด์—์„œ ๊ณต์œ ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
10
10
 
11
11
  ## ๊ตฌ์กฐ
12
+
12
13
  ```
13
14
  shared/
14
15
  โ”œโ”€โ”€ api/ API ์ธ์Šคํ„ด์Šค ์„ค์ • (instance_v2.ts ๋“ฑ)
15
- โ”œโ”€โ”€ constant/ ๊ณตํ†ต ์ƒ์ˆ˜ (path.ts ๋“ฑ)
16
- โ”œโ”€โ”€ hooks/ ๊ณตํ†ต React ํ›…
17
16
  โ”œโ”€โ”€ mock-data/ ๊ฐœ๋ฐœ์šฉ ๋ชฉ ๋ฐ์ดํ„ฐ
18
17
  โ”œโ”€โ”€ lib/ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ž˜ํผ ๋ฐ ์„ค์ •
19
18
  โ”‚ โ””โ”€โ”€ (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ช…)/
@@ -28,14 +27,15 @@ shared/
28
27
  โ”‚ โ”‚ โ””โ”€โ”€ (์ปดํฌ๋„ŒํŠธ๋ช…).tsx
29
28
  โ”‚ โ”œโ”€โ”€ theme/ ํ…Œ๋งˆ ๊ด€๋ จ CSS ํŒŒ์ผ
30
29
  โ”‚ โ””โ”€โ”€ utils/ UI ๊ด€๋ จ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
31
- โ”œโ”€โ”€ util/ ์ผ๋ฐ˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
32
30
  โ””โ”€โ”€ utils/ ์ถ”๊ฐ€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜
33
31
  ```
34
32
 
35
33
  ## lib vs utils ๊ตฌ๋ถ„ ๊ธฐ์ค€
36
34
 
37
35
  ### `shared/lib/`
36
+
38
37
  ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ๊ฐ์‹ธ๊ฑฐ๋‚˜ ์„ค์ •ํ•˜๋Š” ์ฝ”๋“œ
38
+
39
39
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ž˜ํผ(wrapper) ์ปดํฌ๋„ŒํŠธ
40
40
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ดˆ๊ธฐ ์„ค์ • ๋ฐ Provider
41
41
  - ์˜ˆ์‹œ:
@@ -43,16 +43,19 @@ shared/
43
43
  - `shared/lib/tanstack-query/query-client.tsx`
44
44
  - `shared/lib/suspense/suspense-wrapper.tsx`
45
45
 
46
- ### `shared/utils/` (๋˜๋Š” `shared/util/`)
46
+ ### `shared/utils/`
47
+
47
48
  ์ˆœ์ˆ˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ ๋ฐ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ํ—ฌํผ
49
+
48
50
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋ฌด๊ด€ํ•œ ์ˆœ์ˆ˜ ํ•จ์ˆ˜
49
51
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ์œ ํ‹ธ ํ•จ์ˆ˜
50
52
  - ์˜ˆ์‹œ:
51
- - `shared/util/convert-price.ts` - ๊ฐ€๊ฒฉ ๋ณ€ํ™˜ ํ•จ์ˆ˜
52
- - `shared/util/export-excel.ts` - ์—‘์…€ ๋‚ด๋ณด๋‚ด๊ธฐ ํ•จ์ˆ˜
53
- - `shared/util/form-validation.ts` - ํผ ๊ฒ€์ฆ ์œ ํ‹ธ
53
+ - `shared/utils/convert-price.ts` - ๊ฐ€๊ฒฉ ๋ณ€ํ™˜ ํ•จ์ˆ˜
54
+ - `shared/utils/export-excel.ts` - ์—‘์…€ ๋‚ด๋ณด๋‚ด๊ธฐ ํ•จ์ˆ˜
55
+ - `shared/utils/form-validation.ts` - ํผ ๊ฒ€์ฆ ์œ ํ‹ธ
54
56
 
55
57
  ## ํŒ๋‹จ ๊ธฐ์ค€
58
+
56
59
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ์‹ธ๋Š”๊ฐ€? โ†’ `lib/`
57
60
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋ฌด๊ด€ํ•œ ๋กœ์ง์ธ๊ฐ€? โ†’ `utils/`
58
61
  - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ์œ ํ‹ธ์ธ๊ฐ€? โ†’ `utils/`
@@ -1,72 +1,34 @@
1
1
  ---
2
- description: "Views ๋ ˆ์ด์–ด (ํŽ˜์ด์ง€) ๊ตฌ์กฐ"
2
+ description: 'Views ๋ ˆ์ด์–ด (ํŽ˜์ด์ง€) ๊ตฌ์กฐ'
3
3
  paths:
4
- - "**/views/**"
4
+ - '**/views/**'
5
5
  ---
6
6
 
7
7
  # Views Layer (ํŽ˜์ด์ง€ ๋ ˆ์ด์–ด)
8
8
 
9
9
  ํŽ˜์ด์ง€ ๋‹จ์œ„์˜ UI์™€ ๋กœ์ง์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
10
+ ๊ธฐ๋ณธ์ ์œผ๋กœ fsd ๋ฐฉ์‹์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค
10
11
 
11
12
  ## ๊ตฌ์กฐ
13
+
12
14
  ```
13
15
  views/
14
16
  โ””โ”€โ”€ (ํŽ˜์ด์ง€๋ช…)/ ์˜ˆ: inquiry, product, seller
15
17
  โ”œโ”€โ”€ page.tsx ๋ฉ”์ธ ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ (ํ•„์ˆ˜)
16
- โ”œโ”€โ”€ (ํŽ˜์ด์ง€๋ช…)-list-page.tsx
17
- โ”œโ”€โ”€ (ํŽ˜์ด์ง€๋ช…)-detail-page.tsx
18
18
  โ”œโ”€โ”€ detail/
19
19
  โ”‚ โ”œโ”€โ”€ page.tsx
20
20
  โ”‚ โ”œโ”€โ”€ ui/
21
- โ”‚ โ”œโ”€โ”€ hooks/ use-(ํŽ˜์ด์ง€๋ช…)-detail-data.tsx ๋“ฑ
22
- โ”‚ โ””โ”€โ”€ components/
23
- โ”œโ”€โ”€ list/
24
- โ”‚ โ”œโ”€โ”€ (์ปฌ๋Ÿผ๋ช…)-column.tsx
25
- โ”‚ โ”œโ”€โ”€ search.tsx
26
- โ”‚ โ””โ”€โ”€ (๋ชจ๋‹ฌ๋ช…)-modal.tsx
27
21
  โ”œโ”€โ”€ ui/
28
- โ”œโ”€โ”€ hooks/ use-(ํŽ˜์ด์ง€๋ช…)-data.tsx, use-(ํŽ˜์ด์ง€๋ช…)-actions.tsx ๋“ฑ
29
- โ”œโ”€โ”€ components/
30
22
  โ”œโ”€โ”€ utils/
31
- โ”œโ”€โ”€ schema/ zod ์Šคํ‚ค๋งˆ
32
- โ””โ”€โ”€ constants.ts
33
23
  ```
34
24
 
35
25
  ## ์˜ˆ์‹œ
36
26
 
37
27
  ### ๊ธฐ๋ณธ ํŽ˜์ด์ง€ ๊ตฌ์กฐ
28
+
38
29
  ```
39
30
  views/inquiry/
40
31
  โ”œโ”€โ”€ detail/
41
32
  โ”‚ โ””โ”€โ”€ page.tsx
42
- โ”œโ”€โ”€ inquiry-list-page.tsx
43
- โ””โ”€โ”€ list/
44
- โ”œโ”€โ”€ inquiry-column.tsx
45
- โ”œโ”€โ”€ inquiry-modal.tsx
46
- โ””โ”€โ”€ search.tsx
47
- ```
48
-
49
- ### ์ƒ์„ธ ํŽ˜์ด์ง€ ๊ตฌ์กฐ
50
- ```
51
- views/ai-management/agency/agency-detail/
52
- โ”œโ”€โ”€ page.tsx
53
- โ”œโ”€โ”€ hooks/
54
- โ”‚ โ”œโ”€โ”€ use-agency-detail-data.tsx
55
- โ”‚ โ””โ”€โ”€ use-agency-data-actions.tsx
56
- โ”œโ”€โ”€ components/
57
- โ”‚ โ””โ”€โ”€ agency-basic-info-tab.tsx
58
- โ””โ”€โ”€ schema/
59
- โ””โ”€โ”€ agency-detail.ts
60
- ```
61
-
62
- ### ๊ด€๋ฆฌ ํŽ˜์ด์ง€ ๊ตฌ์กฐ
63
- ```
64
- views/product/category-management/
65
- โ”œโ”€โ”€ page.tsx
66
- โ”œโ”€โ”€ hooks/
67
- โ”‚ โ”œโ”€โ”€ use-category-data.tsx
68
- โ”‚ โ””โ”€โ”€ use-category-actions.tsx
69
- โ””โ”€โ”€ ui/
70
- โ”œโ”€โ”€ category-management.tsx
71
- โ””โ”€โ”€ category-modal.tsx
33
+ โ”‚ page.tsx
72
34
  ```