@su-record/vibe 2.4.19 โ 2.4.21
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/CLAUDE.md +6 -0
- package/hooks/hooks.json +2 -2
- package/package.json +1 -1
- package/.claude/settings.json +0 -49
- package/.claude/settings.local.json +0 -28
- package/.claude/vibe/rules/languages/dart-flutter.md +0 -509
- package/.claude/vibe/rules/languages/go.md +0 -396
- package/.claude/vibe/rules/languages/java-spring.md +0 -586
- package/.claude/vibe/rules/languages/kotlin-android.md +0 -491
- package/.claude/vibe/rules/languages/python-django.md +0 -371
- package/.claude/vibe/rules/languages/python-fastapi.md +0 -386
- package/.claude/vibe/rules/languages/rust.md +0 -425
- package/.claude/vibe/rules/languages/swift-ios.md +0 -516
- package/.claude/vibe/rules/languages/typescript-nextjs.md +0 -441
- package/.claude/vibe/rules/languages/typescript-nuxt.md +0 -521
- package/.claude/vibe/rules/languages/typescript-react-native.md +0 -446
- package/.claude/vibe/rules/languages/typescript-react.md +0 -525
- package/.claude/vibe/rules/languages/typescript-vue.md +0 -353
|
@@ -1,521 +0,0 @@
|
|
|
1
|
-
# ๐ข TypeScript + Nuxt 3 ํ์ง ๊ท์น
|
|
2
|
-
|
|
3
|
-
## ํต์ฌ ์์น (Vue์์ ์์)
|
|
4
|
-
|
|
5
|
-
```markdown
|
|
6
|
-
โ
๋จ์ผ ์ฑ
์ (SRP)
|
|
7
|
-
โ
์ค๋ณต ์ ๊ฑฐ (DRY)
|
|
8
|
-
โ
์ฌ์ฌ์ฉ์ฑ
|
|
9
|
-
โ
๋ฎ์ ๋ณต์ก๋
|
|
10
|
-
โ
ํจ์ โค 30์ค, Template โค 100์ค
|
|
11
|
-
โ
์ค์ฒฉ โค 3๋จ๊ณ
|
|
12
|
-
โ
Composition API + script setup
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Nuxt 3 ํนํ ๊ท์น
|
|
16
|
-
|
|
17
|
-
### 1. Auto-imports ํ์ฉ
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
// โ
Nuxt 3๋ ์๋ import (๋ช
์์ import ๋ถํ์)
|
|
21
|
-
<script setup lang="ts">
|
|
22
|
-
// ref, computed, watch ๋ฑ Vue API ์๋ import
|
|
23
|
-
const count = ref(0);
|
|
24
|
-
const doubled = computed(() => count.value * 2);
|
|
25
|
-
|
|
26
|
-
// useFetch, useAsyncData ๋ฑ Nuxt composables ์๋ import
|
|
27
|
-
const { data } = await useFetch('/api/users');
|
|
28
|
-
|
|
29
|
-
// components/ ํด๋์ ์ปดํฌ๋ํธ ์๋ import
|
|
30
|
-
// <UserCard /> ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
// โ ๋ถํ์ํ import
|
|
34
|
-
import { ref, computed } from 'vue';
|
|
35
|
-
import { useFetch } from '#app';
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 2. Server API Routes
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
// โ
server/api/users/index.get.ts (GET /api/users)
|
|
42
|
-
export default defineEventHandler(async (event) => {
|
|
43
|
-
const users = await prisma.user.findMany();
|
|
44
|
-
return users;
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// โ
server/api/users/index.post.ts (POST /api/users)
|
|
48
|
-
export default defineEventHandler(async (event) => {
|
|
49
|
-
const body = await readBody(event);
|
|
50
|
-
|
|
51
|
-
// ์ ํจ์ฑ ๊ฒ์ฌ
|
|
52
|
-
if (!body.email || !body.name) {
|
|
53
|
-
throw createError({
|
|
54
|
-
statusCode: 400,
|
|
55
|
-
message: '์ด๋ฉ์ผ๊ณผ ์ด๋ฆ์ ํ์์
๋๋ค',
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const user = await prisma.user.create({ data: body });
|
|
60
|
-
return user;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// โ
server/api/users/[id].get.ts (GET /api/users/:id)
|
|
64
|
-
export default defineEventHandler(async (event) => {
|
|
65
|
-
const id = getRouterParam(event, 'id');
|
|
66
|
-
|
|
67
|
-
const user = await prisma.user.findUnique({ where: { id } });
|
|
68
|
-
|
|
69
|
-
if (!user) {
|
|
70
|
-
throw createError({
|
|
71
|
-
statusCode: 404,
|
|
72
|
-
message: '์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค',
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return user;
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// โ
server/api/users/[id].put.ts (PUT /api/users/:id)
|
|
80
|
-
export default defineEventHandler(async (event) => {
|
|
81
|
-
const id = getRouterParam(event, 'id');
|
|
82
|
-
const body = await readBody(event);
|
|
83
|
-
|
|
84
|
-
const user = await prisma.user.update({
|
|
85
|
-
where: { id },
|
|
86
|
-
data: body,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
return user;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// โ
server/api/users/[id].delete.ts (DELETE /api/users/:id)
|
|
93
|
-
export default defineEventHandler(async (event) => {
|
|
94
|
-
const id = getRouterParam(event, 'id');
|
|
95
|
-
await prisma.user.delete({ where: { id } });
|
|
96
|
-
return { success: true };
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 3. Data Fetching (SSR ์ง์)
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
// โ
useFetch - ๊ธฐ๋ณธ ๋ฐ์ดํฐ ํ์นญ
|
|
104
|
-
<script setup lang="ts">
|
|
105
|
-
const { data: user, pending, error, refresh } = await useFetch<User>(
|
|
106
|
-
`/api/users/${props.userId}`
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// ์ต์
์ฌ์ฉ
|
|
110
|
-
const { data: posts } = await useFetch('/api/posts', {
|
|
111
|
-
query: { limit: 10, offset: 0 },
|
|
112
|
-
headers: { 'X-Custom': 'value' },
|
|
113
|
-
pick: ['id', 'title'], // ํ์ํ ํ๋๋ง ์ ํ
|
|
114
|
-
transform: (data) => data.items, // ์๋ต ๋ณํ
|
|
115
|
-
});
|
|
116
|
-
</script>
|
|
117
|
-
|
|
118
|
-
// โ
useAsyncData - ์ปค์คํ
ํ์นญ ๋ก์ง
|
|
119
|
-
<script setup lang="ts">
|
|
120
|
-
const { data, pending } = await useAsyncData(
|
|
121
|
-
'user-posts', // ์บ์ ํค
|
|
122
|
-
() => $fetch(`/api/users/${props.userId}/posts`),
|
|
123
|
-
{
|
|
124
|
-
default: () => [], // ๊ธฐ๋ณธ๊ฐ
|
|
125
|
-
lazy: true, // ํด๋ผ์ด์ธํธ์์๋ง ์คํ
|
|
126
|
-
server: false, // SSR ๋นํ์ฑํ
|
|
127
|
-
}
|
|
128
|
-
);
|
|
129
|
-
</script>
|
|
130
|
-
|
|
131
|
-
// โ
useLazyFetch - ์ง์ฐ ๋ก๋ฉ (Suspense ์์ด)
|
|
132
|
-
<script setup lang="ts">
|
|
133
|
-
const { data, pending } = useLazyFetch('/api/heavy-data');
|
|
134
|
-
|
|
135
|
-
// pending ์ํ ์ฒ๋ฆฌ
|
|
136
|
-
</script>
|
|
137
|
-
<template>
|
|
138
|
-
<div v-if="pending">๋ก๋ฉ ์ค...</div>
|
|
139
|
-
<div v-else>{{ data }}</div>
|
|
140
|
-
</template>
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### 4. State Management
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
// โ
useState - ์๋ฒ/ํด๋ผ์ด์ธํธ ๊ณต์ ์ํ
|
|
147
|
-
<script setup lang="ts">
|
|
148
|
-
// ๋ชจ๋ ์ปดํฌ๋ํธ์์ ๊ณต์ ๋๋ ์ํ
|
|
149
|
-
const counter = useState('counter', () => 0);
|
|
150
|
-
|
|
151
|
-
function increment() {
|
|
152
|
-
counter.value++;
|
|
153
|
-
}
|
|
154
|
-
</script>
|
|
155
|
-
|
|
156
|
-
// โ
Pinia Store (๋ณต์กํ ์ํ)
|
|
157
|
-
// stores/user.ts
|
|
158
|
-
export const useUserStore = defineStore('user', () => {
|
|
159
|
-
const user = ref<User | null>(null);
|
|
160
|
-
const isLoggedIn = computed(() => !!user.value);
|
|
161
|
-
|
|
162
|
-
async function login(credentials: LoginCredentials) {
|
|
163
|
-
const data = await $fetch('/api/auth/login', {
|
|
164
|
-
method: 'POST',
|
|
165
|
-
body: credentials,
|
|
166
|
-
});
|
|
167
|
-
user.value = data.user;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function logout() {
|
|
171
|
-
user.value = null;
|
|
172
|
-
navigateTo('/login');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return { user, isLoggedIn, login, logout };
|
|
176
|
-
});
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### 5. Middleware
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
// โ
middleware/auth.ts (Named middleware)
|
|
183
|
-
export default defineNuxtRouteMiddleware((to, from) => {
|
|
184
|
-
const { isLoggedIn } = useUserStore();
|
|
185
|
-
|
|
186
|
-
// ๋ก๊ทธ์ธ ํ์ํ ํ์ด์ง ๋ณดํธ
|
|
187
|
-
if (!isLoggedIn && to.meta.requiresAuth) {
|
|
188
|
-
return navigateTo('/login');
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// ํ์ด์ง์์ ์ฌ์ฉ
|
|
193
|
-
<script setup lang="ts">
|
|
194
|
-
definePageMeta({
|
|
195
|
-
middleware: 'auth',
|
|
196
|
-
requiresAuth: true,
|
|
197
|
-
});
|
|
198
|
-
</script>
|
|
199
|
-
|
|
200
|
-
// โ
middleware/auth.global.ts (Global middleware)
|
|
201
|
-
export default defineNuxtRouteMiddleware((to, from) => {
|
|
202
|
-
// ๋ชจ๋ ๋ผ์ฐํธ์ ์ ์ฉ
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
// โ
Server middleware
|
|
206
|
-
// server/middleware/auth.ts
|
|
207
|
-
export default defineEventHandler((event) => {
|
|
208
|
-
const token = getCookie(event, 'auth-token');
|
|
209
|
-
|
|
210
|
-
if (!token && event.path.startsWith('/api/protected')) {
|
|
211
|
-
throw createError({
|
|
212
|
-
statusCode: 401,
|
|
213
|
-
message: '์ธ์ฆ์ด ํ์ํฉ๋๋ค',
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### 6. Layouts & Pages
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
// โ
layouts/default.vue
|
|
223
|
-
<template>
|
|
224
|
-
<div class="layout">
|
|
225
|
-
<AppHeader />
|
|
226
|
-
<main>
|
|
227
|
-
<slot />
|
|
228
|
-
</main>
|
|
229
|
-
<AppFooter />
|
|
230
|
-
</div>
|
|
231
|
-
</template>
|
|
232
|
-
|
|
233
|
-
// โ
layouts/admin.vue
|
|
234
|
-
<template>
|
|
235
|
-
<div class="admin-layout">
|
|
236
|
-
<AdminSidebar />
|
|
237
|
-
<main>
|
|
238
|
-
<slot />
|
|
239
|
-
</main>
|
|
240
|
-
</div>
|
|
241
|
-
</template>
|
|
242
|
-
|
|
243
|
-
// โ
pages/admin/index.vue
|
|
244
|
-
<script setup lang="ts">
|
|
245
|
-
definePageMeta({
|
|
246
|
-
layout: 'admin',
|
|
247
|
-
middleware: ['auth', 'admin-only'],
|
|
248
|
-
});
|
|
249
|
-
</script>
|
|
250
|
-
|
|
251
|
-
// โ
pages/users/[id].vue (๋์ ๋ผ์ฐํธ)
|
|
252
|
-
<script setup lang="ts">
|
|
253
|
-
const route = useRoute();
|
|
254
|
-
const userId = route.params.id;
|
|
255
|
-
|
|
256
|
-
const { data: user } = await useFetch(`/api/users/${userId}`);
|
|
257
|
-
</script>
|
|
258
|
-
|
|
259
|
-
// โ
pages/posts/[...slug].vue (Catch-all ๋ผ์ฐํธ)
|
|
260
|
-
<script setup lang="ts">
|
|
261
|
-
const route = useRoute();
|
|
262
|
-
const slugParts = route.params.slug; // ['a', 'b', 'c']
|
|
263
|
-
</script>
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### 7. SEO & Meta
|
|
267
|
-
|
|
268
|
-
```typescript
|
|
269
|
-
// โ
ํ์ด์ง๋ณ ๋ฉํ ์ค์
|
|
270
|
-
<script setup lang="ts">
|
|
271
|
-
const { data: post } = await useFetch(`/api/posts/${route.params.id}`);
|
|
272
|
-
|
|
273
|
-
useHead({
|
|
274
|
-
title: post.value?.title,
|
|
275
|
-
meta: [
|
|
276
|
-
{ name: 'description', content: post.value?.summary },
|
|
277
|
-
{ property: 'og:title', content: post.value?.title },
|
|
278
|
-
{ property: 'og:image', content: post.value?.thumbnail },
|
|
279
|
-
],
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// ๋๋ useSeoMeta
|
|
283
|
-
useSeoMeta({
|
|
284
|
-
title: post.value?.title,
|
|
285
|
-
ogTitle: post.value?.title,
|
|
286
|
-
description: post.value?.summary,
|
|
287
|
-
ogDescription: post.value?.summary,
|
|
288
|
-
ogImage: post.value?.thumbnail,
|
|
289
|
-
});
|
|
290
|
-
</script>
|
|
291
|
-
|
|
292
|
-
// โ
nuxt.config.ts ์ ์ญ ์ค์
|
|
293
|
-
export default defineNuxtConfig({
|
|
294
|
-
app: {
|
|
295
|
-
head: {
|
|
296
|
-
title: 'My App',
|
|
297
|
-
meta: [
|
|
298
|
-
{ name: 'description', content: 'My awesome app' },
|
|
299
|
-
],
|
|
300
|
-
link: [
|
|
301
|
-
{ rel: 'icon', href: '/favicon.ico' },
|
|
302
|
-
],
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
});
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### 8. Plugins & Modules
|
|
309
|
-
|
|
310
|
-
```typescript
|
|
311
|
-
// โ
plugins/api.ts
|
|
312
|
-
export default defineNuxtPlugin(() => {
|
|
313
|
-
const api = $fetch.create({
|
|
314
|
-
baseURL: '/api',
|
|
315
|
-
onRequest({ options }) {
|
|
316
|
-
const token = useCookie('auth-token');
|
|
317
|
-
if (token.value) {
|
|
318
|
-
options.headers = {
|
|
319
|
-
...options.headers,
|
|
320
|
-
Authorization: `Bearer ${token.value}`,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
},
|
|
324
|
-
onResponseError({ response }) {
|
|
325
|
-
if (response.status === 401) {
|
|
326
|
-
navigateTo('/login');
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
return {
|
|
332
|
-
provide: { api },
|
|
333
|
-
};
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
// ์ฌ์ฉ
|
|
337
|
-
const { $api } = useNuxtApp();
|
|
338
|
-
const users = await $api('/users');
|
|
339
|
-
|
|
340
|
-
// โ
plugins/dayjs.client.ts (ํด๋ผ์ด์ธํธ ์ ์ฉ)
|
|
341
|
-
import dayjs from 'dayjs';
|
|
342
|
-
import relativeTime from 'dayjs/plugin/relativeTime';
|
|
343
|
-
|
|
344
|
-
export default defineNuxtPlugin(() => {
|
|
345
|
-
dayjs.extend(relativeTime);
|
|
346
|
-
return { provide: { dayjs } };
|
|
347
|
-
});
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
### 9. Composables
|
|
351
|
-
|
|
352
|
-
```typescript
|
|
353
|
-
// โ
composables/useAuth.ts
|
|
354
|
-
export function useAuth() {
|
|
355
|
-
const user = useState<User | null>('auth-user', () => null);
|
|
356
|
-
const isLoggedIn = computed(() => !!user.value);
|
|
357
|
-
|
|
358
|
-
async function login(email: string, password: string) {
|
|
359
|
-
const data = await $fetch('/api/auth/login', {
|
|
360
|
-
method: 'POST',
|
|
361
|
-
body: { email, password },
|
|
362
|
-
});
|
|
363
|
-
user.value = data.user;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async function logout() {
|
|
367
|
-
await $fetch('/api/auth/logout', { method: 'POST' });
|
|
368
|
-
user.value = null;
|
|
369
|
-
await navigateTo('/login');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
return { user, isLoggedIn, login, logout };
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// โ
composables/usePagination.ts
|
|
376
|
-
export function usePagination<T>(
|
|
377
|
-
fetchFn: (page: number) => Promise<{ items: T[]; total: number }>
|
|
378
|
-
) {
|
|
379
|
-
const items = ref<T[]>([]);
|
|
380
|
-
const page = ref(1);
|
|
381
|
-
const total = ref(0);
|
|
382
|
-
const isLoading = ref(false);
|
|
383
|
-
|
|
384
|
-
const hasMore = computed(() => items.value.length < total.value);
|
|
385
|
-
|
|
386
|
-
async function loadMore() {
|
|
387
|
-
if (isLoading.value || !hasMore.value) return;
|
|
388
|
-
|
|
389
|
-
isLoading.value = true;
|
|
390
|
-
const data = await fetchFn(page.value);
|
|
391
|
-
items.value.push(...data.items);
|
|
392
|
-
total.value = data.total;
|
|
393
|
-
page.value++;
|
|
394
|
-
isLoading.value = false;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return { items, isLoading, hasMore, loadMore };
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
### 10. Error Handling
|
|
402
|
-
|
|
403
|
-
```typescript
|
|
404
|
-
// โ
error.vue (์ ์ญ ์๋ฌ ํ์ด์ง)
|
|
405
|
-
<script setup lang="ts">
|
|
406
|
-
const props = defineProps<{
|
|
407
|
-
error: {
|
|
408
|
-
statusCode: number;
|
|
409
|
-
message: string;
|
|
410
|
-
};
|
|
411
|
-
}>();
|
|
412
|
-
|
|
413
|
-
const handleError = () => clearError({ redirect: '/' });
|
|
414
|
-
</script>
|
|
415
|
-
|
|
416
|
-
<template>
|
|
417
|
-
<div class="error-page">
|
|
418
|
-
<h1>{{ error.statusCode }}</h1>
|
|
419
|
-
<p>{{ error.message }}</p>
|
|
420
|
-
<button @click="handleError">ํ์ผ๋ก</button>
|
|
421
|
-
</div>
|
|
422
|
-
</template>
|
|
423
|
-
|
|
424
|
-
// โ
์ปดํฌ๋ํธ ๋ ๋ฒจ ์๋ฌ ์ฒ๋ฆฌ
|
|
425
|
-
<script setup lang="ts">
|
|
426
|
-
const { data, error } = await useFetch('/api/data');
|
|
427
|
-
|
|
428
|
-
if (error.value) {
|
|
429
|
-
throw createError({
|
|
430
|
-
statusCode: error.value.statusCode,
|
|
431
|
-
message: error.value.message,
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
</script>
|
|
435
|
-
|
|
436
|
-
// โ
NuxtErrorBoundary ์ฌ์ฉ
|
|
437
|
-
<template>
|
|
438
|
-
<NuxtErrorBoundary @error="logError">
|
|
439
|
-
<SomeComponent />
|
|
440
|
-
<template #error="{ error, clearError }">
|
|
441
|
-
<p>์ค๋ฅ ๋ฐ์: {{ error.message }}</p>
|
|
442
|
-
<button @click="clearError">๋ค์ ์๋</button>
|
|
443
|
-
</template>
|
|
444
|
-
</NuxtErrorBoundary>
|
|
445
|
-
</template>
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
## ํ์ผ ๊ตฌ์กฐ (Nuxt 3)
|
|
449
|
-
|
|
450
|
-
```
|
|
451
|
-
project/
|
|
452
|
-
โโโ .nuxt/ # ๋น๋ ์ฐ์ถ๋ฌผ (git ์ ์ธ)
|
|
453
|
-
โโโ assets/ # ๋น๋์ ํฌํจ๋๋ ์์
|
|
454
|
-
โโโ components/ # ์๋ import ์ปดํฌ๋ํธ
|
|
455
|
-
โ โโโ ui/ # ๊ธฐ๋ณธ UI ์ปดํฌ๋ํธ
|
|
456
|
-
โ โโโ features/ # ๊ธฐ๋ฅ๋ณ ์ปดํฌ๋ํธ
|
|
457
|
-
โ โโโ App*.vue # ์ฑ ๊ณตํต ์ปดํฌ๋ํธ
|
|
458
|
-
โโโ composables/ # ์๋ import composables
|
|
459
|
-
โโโ layouts/ # ๋ ์ด์์
|
|
460
|
-
โโโ middleware/ # ๋ผ์ฐํธ ๋ฏธ๋ค์จ์ด
|
|
461
|
-
โโโ pages/ # ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ
|
|
462
|
-
โโโ plugins/ # Nuxt ํ๋ฌ๊ทธ์ธ
|
|
463
|
-
โโโ public/ # ์ ์ ํ์ผ
|
|
464
|
-
โโโ server/
|
|
465
|
-
โ โโโ api/ # API ๋ผ์ฐํธ
|
|
466
|
-
โ โโโ middleware/ # ์๋ฒ ๋ฏธ๋ค์จ์ด
|
|
467
|
-
โ โโโ utils/ # ์๋ฒ ์ ํธ๋ฆฌํฐ
|
|
468
|
-
โโโ stores/ # Pinia ์คํ ์ด
|
|
469
|
-
โโโ types/ # TypeScript ํ์
|
|
470
|
-
โโโ utils/ # ์ ํธ๋ฆฌํฐ ํจ์
|
|
471
|
-
โโโ app.vue # ์ฑ ๋ฃจํธ
|
|
472
|
-
โโโ nuxt.config.ts # Nuxt ์ค์
|
|
473
|
-
โโโ tsconfig.json # TypeScript ์ค์
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## ์ํฐํจํด
|
|
477
|
-
|
|
478
|
-
```typescript
|
|
479
|
-
// โ ํด๋ผ์ด์ธํธ์์ ์ง์ DB ์ ๊ทผ
|
|
480
|
-
<script setup>
|
|
481
|
-
import { PrismaClient } from '@prisma/client';
|
|
482
|
-
const prisma = new PrismaClient(); // ํด๋ผ์ด์ธํธ์์ ์คํ ๋ถ๊ฐ
|
|
483
|
-
</script>
|
|
484
|
-
|
|
485
|
-
// โ
Server API ํตํด ์ ๊ทผ
|
|
486
|
-
const { data } = await useFetch('/api/users');
|
|
487
|
-
|
|
488
|
-
// โ useFetch๋ฅผ ์กฐ๊ฑด๋ถ๋ก ์ฌ์ฉ
|
|
489
|
-
if (someCondition) {
|
|
490
|
-
const { data } = await useFetch('/api/data'); // ์๋ฌ ๋ฐ์
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// โ
enabled ์ต์
์ฌ์ฉ
|
|
494
|
-
const { data } = await useFetch('/api/data', {
|
|
495
|
-
immediate: someCondition,
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
// โ navigateTo๋ฅผ setup ๋ฐ์์ ์ฌ์ฉ
|
|
499
|
-
function handleClick() {
|
|
500
|
-
navigateTo('/page'); // ๊ฐ๋ฅํ์ง๋ง ๋น๊ถ์ฅ
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// โ
useRouter ์ฌ์ฉ
|
|
504
|
-
const router = useRouter();
|
|
505
|
-
function handleClick() {
|
|
506
|
-
router.push('/page');
|
|
507
|
-
}
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
## ์ฒดํฌ๋ฆฌ์คํธ
|
|
511
|
-
|
|
512
|
-
- [ ] Auto-imports ํ์ฉ (๋ถํ์ํ import ์ ๊ฑฐ)
|
|
513
|
-
- [ ] Server API ํ์ผ ๋ค์ด๋ฐ ๊ท์น ์ค์ (*.get.ts, *.post.ts)
|
|
514
|
-
- [ ] useFetch/useAsyncData๋ก SSR ์ง์ ๋ฐ์ดํฐ ํ์นญ
|
|
515
|
-
- [ ] useState๋ก ์๋ฒ/ํด๋ผ์ด์ธํธ ์ํ ๊ณต์
|
|
516
|
-
- [ ] definePageMeta๋ก ํ์ด์ง๋ณ ๋ฉํ ์ค์
|
|
517
|
-
- [ ] ๋ฏธ๋ค์จ์ด๋ก ๋ผ์ฐํธ ๋ณดํธ
|
|
518
|
-
- [ ] NuxtErrorBoundary๋ก ์๋ฌ ์ฒ๋ฆฌ
|
|
519
|
-
- [ ] useHead/useSeoMeta๋ก SEO ์ต์ ํ
|
|
520
|
-
- [ ] Composables๋ก ๋ก์ง ์ฌ์ฌ์ฉ
|
|
521
|
-
- [ ] TypeScript ์๊ฒฉ ๋ชจ๋ ์ฌ์ฉ
|