diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 00000000..a480cc00 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,394 @@ +# .cursorrules + +> **디프만 18기 3팀 — 위시리스트 소비 결정 서비스** +> 이 문서는 Cursor AI가 프로젝트 작업 시 반드시 따라야 할 규칙 및 컨텍스트를 정의합니다. +> 팀원 전원이 공유하는 AI 협업 규약이며, 모든 코드 생성/리팩토링은 이 규약을 따릅니다. + +--- + +## 🎯 프로젝트 컨텍스트 + +### 한 줄 정의 +**쌓인 위시리스트에서 먼저 살 것을 골라주는 소비 결정 서비스** + +### 타겟 유저 +위시리스트와 장바구니는 가득하지만, 선택 피로로 구매를 계속 미루는 패션·라이프스타일 중심의 **20~30대 모바일 쇼핑 사용자**. + +### 핵심 기능 +- **링크 기반 상품 저장** — 여러 쇼핑몰 링크를 한곳에 모음 +- **1:1 토너먼트 비교** — 두 개씩 비교해 최종 1순위 결정 (WOW 포인트) +- **AI 소비 메이트** — 질문/공감/리마인드 (WOW 포인트) +- **보류함 & 재판단 알림** — 방치된 위시 재방문 유도 +- **커머스 직접 연동** — 결정된 상품 즉시 구매 + +### 플랫폼 전략 +**RN(Expo) 앱이 WebView로 Next.js 웹앱을 감싸는 구조.** +→ 실제 UI/비즈니스 로직은 `apps/web`에 집중. 네이티브 기능만 `apps/app`에서 처리. + +--- + +## 💻 기술 스택 + +### 공통 +- **패키지 매니저**: pnpm 10.17.0 +- **모노레포**: Turborepo +- **언어**: TypeScript 5.9.2 +- **배포**: Vercel (web), Expo (app) + +### apps/web (Next.js) +- **프레임워크**: Next.js 16 (App Router) +- **React**: 19.2.0 +- **상태관리**: Zustand +- **스타일링**: Tailwind CSS v4 (루트 공통 설정, 각 앱에서 상속) +- **API 통신**: TanStack Query (@tanstack/react-query) +- **스키마 검증**: Zod +- **API 모킹**: MSW (필요 시) +- **아이콘**: Lucide React 우선 → 불가 시 SVG 컴포넌트화 + +### apps/app (React Native) +- **프레임워크**: React Native + Expo +- **라우팅**: Expo Router +- **주요 역할**: WebView로 `apps/web` 렌더링 + +### CI/CD +- **GitHub Actions**: PR 단위 빌드 검사 (`lint` → `check-types` → `build`) +- **필수 통과 조건**: `pnpm install --frozen-lockfile` + +--- + +## 📁 프로젝트 구조 + +### 모노레포 루트 +``` +18th-team3-client/ +├── apps/ +│ ├── app/ # React Native + Expo (WebView 래퍼) +│ └── web/ # Next.js 16 (메인 서비스) +├── packages/ +│ ├── core/ # @repo/core — 웹뷰 통신용 type/hook/util +│ ├── ui/ # @repo/ui — 공유 UI (현재 미사용) +│ ├── eslint-config/ # @repo/eslint-config +│ └── typescript-config/ # @repo/typescript-config +├── prettier.config.mjs # 루트 Prettier (공통) +├── .prettierignore +└── turbo.json +``` + +### apps/web 내부 (고전 구조) +``` +apps/web/src/ +├── app/ # Next.js App Router (layout, page, providers) +│ ├── layout.tsx +│ ├── page.tsx +│ ├── providers.tsx # TanStack Query Provider +│ └── fonts/ +├── components/ # 재사용 가능한 공통 UI 컴포넌트 +├── apis/ # API 호출 함수 (HTTP 메서드 prefix 컨벤션) +├── hooks/ # 커스텀 훅 +├── utils/ # 공통 유틸리티 함수 +├── types/ # 공통 타입 정의 (T suffix) +├── assets/ # 정적 리소스 (SVG, 이미지) +├── styles/ # globals.css (Tailwind import) +└── consts/ # 상수 +``` + +### Path Alias +`@/*` → `apps/web/src/*` + +--- + +## 📝 네이밍 컨벤션 + +| 대상 | 규칙 | 예시 | +|---|---|---| +| **컴포넌트 파일** | PascalCase | `ScaleResult.tsx` | +| **일반 파일** (훅, 유틸) | camelCase | `useAuth.ts`, `formatDate.ts` | +| **폴더** | kebab-case | `scale-result/` | +| **타입** | T suffix | `UserT`, `ProductT` | +| **API 함수** | HTTP 메서드 prefix | `getUser`, `postWishlist`, `patchProfile`, `deleteItem` | + +--- + +## 🎨 코딩 컨벤션 + +### 컴포넌트 +- **`function` 키워드 + default export** + +```tsx +function MyComponent({ children }: MyComponentProps) { + return
{children}
; +} + +export default MyComponent; +``` + +### 유틸 함수 +- **화살표 함수** 사용 + +```ts +const formatDate = (date: Date) => date.toISOString(); +``` + +### 타입 선언 +- **`type` 사용** (interface 대신) +- **T suffix** (컨벤션상 타입 선언 시) +- Props 타입명: `{ComponentName}Props` + +```ts +type UserT = { + id: number; + name: string; +}; + +type MyComponentProps = { + children: React.ReactNode; +}; +``` + +### Props 네이밍 +- **내부 핸들러**: `handle-` (예: `handleClick`, `handleSubmit`) +- **외부에서 받는 props**: `on-` (예: `onClick`, `onSubmit`) + +```tsx +function Button({ onClick }: ButtonProps) { + const handleClick = () => { + // 내부 로직 + onClick?.(); + }; + return ; +} +``` + +### 기타 +- **세미콜론**: 사용 (`semi: true`) +- **따옴표**: `singleQuote: true` (`'홑따옴표'`) +- **printWidth**: 100 +- **trailingComma**: `'es5'` +- **arrowParens**: `'avoid'` (인자 1개 시 괄호 생략) + +--- + +## 📦 Import 규칙 + +### 정렬 (자동화됨 — `@trivago/prettier-plugin-sort-imports`) +``` +1. 외부 라이브러리 () +2. 절대경로 (^@/) +3. 상대경로 (^[./]) +``` + +### 예시 +```tsx +import { useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/apis/apiClient'; +import { UserT } from '@/types/user'; + +import { formatDate } from './utils'; +import styles from './Page.module.css'; +``` + +### Prettier 옵션 +- `importOrderSeparation: true` (그룹 간 빈 줄) +- `importOrderSortSpecifiers: true` (그룹 내 알파벳 정렬) + +--- + +## 🚨 ESLint 주요 규칙 + +- `no-console`: warn/error만 허용 (`console.log` 금지) +- `no-nested-ternary`: error (중첩 삼항 금지) +- `@typescript-eslint/consistent-type-imports`: error (`import type` 강제) +- `@typescript-eslint/no-explicit-any`: error (`any` 금지) +- `unused-imports/no-unused-imports`: error +- `_` prefix 변수/인자는 unused 허용 + +--- + +## 🔀 Git 전략 + +### 브랜치 구조 +``` +main ← dev ← {type}/{issue-number}-{description} +``` + +### 브랜치 네이밍 +- **패턴**: `{type}/{issue-number}-{description}` +- **예시**: `feat/1-login-page`, `chore/3-web-setup`, `fix/5-auth-bug` +- **타입**: `feat`, `fix`, `chore`, `docs`, `refactor`, `style`, `test` + +### 커밋 메시지 +- **형식**: `{type}: {한글 설명}` +- **예시**: + - `feat: 로그인 페이지 구현` + - `chore: Prettier 설정 추가` + - `fix: 토큰 만료 처리 수정` + +### 머지 방식 +- **Squash Merge** 사용 +- PR 제목이 그대로 dev의 커밋 메시지가 됨 → PR 제목 신중히 작성 + +### PR 컨벤션 +- **base 브랜치**: `dev` (main 아님) +- **템플릿**: +```markdown +## 작업 내용 +[내용 정리] + +## 스크린샷 + +## 연관 이슈 +closes #이슈번호 +``` + +### 코드 리뷰 — PN 룰 +- **P1** (Request changes): 꼭 반영 — 중대한 오류 가능성 +- **P2** (Request changes): 적극 고려 — 수용 or 토론 +- **P3** (Comment): 웬만하면 반영 — 미반영 시 사유 설명 +- **P4** (Approve): 반영해도/안해도 OK — 고민 정도 +- **P5** (Approve): 사소한 의견 — 무시 가능 + +### 리뷰 규칙 +- **랜덤 1명 승인** 시 머지 가능 +- 리뷰 자동 배정 워크플로우 사용 + +--- + +## 🌐 API 통신 — Response Schema 규약 + +### 기본 원칙 +1. **HTTP Status Code는 REST 의미대로 사용** (200, 201, 400, 401, 403, 404, 500) +2. **성공/실패 Body 구조 통일** +3. **`fetch`는 4xx/5xx에서 자동 throw 안 함** → 반드시 `response.ok` 확인 +4. **`success` 필드 사용 안 함** (`response.ok`와 중복) + +### 응답 구조 +```json +{ + "status": 200, + "data": {}, + "detail": "요청이 정상적으로 처리되었습니다.", + "code": "COMMON_SUCCESS" +} +``` + +| 필드 | 설명 | +|---|---| +| `status` | HTTP Status Code와 동일 | +| `data` | 실제 비즈니스 응답 데이터 (실패 시 `null`) | +| `detail` | 응답 메시지 (사용자 표시용) | +| `code` | 서버 정의 Enum 코드 (세부 분기용) | + +### 검증 오류 응답 (400) +```json +{ + "status": 400, + "data": null, + "detail": "입력값이 올바르지 않습니다.", + "code": "INVALID_INPUT", + "errors": [ + { "field": "url", "reason": "URL 형식이 올바르지 않습니다." } + ] +} +``` + +### 페이지 응답 구조 +```json +{ + "status": 200, + "data": [ { "wishId": 1, "title": "셔츠" } ], + "detail": "요청이 정상적으로 처리되었습니다.", + "code": "COMMON_SUCCESS", + "pageInfo": { "nextCursor": "abc123", "hasNext": true } +} +``` + +### 프론트엔드 fetch 처리 표준 +```ts +export async function request(url: string, options?: RequestInit) { + const response = await fetch(url, options); + + let body; + try { + body = await response.json(); + } catch { + throw new Error('서버 응답을 해석할 수 없습니다.'); + } + + if (response.ok) { + return body.data; + } + + throw new Error(body.detail || '요청 처리 중 오류가 발생했습니다.'); +} +``` + +### 검증 오류 세부 처리 +```ts +if (response.status === 400 && body.errors) { + return body.errors; +} +``` + +### 분기 처리 (세부 에러) +```ts +if (body.code === 'WISH_NOT_FOUND') { + // 특정 에러 처리 +} +``` + +### HTTP Status 사용 기준 +| 상황 | HTTP Status | +|---|---| +| 조회 성공 | 200 OK | +| 생성 성공 | 201 Created | +| 잘못된 요청 | 400 Bad Request | +| 인증 실패 | 401 Unauthorized | +| 권한 없음 | 403 Forbidden | +| 리소스 없음 | 404 Not Found | +| 서버 오류 | 500 Internal Server Error | + +--- + +## 🤖 Cursor AI 작업 지침 + +### 코드 생성 시 +- **위 컨벤션을 반드시 준수** +- 신규 컴포넌트: `function` 키워드 + PascalCase 파일명 + default export +- 신규 훅: 화살표 함수 + camelCase 파일명 +- 신규 타입: `type` 키워드 + T suffix +- API 함수: HTTP 메서드 prefix (`getUser`, `postWishlist` 등) +- 경로는 `@/*` 절대경로 우선, 같은 디렉토리는 상대경로 + +### 커밋 메시지 제안 시 +- `{type}: {한글 설명}` 형식 사용 +- type: feat, fix, chore, docs, refactor, style, test + +### PR 작성 시 +- 제목: `{type}: {한글 설명}` 형식 +- 본문에 `closes #이슈번호` 포함 +- base 브랜치는 `dev` + +### 리뷰 코멘트 작성 시 +- PN 태그 (P1~P5) 사용 +- 이유 명확히 설명 + +### API 관련 코드 생성 시 +- HTTP status 기반 분기 (`response.ok`) +- body 구조: `{ status, data, detail, code }` 가정 +- fetch 래퍼 함수 활용 패턴 + + +### 추천 패턴 +- ✅ TanStack Query로 API 호출 래핑 +- ✅ Zod로 응답 검증 +- ✅ Zustand로 전역 상태 +- ✅ Tailwind 클래스 사용 (CSS Module 지양) +- ✅ Error Boundary / Suspense는 필요한 페이지만 선택 적용 + +### 의심스러울 때 +- 기존 코드 패턴 확인 +- 팀 컨벤션 우선 (이 문서) +- `CLAUDE.md` 참고 (동일 내용) +- 판단 어려우면 사용자에게 확인 요청 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..35aaf74a --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,386 @@ +# CLAUDE.md + +> **디프만 18기 3팀 — 위시리스트 소비 결정 서비스** +> 이 문서는 Claude Code가 프로젝트 작업 시 반드시 따라야 할 규칙 및 컨텍스트를 정의합니다. +> 팀원 전원이 공유하는 AI 협업 규약이며, 모든 코드 생성/리팩토링은 이 규약을 따릅니다. + +--- + +## 🎯 프로젝트 컨텍스트 + +### 한 줄 정의 +**쌓인 위시리스트에서 먼저 살 것을 골라주는 소비 결정 서비스** + +### 타겟 유저 +위시리스트와 장바구니는 가득하지만, 선택 피로로 구매를 계속 미루는 패션·라이프스타일 중심의 **20~30대 모바일 쇼핑 사용자**. + +### 핵심 기능 +- **링크 기반 상품 저장** — 여러 쇼핑몰 링크를 한곳에 모음 +- **1:1 토너먼트 비교** — 두 개씩 비교해 최종 1순위 결정 (WOW 포인트) +- **AI 소비 메이트** — 질문/공감/리마인드 (WOW 포인트) +- **보류함 & 재판단 알림** — 방치된 위시 재방문 유도 +- **커머스 직접 연동** — 결정된 상품 즉시 구매 + +### 플랫폼 전략 +**RN(Expo) 앱이 WebView로 Next.js 웹앱을 감싸는 구조.** +→ 실제 UI/비즈니스 로직은 `apps/web`에 집중. 네이티브 기능만 `apps/app`에서 처리. + +--- + +## 💻 기술 스택 + +### 공통 +- **패키지 매니저**: pnpm 10.17.0 +- **모노레포**: Turborepo +- **언어**: TypeScript 5.9.2 +- **배포**: Vercel (web), Expo (app) + +### apps/web (Next.js) +- **프레임워크**: Next.js 16 (App Router) +- **React**: 19.2.0 +- **상태관리**: Zustand +- **스타일링**: Tailwind CSS v4 (루트 공통 설정, 각 앱에서 상속) +- **API 통신**: TanStack Query (@tanstack/react-query) +- **스키마 검증**: Zod +- **API 모킹**: MSW (필요 시) +- **아이콘**: Lucide React 우선 → 불가 시 SVG 컴포넌트화 + +### apps/app (React Native) +- **프레임워크**: React Native + Expo +- **라우팅**: Expo Router +- **주요 역할**: WebView로 `apps/web` 렌더링 + +### CI/CD +- **GitHub Actions**: PR 단위 빌드 검사 (`lint` → `check-types` → `build`) +- **필수 통과 조건**: `pnpm install --frozen-lockfile` + +--- + +## 📁 프로젝트 구조 + +### 모노레포 루트 +``` +18th-team3-client/ +├── apps/ +│ ├── app/ # React Native + Expo (WebView 래퍼) +│ └── web/ # Next.js 16 (메인 서비스) +├── packages/ +│ ├── core/ # @repo/core — 웹뷰 통신용 type/hook/util +│ ├── ui/ # @repo/ui — 공유 UI (현재 미사용) +│ ├── eslint-config/ # @repo/eslint-config +│ └── typescript-config/ # @repo/typescript-config +├── prettier.config.mjs # 루트 Prettier (공통) +├── .prettierignore +└── turbo.json +``` + +### apps/web 내부 (고전 구조) +``` +apps/web/src/ +├── app/ # Next.js App Router (layout, page, providers) +│ ├── layout.tsx +│ ├── page.tsx +│ ├── providers.tsx # TanStack Query Provider +│ └── fonts/ +├── components/ # 재사용 가능한 공통 UI 컴포넌트 +├── apis/ # API 호출 함수 (HTTP 메서드 prefix 컨벤션) +├── hooks/ # 커스텀 훅 +├── utils/ # 공통 유틸리티 함수 +├── types/ # 공통 타입 정의 (T suffix) +├── assets/ # 정적 리소스 (SVG, 이미지) +├── styles/ # globals.css (Tailwind import) +└── consts/ # 상수 +``` + +### Path Alias +`@/*` → `apps/web/src/*` + +--- + +## 📝 네이밍 컨벤션 + +| 대상 | 규칙 | 예시 | +|---|---|---| +| **컴포넌트 파일** | PascalCase | `ScaleResult.tsx` | +| **일반 파일** (훅, 유틸) | camelCase | `useAuth.ts`, `formatDate.ts` | +| **폴더** | kebab-case | `scale-result/` | +| **타입** | T suffix | `UserT`, `ProductT` | +| **API 함수** | HTTP 메서드 prefix | `getUser`, `postWishlist`, `patchProfile`, `deleteItem` | + +--- + +## 🎨 코딩 컨벤션 + +### 컴포넌트 +- **`function` 키워드 + default export** + +```tsx +function MyComponent({ children }: MyComponentProps) { + return
{children}
; +} + +export default MyComponent; +``` + +### 유틸 함수 +- **화살표 함수** 사용 + +```ts +const formatDate = (date: Date) => date.toISOString(); +``` + +### 타입 선언 +- **`type` 사용** (interface 대신) +- **T suffix** (컨벤션상 타입 선언 시) +- Props 타입명: `{ComponentName}Props` + +```ts +type UserT = { + id: number; + name: string; +}; + +type MyComponentProps = { + children: React.ReactNode; +}; +``` + +### Props 네이밍 +- **내부 핸들러**: `handle-` (예: `handleClick`, `handleSubmit`) +- **외부에서 받는 props**: `on-` (예: `onClick`, `onSubmit`) + +```tsx +function Button({ onClick }: ButtonProps) { + const handleClick = () => { + // 내부 로직 + onClick?.(); + }; + return ; +} +``` + +### 기타 +- **세미콜론**: 사용 (`semi: true`) +- **따옴표**: `singleQuote: true` (`'홑따옴표'`) +- **printWidth**: 100 +- **trailingComma**: `'es5'` +- **arrowParens**: `'avoid'` (인자 1개 시 괄호 생략) + +--- + +## 📦 Import 규칙 + +### 정렬 (자동화됨 — `@trivago/prettier-plugin-sort-imports`) +``` +1. 외부 라이브러리 () +2. 절대경로 (^@/) +3. 상대경로 (^[./]) +``` + +### 예시 +```tsx +import { useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; + +import { apiClient } from '@/apis/apiClient'; +import { UserT } from '@/types/user'; + +import { formatDate } from './utils'; +import styles from './Page.module.css'; +``` + +### Prettier 옵션 +- `importOrderSeparation: true` (그룹 간 빈 줄) +- `importOrderSortSpecifiers: true` (그룹 내 알파벳 정렬) + +--- + +## 🚨 ESLint 주요 규칙 + +- `no-console`: warn/error만 허용 (`console.log` 금지) +- `no-nested-ternary`: error (중첩 삼항 금지) +- `@typescript-eslint/consistent-type-imports`: error (`import type` 강제) +- `@typescript-eslint/no-explicit-any`: error (`any` 금지) +- `unused-imports/no-unused-imports`: error +- `_` prefix 변수/인자는 unused 허용 + +--- + +## 🔀 Git 전략 + +### 브랜치 구조 +``` +main ← dev ← {type}/{issue-number}-{description} +``` + +### 브랜치 네이밍 +- **패턴**: `{type}/{issue-number}-{description}` +- **예시**: `feat/1-login-page`, `chore/3-web-setup`, `fix/5-auth-bug` +- **타입**: `feat`, `fix`, `chore`, `docs`, `refactor`, `style`, `test` + +### 커밋 메시지 +- **형식**: `{type}: {한글 설명}` +- **예시**: + - `feat: 로그인 페이지 구현` + - `chore: Prettier 설정 추가` + - `fix: 토큰 만료 처리 수정` + +### 머지 방식 +- **Squash Merge** 사용 +- PR 제목이 그대로 dev의 커밋 메시지가 됨 → PR 제목 신중히 작성 + +### PR 컨벤션 +- **base 브랜치**: `dev` (main 아님) +- **템플릿**: +```markdown +## 작업 내용 +[내용 정리] + +## 스크린샷 + +## 연관 이슈 +closes #이슈번호 +``` + +### 코드 리뷰 — PN 룰 +- **P1** (Request changes): 꼭 반영 — 중대한 오류 가능성 +- **P2** (Request changes): 적극 고려 — 수용 or 토론 +- **P3** (Comment): 웬만하면 반영 — 미반영 시 사유 설명 +- **P4** (Approve): 반영해도/안해도 OK — 고민 정도 +- **P5** (Approve): 사소한 의견 — 무시 가능 + +### 리뷰 규칙 +- **랜덤 1명 승인** 시 머지 가능 +- 리뷰 자동 배정 워크플로우 사용 + +--- + +## 🌐 API 통신 — Response Schema 규약 + +### 기본 원칙 +1. **HTTP Status Code는 REST 의미대로 사용** (200, 201, 400, 401, 403, 404, 500) +2. **성공/실패 Body 구조 통일** +3. **`fetch`는 4xx/5xx에서 자동 throw 안 함** → 반드시 `response.ok` 확인 +4. **`success` 필드 사용 안 함** (`response.ok`와 중복) + +### 응답 구조 +```json +{ + "status": 200, + "data": {}, + "detail": "요청이 정상적으로 처리되었습니다.", + "code": "COMMON_SUCCESS" +} +``` + +| 필드 | 설명 | +|---|---| +| `status` | HTTP Status Code와 동일 | +| `data` | 실제 비즈니스 응답 데이터 (실패 시 `null`) | +| `detail` | 응답 메시지 (사용자 표시용) | +| `code` | 서버 정의 Enum 코드 (세부 분기용) | + +### 검증 오류 응답 (400) +```json +{ + "status": 400, + "data": null, + "detail": "입력값이 올바르지 않습니다.", + "code": "INVALID_INPUT", + "errors": [ + { "field": "url", "reason": "URL 형식이 올바르지 않습니다." } + ] +} +``` + +### 페이지 응답 구조 +```json +{ + "status": 200, + "data": [ { "wishId": 1, "title": "셔츠" } ], + "detail": "요청이 정상적으로 처리되었습니다.", + "code": "COMMON_SUCCESS", + "pageInfo": { "nextCursor": "abc123", "hasNext": true } +} +``` + +### 프론트엔드 fetch 처리 표준 +```ts +export async function request(url: string, options?: RequestInit) { + const response = await fetch(url, options); + + let body; + try { + body = await response.json(); + } catch { + throw new Error('서버 응답을 해석할 수 없습니다.'); + } + + if (response.ok) { + return body.data; + } + + throw new Error(body.detail || '요청 처리 중 오류가 발생했습니다.'); +} +``` + +### 검증 오류 세부 처리 +```ts +if (response.status === 400 && body.errors) { + return body.errors; +} +``` + +### 분기 처리 (세부 에러) +```ts +if (body.code === 'WISH_NOT_FOUND') { + // 특정 에러 처리 +} +``` + +### HTTP Status 사용 기준 +| 상황 | HTTP Status | +|---|---| +| 조회 성공 | 200 OK | +| 생성 성공 | 201 Created | +| 잘못된 요청 | 400 Bad Request | +| 인증 실패 | 401 Unauthorized | +| 권한 없음 | 403 Forbidden | +| 리소스 없음 | 404 Not Found | +| 서버 오류 | 500 Internal Server Error | + +--- + +## 🤖 Claude 작업 지침 + +### 코드 생성 시 +- **위 컨벤션을 반드시 준수** +- 신규 컴포넌트: `function` 키워드 + PascalCase 파일명 + default export +- 신규 훅: 화살표 함수 + camelCase 파일명 +- 신규 타입: `type` 키워드 + T suffix +- API 함수: HTTP 메서드 prefix (`getUser`, `postWishlist` 등) +- 경로는 `@/*` 절대경로 우선, 같은 디렉토리는 상대경로 + +### 커밋 메시지 제안 시 +- `{type}: {한글 설명}` 형식 사용 +- type: feat, fix, chore, docs, refactor, style, test + +### PR 작성 시 +- 제목: `{type}: {한글 설명}` 형식 +- 본문에 `closes #이슈번호` 포함 +- base 브랜치는 `dev` + +### 리뷰 코멘트 작성 시 +- PN 태그 (P1~P5) 사용 +- 이유 명확히 설명 + +### API 관련 코드 생성 시 +- HTTP status 기반 분기 (`response.ok`) +- body 구조: `{ status, data, detail, code }` 가정 +- fetch 래퍼 함수 활용 패턴 + + +### 의심스러울 때 +- 기존 코드 패턴 확인 +- 팀 컨벤션 우선 (이 문서) +- 판단 어려우면 사용자에게 확인