116 lines
3.0 KiB
Markdown
116 lines
3.0 KiB
Markdown
# 코드 스타일 & 네이밍 컨벤션
|
|
|
|
> Nuxt 4 + TypeScript strict 환경 기준
|
|
> 최종 업데이트: 2026-04-07
|
|
|
|
---
|
|
|
|
## 디렉토리 구조
|
|
|
|
```
|
|
my-nuxt-app/
|
|
├─ app/
|
|
│ ├─ assets/
|
|
│ ├─ components/ # PascalCase.vue
|
|
│ │ └─ ui/ # shadcn-vue 컴포넌트
|
|
│ ├─ composables/ # useXxx.ts
|
|
│ ├─ layouts/ # kebab-case.vue
|
|
│ ├─ middleware/ # kebab-case.ts
|
|
│ ├─ pages/ # kebab-case.vue
|
|
│ ├─ plugins/ # kebab-case.ts
|
|
│ ├─ stores/ # useXxxStore.ts
|
|
│ ├─ utils/ # camelCase.ts
|
|
│ ├─ app.vue
|
|
│ ├─ app.config.ts # 테마/UI 등 공개 설정값
|
|
│ └─ error.vue
|
|
├─ server/
|
|
│ ├─ api/ # kebab-case.ts
|
|
│ ├─ middleware/
|
|
│ └─ utils/
|
|
├─ shared/ # 클라이언트/서버 공유 타입 및 유틸
|
|
├─ public/
|
|
├─ nuxt.config.ts
|
|
├─ tailwind.config.ts
|
|
└─ tsconfig.json
|
|
```
|
|
|
|
---
|
|
|
|
## 네이밍 컨벤션
|
|
|
|
### 파일명
|
|
|
|
| 파일 종류 | 규칙 | 예시 |
|
|
|-----------|------|------|
|
|
| 컴포넌트 | `PascalCase.vue` | `UserProfile.vue` |
|
|
| 페이지 / 레이아웃 / 미들웨어 / 플러그인 | `kebab-case` | `user-profile.vue` |
|
|
| 컴포저블 | `useXxx.ts` | `useUserProfile.ts` |
|
|
| 유틸 함수 | `camelCase.ts` | `formatDate.ts` |
|
|
| Pinia store | `useXxxStore.ts` | `useAuthStore.ts` |
|
|
| API 라우트 | `kebab-case.ts` | `user-profile.ts` |
|
|
| 테스트 파일 | `*.spec.ts` / `*.test.ts` | `UserProfile.spec.ts` |
|
|
|
|
### 코드 내
|
|
|
|
| 대상 | 규칙 | 예시 |
|
|
|------|------|------|
|
|
| 변수 / 함수 | `camelCase` | `const userName`, `function fetchUser()` |
|
|
| 컴포넌트명 | `PascalCase` | `UserProfile`, `AppHeader` |
|
|
| **상수** | **`UPPER_SNAKE_CASE`** | `const MAX_RETRY_COUNT = 3` |
|
|
| 타입 / 인터페이스 | `PascalCase` | `interface UserProfile` |
|
|
|
|
```typescript
|
|
// DO
|
|
const MAX_PAGE_SIZE = 100
|
|
const DEFAULT_LOCALE = 'ko'
|
|
|
|
// DON'T
|
|
const maxPageSize = 100 // 금지: 상수에 camelCase
|
|
const defaultLocale = 'ko' // 금지
|
|
```
|
|
|
|
---
|
|
|
|
## TypeScript 엄격 모드
|
|
|
|
### any 타입 사용 금지
|
|
|
|
`any`는 타입 안전성을 무력화한다. `unknown` 또는 명시적 타입을 사용한다.
|
|
|
|
```typescript
|
|
// DON'T
|
|
async function fetchUser(id: any): Promise<any> {
|
|
return await $fetch(`/api/users/${id}`)
|
|
}
|
|
|
|
// DO
|
|
async function fetchUser(id: string): Promise<User> {
|
|
return await $fetch<User>(`/api/users/${id}`)
|
|
}
|
|
|
|
// unknown 사용 시 타입 좁히기
|
|
function parseResponse(raw: unknown): User {
|
|
if (!isUser(raw)) throw new Error('유효하지 않은 응답입니다.')
|
|
return raw
|
|
}
|
|
```
|
|
|
|
### Props 타입 명시
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
// DO: interface로 분리 선언
|
|
interface Props {
|
|
title: string
|
|
count: number
|
|
variant?: 'primary' | 'secondary'
|
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
variant: 'primary',
|
|
})
|
|
|
|
// DON'T: 타입 없는 defineProps
|
|
// const props = defineProps(['title', 'count'])
|
|
</script>
|
|
```
|