# 전역 규칙
## 1. 작업
### 1.1 순서
1. **이해** — 요청 파악
2. **이해 확인** — 신규 기능·기획·알고리즘·DB 변경 등 큰 작업은 계획표 전에
"이해한 내용 요약 + 애매하거나 미확정인 점 질문"을 먼저 제시 (사용자 승인 후 계획 진행)
> 단순 UI 미세조정·소규모 수정은 생략 가능
3. **작업 계획** — 계획표 제시
4. **작업** — 계획 제시 후 바로 진행
5. **작업 완료** — 완료표 + 테스트 방법 제시
6. **커밋** — 작업 완료 후 자동 진행, `Co-Authored-By: Claude` 포함 금지
> 빌드(`npm run build`)와 푸시(`git push`)는 명시적 요청 시에만 실행
### 1.2 규칙
- 요청 범위 밖 코드는 건드리지 말 것
- 삭제 전 다른 곳에서 참조하는지 확인할 것
- 범위가 클 때는 단계별로 진행하며 중간에 확인받을 것
- 불확실하면 추측하지 말고 물어볼 것
- 버그 수정 시 근본 원인을 파악할 것 (증상만 막는 우회 금지)
- 같은 버그가 2회 이상 안 고쳐지면 추측성 수정 중단 — 콘솔 로그·렌더 추적·마운트 추적 등으로 근본 원인을 먼저 규명한 뒤 접근법 자체를 바꿀 것
- UI 미세조정은 추측 금지 — 높이·정렬·패딩 등 연관 속성은 한 번에 함께 처리해 재요청을 줄일 것
- 데이터 가공·알고리즘 구현 시 각 단계 중간 데이터를 `<pre>`/콘솔로 가시화해 검증 후 다음 단계 진행
- 같은 파일이라도 기능이 다르면 커밋 분리
- 방향 전환이 잦은 작업은 마이크로 단위로 자주 커밋 — 롤백 지점을 촘촘히 남길 것
- 다지점 변경(설정·문서 등)은 단계별 승인 없이 한 번에 적용 — 같은 파일은 Edit 여러 번 대신 Write 1회로
- 태스크 파일: 새 작업이면 시작 전 항목 추가, 커밋 완료 후 완료(`[x]`) 처리
### 1.3 Supabase MCP
- DB 스키마 변경·RLS 정책·데이터 조회 작업 시 Supabase MCP 활용
- MCP 미연결 상태에서 DB 관련 작업 요청 시: 즉시 알리고 작업 일시 중지
| 상황 | 도구 |
|------|------|
| 테이블 스키마 변경 | `apply_migration` |
| 테이블 구조 확인 | `list_tables` |
| 데이터 조회·수정·RLS 확인 | `execute_sql` |
| TypeScript 타입 동기화 | `generate_typescript_types` |
### 1.4 빌드 검증
- 빌드는 자동 실행 금지 — 명시적 요청 또는 "배포 오류" 언급 시에만 실행
- 에러 발생 시 즉시 수정 후 재빌드 — 통과될 때까지 반복
### 1.5 양식
#### 작업 계획표
작업 시작 전 아래 형식으로 계획을 제시할 것.
> **디테일 기준** (설명글만 나열 금지):
> - DB 변경 시 테이블 스키마(컬럼·타입·제약) 명시
> - 신규 타입은 정의 골격 제시
> - 파일별로 "무엇을 어떻게" 수준의 코드 골격 포함
> - 데이터/알고리즘 작업은 단계별 입·출력 예시 데이터 포함
**작업명:** (작업 이름)
**작업 경로:** (페이지 > 섹션 > UI 단위) — 예) 메인 > 헤더 > 로그인 버튼
| 순서 | 파일 | 변경 내용 |
|------|------|-----------|
| 1 | `src/foo/Bar.tsx` | 어떤 이유로 무엇을 어떻게 변경할지 |
| 2 | `src/foo/baz.ts` | 어떤 이유로 무엇을 어떻게 변경할지 |
#### 작업 완료표
작업 완료 후 아래 형식으로 정리할 것:
**작업명:** (작업 이름)
**작업 경로:** (페이지 > 섹션 > UI 단위) — 예) 메인 > 헤더 > 로그인 버튼
| 작업 | 경로 (파일:라인 또는 파일:시작-끝) | 설명 |
|------|-----------------------------------|------|
| 추가 | `src/foo/Bar.tsx:12` | 설명 |
| 수정 | `src/foo/Bar.tsx:34-56` | 설명 |
| 삭제 | `src/foo/Bar.tsx:78` | 설명 |
**요약:** 무엇을 왜 바꿨는지 1~3문장으로 서술.
**테스트:** (페이지 > 섹션 > 동작) 예) 메인 > 로그인 폼 > 제출 버튼 클릭 (UI·동작 변경 없으면 생략)
#### 커밋 컨벤션
형식: `이모지 한국어 설명`
| 이모지 | 용도 |
|--------|------|
| ✨ | 새 기능 |
| 🐛 | 일반 버그 픽스 |
| 🚑 | 긴급 버그 픽스 |
| 🌱 | 일반 / 기타 |
| 🚀 | 배포 |
| ⚙ | 리팩토링 / 설정 변경 |
| 🎨 | 스타일링 |
| 🧪 | 테스트 / 데모 |
#### 기본 답변 양식
- 한국어로 답변할 것
- **순차적 설명** — 단계가 있는 내용은 1 → 2 → 3 순서로 전개
- **예시 기반** — 추상 설명보다 구체 예시(입·출력, 경로, 코드)로 보여줄 것
- **코드 참조 필수** — 설명에는 `파일:라인` 링크 또는 코드 스니펫을 함께 첨부
- **팩트 기반** — 위로·공감·과장 금지. 객관적 사실과 근거만 제시하고, 틀린 건 틀리다고 말할 것
- **최소 출력** — 불필요한 서론·맺음말·반복 제거, 짧고 핵심만 (단, 위 순차·예시·코드 참조는 유지 = "구조는 있되 짧게")
- 파일 검색 시 관련 라인 함께 첨부
---
## 2. 검수
코드 변경 후 품질 검증이 필요할 때 수행. 자동화 도구 없이 DB 쿼리 + 코드 리뷰 방식으로 진행.
### 2.1 검수 순서
1. **범위 파악** — `git log --format="%h %ad %s" --date=format:"%m-%d %H:%M" --since="YYYY-MM-DD"` 로 커밋 목록 조회
2. **DB 검증** — Supabase MCP `execute_sql`로 스키마·제약·FK 확인
3. **코드 리뷰** — 핵심 변경 파일 정적 분석 (로직 추적, 엣지 케이스 확인)
4. **TC 작성** — 아래 양식으로 결과 정리
5. **이슈 수정** — 발견된 버그 즉시 수정 후 커밋
### 2.2 TC 양식
#### A. DB 스키마 검증
Supabase MCP `execute_sql`로 직접 쿼리하여 확인.
```
확인 항목: 컬럼 존재, CHECK 제약, FK, NOT NULL 등
```
| TC | 항목 | 확인 방법 | 결과 |
|----|------|-----------|------|
| A-1 | 컬럼명 존재 | `information_schema.columns` | ✅ / ❌ |
| A-2 | CHECK 제약 내용 | `pg_constraint` | ✅ / ❌ |
#### B. 핵심 로직 코드 리뷰
변경된 핵심 파일 직접 읽고 로직 정합성 확인.
| TC | 항목 | 확인 내용 | 결과 |
|----|------|-----------|------|
| B-1 | 로직명 | 파일:라인 — 확인 내용 | ✅ / ❌ |
#### C. 브라우저 직접 확인 필요
코드 리뷰로 확인 불가한 UI·렌더링·인터랙션 항목.
| TC | 항목 | 테스트 경로 |
|----|------|-------------|
| C-1 | UI 항목 | 페이지 > 섹션 > 동작 확인 |
#### D. 발견된 이슈
| 번호 | 심각도 | 파일 | 내용 |
|------|--------|------|------|
| I-1 | 🔴 심각 / 🟡 경미 / 🟢 낮음 | `경로:라인` | 이슈 설명 |
> 심각도 기준: 🔴 데이터 손실·기능 불가 / 🟡 잘못된 동작·UX 저하 / 🟢 코드 품질·잠재적 위험
---
## 3. 코딩 규칙
### 3.1 공통 (모든 프로젝트)
- TypeScript 사용, `any` 최소화 (불가피한 경우 주석으로 이유 명시)
- 새 파일보다 기존 파일 수정 선호, 불필요한 코드/파일 즉시 정리
- 파일당 컴포넌트 1개 원칙 (스타일 컴포넌트 제외)
- props 타입 항상 명시 (`type PropsType = { ... }`)
- 컴포넌트 로직 200줄 초과 시 분리 고려
- UI 라이브러리 테마로 디자인 토큰(색상·타이포·간격) 중앙 관리 — 값 하드코딩 지양
- 2회 이상 반복되는 UI는 공통 컴포넌트로 추출해 재사용 (`shared/components`)
- `console.log` 배포 전 제거 (console.error는 에러 처리용으로 유지)
- 매직 넘버/문자열은 상수로 분리하여 의미 부여
- 변수·함수명 약어 금지 — 의미 그대로 풀어쓸 것 (예: `app` ❌ → `application` ✅)
- 리턴 가독성 — 복잡한 연산은 중간 결과를 변수에 단계별로 담고 함수 끝에서 변수 하나를 리턴 (한 줄에 map/filter/sort 체이닝 후 바로 리턴 지양)
- 표준 메서드·라이브러리 우선 — 직접 구현보다 검증된 라이브러리(lodash 등) 활용
- DB 스키마 타입 직접 사용 — snake_case 등 DB 구조를 그대로 가져가 불필요한 케이스 변환을 만들지 말 것
### 3.2 아키텍처: 라우트 코로케이션 + Shared
라우트별로 자기완결(self-contained) 폴더를 두고, 전역 공유 자원은 `shared/`에 모은다. 각 레이어는 역할만 담당하며 역할 침범 금지.
**최상위 구조**
```
src/
├── app/ # 라우트 진입점만 (웹: page.tsx/layout.tsx, RN: expo-router 파일)
├── views/ # 라우트별 화면 — 1 라우트 = 1 폴더 (경로를 케밥으로 평탄화)
│ ├── main/
│ ├── admin-songyi/ # /admin/songyi → admin-songyi
│ └── admin-room/
└── shared/ # 전역 공유 레이어
```
**라우트 폴더 (`views/[route]/`)** — 해당 라우트 전용 자원만
```
views/admin-songyi/
├── AdminSongyiView.tsx # 컨테이너 (진입 컴포넌트)
├── _components/ # 이 라우트 전용 컴포넌트
├── _hooks/ # 이 라우트 전용 훅 (데이터 패칭 + 상태 조율)
├── _store/ # 이 라우트 전용 스토어
├── _utils/ # 이 라우트 전용 유틸
└── _constants/ # 이 라우트 전용 상수
```
**`shared/`** — 전역 공유
```
shared/
├── components/ # 전역 공통 컴포넌트
├── hooks/ # 전역 공통 훅
├── store/ # 전역 공통 스토어
├── services/ # Supabase/외부 API 호출 전담 — 모든 DB 호출은 여기
├── utils/ # 전역 유틸
├── constants/ # 전역 상수
└── types/ # 전역 타입
```
**레이어 역할 (침범 금지)**
| 레이어 | 위치 | 역할 | 금지 |
|--------|------|------|------|
| **Presentation** | `views/[route]/`, `views/[route]/_components/`, `shared/components/` | UI 렌더링 | 데이터 패칭, 유틸/상수 정의 |
| **Application** | `views/[route]/_hooks/`, `shared/hooks/` | 데이터 패칭 + 로컬 상태 조율 | UI 반환, service 우회(supabase 직접 호출) |
| **Service** | `shared/services/` | Supabase/외부 API 호출 전담, 순수 async 함수 | 상태 변경, UI 로직 |
| **State** | `views/[route]/_store/`, `shared/store/` | 상태 변경만 | 데이터 패칭, 비즈니스 로직 |
| **Shared** | `shared/utils/`, `shared/constants/`, `shared/types/` | 전역 재사용 유틸·상수·타입 | 특정 라우트 종속 로직 |
**규칙**
- 라우트 평탄화: 중첩 경로는 케밥으로 (`/admin/songyi` → `admin-songyi`)
- 라우트 전용 자원은 해당 폴더의 `_components`/`_hooks`/`_store`/`_utils`/`_constants`에 둔다
- **2개 이상 라우트가 공유하면 `shared/`로 승격** (중복 정의 금지)
- 모든 DB 호출은 `shared/services/`에만 — 컴포넌트·훅에서 supabase 직접 import 금지
- 컴포넌트는 service 직접 호출 금지 → 반드시 훅 경유
- 컴포넌트의 `useState`는 순수 UI 상태(모달 열림, 탭 선택 등)만 허용
- 유틸 함수는 컴포넌트·훅 내부 인라인 정의 금지
- `app/`은 라우트 진입만, 실제 화면 로직은 `views/[route]/[Route]View.tsx`로 분리 (웹·RN 공통)
### 3.3 에러 (공통 원칙)
- catch 블록: `console.error` 후 `throw`
- 사용자 노출 에러: 토스트로 안내 (구현 라이브러리는 스택별 섹션 참조)
### 3.4 주석 (공통)
- 한국어로 작성
- 섹션: `//////////////////////////////////////// 섹션명 ////////////////////////////////////////`
- 중간: `////////// 함수/기능명`
- 소단위: `//////////////////// 설명 ////////////////////`
- 인라인: `// 설명`
- JSX: `{/* 설명 */}`
### 3.5 보안 (공통)
- 민감 정보 하드코딩 금지, 환경변수로 처리
- 커밋 전 민감 정보(키, 비밀번호, 토큰) 포함 여부 확인
---
> **양식**: 3.6·3.7은 동일 구조 — **기술 스택 / 라이브러리 / 코딩 스타일 / 코딩 규칙**.
> 라이브러리 표의 ⭐ = 확정 채택, 무표시 = 웹 표준 권장(프로젝트별 실제 채택은 다를 수 있음).
### 3.6 웹 [적용: Next.js + MUI/Emotion 프로젝트]
**기술 스택**
- Next.js (App Router) · React · TypeScript · Vercel(배포)
**라이브러리** (역할 고정)
| 관심사 | 라이브러리 | 규칙 |
|--------|-----------|------|
| 라우팅 | ⭐ Next.js App Router | `app/`은 라우트만, 화면은 `views/[route]/` |
| 서버 상태/패칭 | ⭐ useEffect + async 직접 | React Query 미도입 (도입 필요 시 먼저 논의) |
| 클라 상태 | ⭐ Zustand (persist) | UI/세션 등 클라 상태만 |
| UI 컴포넌트 | ⭐ MUI v6 | 공통 UI는 테마/공통 컴포넌트로 재사용 |
| 스타일링 | ⭐ Emotion (styled) | 스타일은 파일 하단 모음, `$` prefix |
| 토스트/알림 | ⭐ notistack | 사용자 노출 에러 `enqueueSnackbar` |
| 날짜 | ⭐ dayjs | |
| 백엔드 | ⭐ @supabase/supabase-js | DB 호출은 `shared/services`에만 |
| 폼/검증 | react-hook-form + zod | zodResolver로 검증 |
| 아이콘 | @mui/icons-material | |
| 이미지 | next/image | 최적화·lazy 로딩 |
| 애니메이션 | Framer Motion | 간단한 건 MUI transition |
| 리스트 가상화 | @tanstack/react-virtual | 대용량 리스트 |
| 에러 모니터링 | @sentry/nextjs | 루트 wrap |
**코딩 스타일**
- 스타일: MUI + Emotion styled components (스타일은 파일 하단에 모아서 정리)
- styled component 이름은 PascalCase, 의미 있게 작성
- 조건부 props는 `$` prefix (예: `$isFilled`, `$isActive`) — DOM 전파 방지
- 간단한 레이아웃은 MUI `<Stack>`, `<Grid2>`
- 테마: MUI `ThemeProvider` + 중앙 theme(palette·typography·spacing·component override)로 관리 — `sx`/인라인 하드코딩 지양
- 공통 컴포넌트는 `shared/components`에 두고 재사용 (MUI 위에 프로젝트 표준 래퍼 구성)
**코딩 규칙**
- 폴더 구조: 3.2(라우트 코로케이션) 따름 — `app/`=라우트, 화면=`views/[route]/`
- 데이터 패칭: `useEffect` + async 직접 호출 (React Query 미도입)
- 빌드: `npm run build`
- 보안: 서버 전용 환경변수에 `NEXT_PUBLIC_` 접두사 금지 / `.env.local` 수정 시 `.env.production` 동기화
---
### 3.7 앱 [적용: Expo/RN 프로젝트]
**기술 스택**
- Expo SDK 56 · expo-router(파일기반 `app/`) · React 19 · React Native 0.85 · TypeScript 6
- ⚠️ 버전이 최신이라 API는 context7로 확인 후 사용 (추측 금지)
**라이브러리** (역할 고정 · ⭐ 확정 채택)
| 관심사 | 라이브러리 | 규칙 |
|--------|-----------|------|
| 라우팅 | ⭐ expo-router | `app/` 파일기반, 화면은 `views/[route]/` |
| 서버 상태 | ⭐ @tanstack/react-query | 패칭·캐시·뮤테이션 전담 (useEffect 직접 패칭 지양) |
| 클라 상태 | ⭐ zustand | UI/세션 등 클라 상태만 |
| 영구 저장 | ⭐ react-native-mmkv | zustand persist 스토리지 어댑터 |
| 민감 저장 | ⭐ expo-secure-store | 토큰·비밀값은 mmkv 아닌 secure-store |
| 백엔드 | ⭐ @supabase/supabase-js | DB 호출은 `shared/services`, entry 최상단에 `react-native-url-polyfill`·`react-native-get-random-values` import |
| 폼/검증 | ⭐ react-hook-form + zod | zodResolver로 검증 |
| UI 컴포넌트 | ⭐ react-native-paper(MD3) + @expo/ui | |
| 바텀시트 | ⭐ @gorhom/bottom-sheet | |
| 리스트 | ⭐ @shopify/flash-list | FlatList 대신 FlashList |
| 이미지 | ⭐ expo-image | RN Image 대신 (캐싱) |
| 애니메이션 | ⭐ reanimated v4 + worklets + gesture-handler | |
| 날짜 | ⭐ dayjs | |
| 에러 모니터링 | ⭐ @sentry/react-native | 루트 wrap |
**코딩 스타일**
- 스타일: react-native-paper(MD3) 우선, 커스텀은 `StyleSheet.create` (인라인 지양)
- 테마: react-native-paper 커스텀 테마 + `PaperProvider`로 색·폰트(디자인 토큰) 관리 — 값 하드코딩 지양
- 공통 컴포넌트는 `shared/components`에 두고 재사용 (Paper 위에 프로젝트 표준 래퍼 구성)
**코딩 규칙**
- 폴더 구조: 3.2(라우트 코로케이션) 따름 — `app/`=expo-router, 화면=`views/[route]/`
- 빌드/실행: `expo start` · lint: `expo lint`
- TS strict, 네이티브 의존 추가 시 `expo-*` 우선 검토
- Supabase 사용 시 entry 최상단 폴리필 import 순서 주의
---
> 현재 스택으로 해결하기 어렵거나 더 적합한 라이브러리가 있으면 이유와 함께 추천할 것 (스택 공통)