feat: 프로젝트 지침 양식 및 설치 스크립트 개선

This commit is contained in:
hyeonggil
2026-04-11 20:00:58 +09:00
parent 2def6f705d
commit d3f7b86f1b
8 changed files with 402 additions and 35 deletions

70
.cursorrules Normal file
View File

@@ -0,0 +1,70 @@
# Nuxt 프로젝트 — Cursor 규칙
## 언어
- 사용자와의 대화·설명·커밋 메시지 본문은 **한국어**를 사용한다.
## Git 커밋 메시지 (필수)
- **제목은 한 줄**, [Conventional Commits](https://www.conventionalcommits.org/) + **이모지**를 함께 쓴다.
- 형식: `<이모지> <type>: <한글 설명>` (이모지·타입·콜론 뒤 공백 한 칸)
- **설명(제목 본문)은 반드시 한글**로 작성한다. 영어 제목만 단독으로 쓰지 않는다.
- **명령형** 어조 (`추가`, `수정` — `추가됨`, `수정함` 지양).
- **첫 줄(제목)은 72자 미만**을 권장한다.
- **원자적 커밋**: 한 커밋에 단일 목적만 담는다. 관련 없는 변경은 분할한다.
- 커밋 생성 시 **Claude·AI 서명·Co-authored-by 등 메타 서명을 본문에 넣지 않는다.**
### 커밋 생성 프로세스 (`/commit` 등 요청 시)
1. **스테이지된 파일**이 있으면 그 파일만 대상으로 커밋한다. 없으면 사용자에게 스테이징 여부를 확인한다.
2. `git diff` 등으로 **논리적 변경 덩어리**를 분석한다.
3. 타입이 섞였거나 관심사가 다르면 **분할 커밋**을 제안한다.
4. 아래 **이모지 맵**과 타입에 맞춰 제목을 만든다.
### 타입 (type)
| type | 용도 |
|------|------|
| `feat` | 새 기능 |
| `fix` | 버그 수정 |
| `docs` | 문서 |
| `style` | 포맷팅·세미콜론 등 의미 없는 스타일 |
| `refactor` | 리팩터링 |
| `perf` | 성능 개선 |
| `test` | 테스트 |
| `chore` | 빌드·도구·잡무 |
| `ci` | CI |
| `build` | 빌드 시스템·번들러 |
### 이모지 맵 (타입·맥락에 맞게 선택)
✨ feat | 🐛 fix | 📝 docs | 💄 style | ♻️ refactor | ⚡ perf | ✅ test | 🔧 chore | 🚀 ci | 🚨 warnings | 🔒️ security | 🚚 move | 🏗️ architecture | add-dep | remove-dep | 🌱 seed | 🧑‍💻 dx | 🏷️ types | 👔 business | 🚸 ux | 🩹 minor-fix | 🥅 errors | 🔥 remove | 🎨 structure | 🚑️ hotfix | 🎉 init | 🔖 release | 🚧 wip | 💚 ci-fix | 📌 pin-deps | 👷 ci-build | 📈 analytics | ✏️ typos | ⏪️ revert | 📄 license | 💥 breaking | 🍱 assets | ♿️ accessibility | 💡 comments | 🗃️ db | 🔊 logs | 🔇 remove-logs | 🙈 gitignore | 📸 snapshots | ⚗️ experiment | 🚩 flags | 💫 animations | ⚰️ dead-code | 🦺 validation | ✈️ offline
### 분할 제안 기준
- 서로 다른 **관심사**가 한 diff에 섞인 경우
- **타입**이 혼합된 경우 (예: `feat` + `fix`)
- **파일 패턴**이 완전히 다른 영역(예: 앱 코드 vs 인프라만)인 경우
- **변경량이 크고** 커밋 단위로 나눌 수 있는 경우
### 예시
- `✨ feat: 로그인 폼 유효성 검사 추가`
- `♻️ refactor: 사용자 API 호출 로직을 composable로 분리`
- `🐛 fix: 모바일에서 헤더가 겹치는 문제 수정`
### 본문이 필요할 때
- 제목 아래 빈 줄 후 본문을 한글 bullet 또는 문단으로 적는다.
## 코드·작업 방식
- 요청 범위 밖의 리팩터·포맷 일괄 변경·무관 파일 수정을 하지 않는다.
- 기존 코드의 네이밍, import 스타일, 타입·주석 수준에 맞춘다.
- 변경 이유가 드러나는 **작고 집중된 diff**를 선호한다.
## Cursor 사용 시
- 파일을 수정하기 전에 관련 맥락(주변 코드·설정)을 읽고 일관되게 맞춘다.
- 사용자가 명시적으로 요청하지 않은 README·문서 파일은 새로 쓰거나 크게 늘리지 않는다.

View File

@@ -16,8 +16,15 @@ fe-common-rules/
│ ├── framework-rules.md # Vue/Nuxt/Tailwind/라이브러리 규칙
│ ├── commit-pr.md # 커밋 & PR 규칙
│ └── claude-workflow.md # Claude 작업 방식 지침
├── templates/
│ ├── CLAUDE.md.tpl # 프로젝트 루트 CLAUDE.md 템플릿
│ └── project/ # 프로젝트 지침 양식
│ ├── overview.md
│ ├── conventions.md
│ └── architecture.md
├── scripts/
│ ├── install.sh # 프로젝트에 submodule 설치
│ ├── install.sh # 프로젝트에 submodule 설치 + 템플릿 복사
│ ├── init-project.sh # .claude/project 양식만 다시 초기화/diff
│ └── update.sh # 최신 공통 지침으로 업데이트
└── examples/
└── sample-nuxt-project/ # Nuxt4 + Vue3 + TS + Tailwind 샘플
@@ -92,6 +99,45 @@ git commit -m "chore: update fe-common-rules submodule"
---
## 📝 프로젝트 지침 양식 사용
`.claude/project/` 에 들어갈 지침은 공통 저장소의 `templates/project/` 에 양식으로 관리됩니다.
팀원은 `install.sh` 를 사용하면 submodule 설치와 동시에 이 양식을 자동으로 복사받아 작성을
시작할 수 있습니다.
### 신규 프로젝트 — install.sh 한 번으로 끝
```bash
bash .claude/common/scripts/install.sh git@github.com:<org>/fe-common-rules.git
# → .claude/common 에 submodule 설치
# → templates/project/*.md 를 .claude/project/ 로 복사
# → templates/CLAUDE.md.tpl 을 루트 CLAUDE.md 로 복사
```
### 이미 submodule 이 있는 프로젝트 — init-project.sh
이미 공통 저장소를 submodule 로 연결해둔 프로젝트에서 `.claude/project/` 양식만 따로
초기화하거나, 공통 저장소에 새 양식이 추가됐을 때 사용합니다.
```bash
# 기본: 없는 파일만 복사 (기존 파일은 건너뜀)
bash .claude/common/scripts/init-project.sh
# 기존 파일을 덮어쓰고 싶을 때
bash .claude/common/scripts/init-project.sh --force
# 차이만 확인하고 복사는 하지 않기
bash .claude/common/scripts/init-project.sh --diff
```
### 양식을 추가/수정하려면
공통 저장소의 `templates/project/*.md` 를 수정하는 PR 을 올립니다. 머지 후 팀원들은
`git submodule update --remote` 로 새 양식을 받은 뒤, 필요하면
`init-project.sh --diff` 로 자신의 프로젝트 지침과 비교할 수 있습니다.
---
## ✍️ 공통 지침 수정 프로세스
1. 이 저장소를 별도로 clone 합니다.

96
scripts/init-project.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env bash
#
# fe-common-rules project template initializer
# 이미 .claude/common 이 설치된 프로젝트에서 templates/project/ 의
# 양식을 .claude/project/ 에 복사합니다.
#
# 사용법:
# bash .claude/common/scripts/init-project.sh # 없는 파일만 복사 (기본)
# bash .claude/common/scripts/init-project.sh --force # 기존 파일을 덮어씀
# bash .claude/common/scripts/init-project.sh --diff # 차이만 보여주고 복사는 안 함
#
set -euo pipefail
COMMON_PATH=".claude/common"
PROJECT_PATH=".claude/project"
TEMPLATE_DIR="$COMMON_PATH/templates/project"
MODE="safe" # safe | force | diff
for arg in "$@"; do
case "$arg" in
--force) MODE="force" ;;
--diff) MODE="diff" ;;
-h|--help)
grep '^#' "$0" | sed 's/^# \{0,1\}//'
exit 0
;;
*)
echo "❌ 알 수 없는 옵션: $arg" >&2
exit 1
;;
esac
done
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "❌ 현재 디렉토리는 Git 저장소가 아닙니다." >&2
exit 1
fi
cd "$(git rev-parse --show-toplevel)"
if [[ ! -d "$TEMPLATE_DIR" ]]; then
echo "$TEMPLATE_DIR 가 없습니다. 먼저 submodule 을 설치/업데이트하세요:" >&2
echo " git submodule update --init --recursive" >&2
exit 1
fi
mkdir -p "$PROJECT_PATH"
echo "📝 templates/project → $PROJECT_PATH (mode=$MODE)"
echo ""
shopt -s nullglob
copied=0
skipped=0
diffed=0
for f in "$TEMPLATE_DIR"/*.md; do
name="$(basename "$f")"
dest="$PROJECT_PATH/$name"
case "$MODE" in
diff)
if [[ -f "$dest" ]]; then
if ! diff -q "$f" "$dest" >/dev/null 2>&1; then
echo "📄 diff: $dest"
diff -u "$dest" "$f" || true
echo ""
diffed=$((diffed + 1))
fi
else
echo " 새 양식: $dest (복사되지 않음. --force 로 복사하세요)"
diffed=$((diffed + 1))
fi
;;
force)
cp "$f" "$dest"
echo "$dest (덮어씀)"
copied=$((copied + 1))
;;
safe)
if [[ -f "$dest" ]]; then
echo "$dest (이미 존재)"
skipped=$((skipped + 1))
else
cp "$f" "$dest"
echo "$dest"
copied=$((copied + 1))
fi
;;
esac
done
echo ""
case "$MODE" in
diff) echo "🔍 차이가 있는 파일: $diffed";;
*) echo "🎉 완료: 복사 $copied개 / 건너뜀 $skipped개";;
esac

View File

@@ -2,10 +2,11 @@
#
# fe-common-rules installer
# 현재 Git 프로젝트의 .claude/common 경로에 fe-common-rules 저장소를
# submodule 로 추가하고 루트 CLAUDE.md 템플릿을 생성합니다.
# submodule 로 추가하고, templates/ 에서 프로젝트 지침 양식과
# CLAUDE.md 템플릿을 복사합니다.
#
# 사용법:
# bash scripts/install.sh [<repo-url>] [<branch>]
# bash scripts/install.sh <repo-url> [<branch>]
#
# 예:
# bash scripts/install.sh git@github.com:our-team/fe-common-rules.git main
@@ -15,6 +16,7 @@ set -euo pipefail
REPO_URL="${1:-}"
BRANCH="${2:-main}"
TARGET_PATH=".claude/common"
PROJECT_PATH=".claude/project"
if [[ -z "$REPO_URL" ]]; then
echo "❌ 사용법: bash scripts/install.sh <repo-url> [branch]" >&2
@@ -28,9 +30,12 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
exit 1
fi
# 이미 submodule 이 존재하는지 확인
# 루트로 이동
cd "$(git rev-parse --show-toplevel)"
# 1) Submodule 추가
if [[ -d "$TARGET_PATH" ]]; then
echo "⚠️ '$TARGET_PATH' 경로가 이미 존재합니다. 설치를 건너뜁니다."
echo "⚠️ '$TARGET_PATH' 경로가 이미 존재합니다. submodule 추가를 건너뜁니다."
else
echo "📦 fe-common-rules 를 submodule 로 추가합니다..."
git submodule add -b "$BRANCH" "$REPO_URL" "$TARGET_PATH"
@@ -38,12 +43,35 @@ else
echo "✅ submodule 추가 완료: $TARGET_PATH"
fi
# 프로젝트 지침 폴더 생성
mkdir -p .claude/project
# 2) 프로젝트 지침 양식 복사 (templates/project/ → .claude/project/)
mkdir -p "$PROJECT_PATH"
# CLAUDE.md 템플릿 생성 (없을 때만)
TEMPLATE_DIR="$TARGET_PATH/templates/project"
if [[ -d "$TEMPLATE_DIR" ]]; then
echo "📝 프로젝트 지침 양식을 복사합니다..."
for f in "$TEMPLATE_DIR"/*.md; do
[[ -e "$f" ]] || continue
name="$(basename "$f")"
dest="$PROJECT_PATH/$name"
if [[ -f "$dest" ]]; then
echo "$dest (이미 존재 - 건너뜀)"
else
cp "$f" "$dest"
echo "$dest"
fi
done
else
echo "⚠️ $TEMPLATE_DIR 를 찾지 못했습니다. 공통 저장소의 templates 가 오래됐을 수 있습니다."
fi
# 3) 루트 CLAUDE.md 템플릿 복사
if [[ ! -f "CLAUDE.md" ]]; then
cat > CLAUDE.md <<'EOF'
TPL_FILE="$TARGET_PATH/templates/CLAUDE.md.tpl"
if [[ -f "$TPL_FILE" ]]; then
cp "$TPL_FILE" CLAUDE.md
echo "✅ CLAUDE.md 템플릿을 생성했습니다."
else
cat > CLAUDE.md <<'EOF'
# <프로젝트 이름>
## 공통 지침
@@ -52,42 +80,31 @@ if [[ ! -f "CLAUDE.md" ]]; then
## 프로젝트 지침
@.claude/project/overview.md
@.claude/project/conventions.md
@.claude/project/architecture.md
EOF
echo "✅ CLAUDE.md 템플릿을 생성했습니다."
echo "✅ CLAUDE.md 템플릿을 생성했습니다. (fallback)"
fi
else
echo " 기존 CLAUDE.md 가 이미 존재합니다. 아래 블록을 수동으로 추가하세요:"
echo ""
echo " ## 공통 지침"
echo " @.claude/common/CLAUDE.md"
echo ""
fi
# 프로젝트 지침 샘플 파일
if [[ ! -f ".claude/project/overview.md" ]]; then
cat > .claude/project/overview.md <<'EOF'
# 프로젝트 개요
- 서비스명:
- 기술 스택:
- 주요 기능:
- 주의사항:
EOF
fi
if [[ ! -f ".claude/project/conventions.md" ]]; then
cat > .claude/project/conventions.md <<'EOF'
# 프로젝트 전용 컨벤션
공통 지침 외에 이 프로젝트에서만 적용되는 규칙을 작성하세요.
EOF
echo " ## 프로젝트 지침"
echo " @.claude/project/overview.md"
echo " @.claude/project/conventions.md"
echo " @.claude/project/architecture.md"
echo ""
fi
echo ""
echo "🎉 설치가 완료되었습니다."
echo " - 공통 지침: .claude/common/CLAUDE.md"
echo " - 프로젝트 지침: .claude/project/"
echo " - 공통 지침: $TARGET_PATH/CLAUDE.md"
echo " - 프로젝트 지침: $PROJECT_PATH/"
echo " - 엔트리 파일: CLAUDE.md"
echo ""
echo "변경 사항을 커밋해 주세요:"
echo " git add .gitmodules .claude CLAUDE.md"
echo " git commit -m 'chore: add fe-common-rules submodule'"
echo "다음 작업을 진행해 주세요:"
echo " 1) $PROJECT_PATH/*.md 내용을 프로젝트에 맞게 채우기"
echo " 2) 변경 사항을 커밋하기"
echo " git add .gitmodules .claude CLAUDE.md"
echo " git commit -m 'chore: add fe-common-rules submodule'"

9
templates/CLAUDE.md.tpl Normal file
View File

@@ -0,0 +1,9 @@
# <프로젝트 이름>
## 공통 지침
@.claude/common/CLAUDE.md
## 프로젝트 지침
@.claude/project/overview.md
@.claude/project/conventions.md
@.claude/project/architecture.md

View File

@@ -0,0 +1,46 @@
# 아키텍처
> 이 파일은 `fe-common-rules/templates/project/architecture.md` 에서 복사된 양식입니다.
> 프로젝트의 레이어 구조와 데이터 흐름을 간단히 설명해주세요.
## 레이어 구조
<프로젝트의 레이어 구조를 그림 또는 텍스트로 그려주세요>
```
┌───────────────────────────────┐
│ presentation │ ← pages / components
├───────────────────────────────┤
│ logic │ ← composables / hooks / stores
├───────────────────────────────┤
│ data access │ ← api wrapper / queries
├───────────────────────────────┤
│ server │ ← 서버 라우트 / BFF
└───────────────────────────────┘
```
## 의존 규칙
- 상위 → 하위 **단방향 의존**만 허용
- 같은 레이어 간 순환 import 금지
- <프로젝트 고유 규칙 추가>
## 데이터 흐름
1. <이벤트 발생부터 응답까지의 흐름을 간단히>
2. ...
3. ...
## 상태 관리 가이드
| 상태 종류 | 권장 위치 |
| -------------------- | ------------------------ |
| 컴포넌트 로컬 상태 | <예: ref / useState> |
| 페이지 단위 공유 상태| <예: provide/inject> |
| 앱 전역 상태 | <예: Pinia / Zustand> |
| 서버 데이터 | <예: useFetch / TanStack Query> |
## 외부 의존성
- 반드시 알아야 할 외부 서비스나 내부 API 를 나열
- 장애 발생 시 fallback 정책이 있다면 함께 기술

View File

@@ -0,0 +1,44 @@
# 프로젝트 전용 컨벤션
> 이 파일은 `fe-common-rules/templates/project/conventions.md` 에서 복사된 양식입니다.
> 공통 지침(`.claude/common/`) 외에 **이 프로젝트에서만** 적용되는 규칙을 작성하세요.
> 공통 지침과 충돌할 경우 이 문서가 우선합니다.
## 디렉토리 규칙
- `components/`<설명>
- `composables/` 또는 `hooks/`<설명>
- `pages/` 또는 `app/`<설명>
- `server/` 또는 `api/`<설명>
- `types/`<설명>
## 컴포넌트 규칙 (공통 규칙 오버라이드)
<공통 규칙과 달리 프로젝트에서만 적용할 제약을 적어주세요>
- 예) 컴포넌트 파일 길이 제한: 150줄 (공통 200줄보다 엄격)
- 예) Props 개수 최대 7개, 초과 시 객체 props 로 묶기
## 스타일
- 색상/간격/타이포는 디자인 토큰만 사용하고 임의값 금지
- 다크모드 prefix: `dark:`
- 기타 프로젝트 고유 규칙: <작성>
## 네트워크 / 데이터
- API 호출 창구: <예: composables/api wrapper 사용>
- 인증 토큰 저장 위치: <예: httpOnly 쿠키>
- 에러 핸들링 규칙: <작성>
## 금지 사항
- <예: 직접 $fetch 사용 금지>
- <예: 전역 이벤트 버스 사용 금지>
- <예: any 타입 사용 금지>
## 테스트
- 테스트 러너: <Vitest / Jest >
- 테스트 파일 위치: <소스 / __tests__ 폴더>
- 최소 커버리지: <숫자>

View File

@@ -0,0 +1,39 @@
# 프로젝트 개요
> 이 파일은 `fe-common-rules/templates/project/overview.md` 에서 복사된 양식입니다.
> 프로젝트 세팅 후 실제 내용으로 채워주세요.
## 서비스
- **이름**: <프로젝트 이름>
- **설명**: < 설명>
- **배포 환경**: <dev / staging / production URL 또는 환경>
- **저장소**: <git 주소>
## 기술 스택
- **Framework**: <예: Nuxt 4 / Next 15 / ...>
- **UI**: <예: Vue 3 <script setup> / React 19 / ...>
- **Language**: TypeScript (strict)
- **Styling**: <예: Tailwind CSS / styled-components / ...>
- **상태관리**: <예: Pinia / Zustand / Redux Toolkit / ...>
- **테스트**: <예: Vitest + Playwright / Jest + RTL / ...>
- **패키지매니저**: <pnpm / npm / yarn>
## 주요 기능
- <기능 1>
- <기능 2>
- <기능 3>
## 팀 / 오너
- 오너: < 또는 담당자>
- 문의 채널: <Slack 채널 / 이메일>
- 온콜/긴급 연락: <필요 >
## 참고 문서
- 기획 문서: <링크>
- 디자인 시스템: <링크>
- API 스펙: <링크>