🔥 remove: 샘플 Nuxt 프로젝트 관련 파일 및 디렉토리 삭제
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
# 팀 공통 Claude 지침 (fe-common-rules)
|
||||
|
||||
이 파일은 팀 전체에서 공통으로 사용하는 Claude 작업 지침의 **엔트리 포인트**입니다.
|
||||
각 프로젝트의 `CLAUDE.md`에서 `@.claude/common/CLAUDE.md` 형태로 참조하여 사용합니다.
|
||||
|
||||
## 적용 범위
|
||||
|
||||
- 팀 내 모든 프론트엔드 프로젝트
|
||||
- Claude Code / Claude 기반 자동화 작업 전반
|
||||
|
||||
## 포함된 지침
|
||||
|
||||
아래 문서들은 Claude가 이 파일을 읽을 때 자동으로 함께 로드됩니다.
|
||||
|
||||
@rules/coding-conventions.md
|
||||
|
||||
@rules/framework-rules.md
|
||||
|
||||
@rules/commit-pr.md
|
||||
|
||||
@rules/claude-workflow.md
|
||||
|
||||
## 우선순위
|
||||
|
||||
1. 프로젝트 `CLAUDE.md`에 명시된 **프로젝트 지침**이 최우선입니다.
|
||||
2. 이 공통 지침은 프로젝트 지침과 충돌하지 않는 범위 내에서 항상 적용됩니다.
|
||||
3. 충돌이 발생하면 프로젝트 지침을 따르되, 차이를 사용자에게 알려주세요.
|
||||
|
||||
## 공통 Skill
|
||||
|
||||
공통 저장소는 `skills/` 폴더에 팀 공용 Claude skill 들을 함께 관리합니다.
|
||||
프로젝트는 `scripts/link-skills.sh` 로 `.claude/skills/` 에 심볼릭 링크하여 사용합니다.
|
||||
자세한 내용은 `skills/README.md` 를 참고하세요.
|
||||
|
||||
## 업데이트
|
||||
|
||||
- 공통 지침은 이 레파지토리(`fe-common-rules`)에서만 수정합니다.
|
||||
- 각 프로젝트는 `scripts/update.sh`(또는 `git submodule update --remote`)로 최신 버전을 받아갑니다.
|
||||
- 수정 제안은 PR로 받습니다. 자세한 내용은 루트 `README.md` 참고.
|
||||
@@ -1,62 +0,0 @@
|
||||
# Claude 작업 방식 지침
|
||||
|
||||
이 문서는 Claude가 팀 프로젝트에서 작업할 때 따라야 할 일반적인 원칙을 정의합니다.
|
||||
|
||||
## 기본 원칙
|
||||
|
||||
1. **기존 코드 존중**: 수정 전에 관련 파일과 주변 컨벤션을 먼저 파악합니다.
|
||||
2. **최소 변경**: 요구사항을 충족하는 최소한의 변경만 수행합니다. 관련 없는 리팩토링은 별도 작업으로 분리합니다.
|
||||
3. **가정 대신 질문**: 요구사항이 모호하면 추측하지 말고 사용자에게 확인합니다.
|
||||
4. **근거 있는 수정**: 코드 변경의 이유를 설명할 수 있어야 합니다.
|
||||
|
||||
## 작업 순서
|
||||
|
||||
1. **탐색 (Explore)**
|
||||
- 관련 파일을 먼저 읽고 프로젝트 구조를 파악합니다.
|
||||
- 유사한 패턴이 이미 존재하는지 확인합니다.
|
||||
|
||||
2. **계획 (Plan)**
|
||||
- 여러 파일을 수정하거나 복잡한 작업이면 할 일 목록을 만들어 공유합니다.
|
||||
- 아키텍처에 영향을 주는 변경은 착수 전에 사용자 승인을 받습니다.
|
||||
|
||||
3. **구현 (Implement)**
|
||||
- 한 번에 하나의 논리적 변경에 집중합니다.
|
||||
- 공통 지침과 프로젝트 지침을 모두 준수합니다.
|
||||
|
||||
4. **검증 (Verify)**
|
||||
- 린트 / 타입체크 / 빌드가 깨지지 않는지 확인합니다.
|
||||
- 테스트가 있는 프로젝트라면 관련 테스트를 실행합니다.
|
||||
- 수동 검증이 필요한 경우 확인 방법을 사용자에게 안내합니다.
|
||||
|
||||
## 해서는 안 되는 것
|
||||
|
||||
- **임의 기능 추가 금지**: 사용자가 요청하지 않은 기능을 추가하지 않습니다.
|
||||
- **기존 코드 대량 리팩토링 금지**: 요청 범위를 벗어나는 변경은 하지 않습니다.
|
||||
- **주석 / 문서 임의 삭제 금지**: 불필요해 보여도 삭제 전 사용자에게 확인합니다.
|
||||
- **비밀정보 출력 금지**: 환경변수, 키, 토큰 등은 코드에 하드코딩하지 않습니다.
|
||||
- **의존성 버전 임의 변경 금지**: 요청이 없다면 `package.json`의 버전을 변경하지 않습니다.
|
||||
- **강제 푸시 / 히스토리 재작성 금지**: `push --force`, `reset --hard` 등은 사용자의 명시적 요청 없이 실행하지 않습니다.
|
||||
|
||||
## 커뮤니케이션
|
||||
|
||||
- 답변은 간결하게, 결론을 먼저 말합니다.
|
||||
- 코드를 수정했다면 **어떤 파일을 어떻게 바꿨는지** 요약합니다.
|
||||
- 불확실한 부분은 솔직하게 밝히고 대안을 제시합니다.
|
||||
- 긴 설명보다 실제 결과물(코드/파일)을 우선합니다.
|
||||
|
||||
## 파일 작업 원칙
|
||||
|
||||
- 새 파일 생성보다 **기존 파일 수정**을 우선합니다.
|
||||
- README, 문서는 사용자가 명시적으로 요청했을 때만 생성합니다.
|
||||
- 파일을 읽지 않고 수정하지 않습니다.
|
||||
- 대량 변경 시에는 diff를 확인할 수 있도록 단계별로 진행합니다.
|
||||
|
||||
## 질문이 필요한 상황
|
||||
|
||||
다음의 경우 반드시 사용자에게 확인을 요청합니다.
|
||||
|
||||
- 요구사항의 일부가 불명확할 때
|
||||
- 여러 구현 방식이 있고 각각 장단점이 뚜렷할 때
|
||||
- 공통 지침과 프로젝트 지침이 충돌할 때
|
||||
- 파괴적 작업(파일 삭제, 데이터 마이그레이션, 스키마 변경 등)이 필요할 때
|
||||
- 외부 서비스 호출이나 결제 관련 작업일 때
|
||||
@@ -1,50 +0,0 @@
|
||||
# 코딩 컨벤션
|
||||
|
||||
## 기본 원칙
|
||||
|
||||
- **가독성 우선**: 영리한 코드보다 읽기 쉬운 코드를 선호합니다.
|
||||
- **일관성 유지**: 기존 코드의 스타일을 먼저 관찰하고 그에 맞춥니다.
|
||||
- **작은 단위**: 함수와 파일은 한 가지 책임만 지도록 작게 유지합니다.
|
||||
|
||||
## 포맷팅
|
||||
|
||||
- 들여쓰기: 스페이스 2칸 (탭 사용 금지)
|
||||
- 문자열: 싱글 쿼터(`'`) 사용, JSX/템플릿 속성값은 더블 쿼터(`"`)
|
||||
- 세미콜론: 생략하지 않고 항상 작성
|
||||
- 줄 끝 공백 제거, 파일 끝 개행 1줄 유지
|
||||
- 한 줄 최대 100자 (초과 시 줄바꿈)
|
||||
- Prettier 설정 파일(`.prettierrc`)이 있는 경우 해당 설정을 우선합니다.
|
||||
|
||||
## 네이밍
|
||||
|
||||
- **변수/함수**: `camelCase` (예: `userProfile`, `fetchUserData`)
|
||||
- **상수**: `UPPER_SNAKE_CASE` (예: `MAX_RETRY_COUNT`)
|
||||
- **컴포넌트/클래스/타입**: `PascalCase` (예: `UserCard`, `OrderStatus`)
|
||||
- **파일명**
|
||||
- Vue 컴포넌트: `PascalCase.vue` (예: `UserCard.vue`)
|
||||
- Composable: `use` 접두사 + `camelCase` (예: `useAuth.ts`)
|
||||
- 일반 TS 모듈: `kebab-case.ts` (예: `format-date.ts`)
|
||||
- **이벤트 핸들러**: `handle` 또는 `on` 접두사 (예: `handleClick`, `onSubmit`)
|
||||
- **불리언**: `is`, `has`, `can`, `should` 접두사 (예: `isLoading`, `hasError`)
|
||||
|
||||
## 타입
|
||||
|
||||
- `any` 사용 금지. 불가피할 경우 주석으로 이유를 남기고 `unknown`을 먼저 고려합니다.
|
||||
- 함수 시그니처에는 매개변수와 반환 타입을 명시합니다.
|
||||
- 공개 API(타 모듈에서 import 되는 것)는 반드시 타입을 export 합니다.
|
||||
- 유니온 타입은 `as const` 또는 별도 타입 alias로 관리합니다.
|
||||
|
||||
## 주석
|
||||
|
||||
- "무엇을" 보다 "왜"를 설명합니다.
|
||||
- TODO/FIXME 주석에는 작성자와 날짜 또는 이슈 번호를 포함합니다.
|
||||
- 공개 함수/컴포넌트에는 JSDoc 한 줄 설명을 권장합니다.
|
||||
|
||||
## import 순서
|
||||
|
||||
1. 외부 라이브러리 (예: `vue`, `nuxt`)
|
||||
2. 내부 절대 경로 (예: `~/components/...`)
|
||||
3. 상대 경로 (예: `./utils`)
|
||||
4. 타입 only import는 각 그룹 내에서 별도 블록으로 분리
|
||||
|
||||
그룹 사이에는 빈 줄을 한 줄 둡니다.
|
||||
@@ -1,83 +0,0 @@
|
||||
# 커밋 / PR 규칙
|
||||
|
||||
## 커밋 메시지
|
||||
|
||||
[Conventional Commits](https://www.conventionalcommits.org/)를 따릅니다.
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### type
|
||||
|
||||
- `feat`: 새로운 기능 추가
|
||||
- `fix`: 버그 수정
|
||||
- `refactor`: 기능 변화 없는 구조 개선
|
||||
- `style`: 코드 포맷/세미콜론 등 스타일 변경
|
||||
- `docs`: 문서 수정
|
||||
- `test`: 테스트 추가/수정
|
||||
- `chore`: 빌드, 설정, 패키지 업데이트 등
|
||||
- `perf`: 성능 개선
|
||||
- `ci`: CI 설정 변경
|
||||
|
||||
### 작성 규칙
|
||||
|
||||
- **subject**는 50자 이내, 명령형 현재 시제(예: `add`, `fix` — `added`, `fixes` 아님)
|
||||
- subject 끝에 마침표를 찍지 않습니다.
|
||||
- body는 "무엇을"보다 "왜"를 설명합니다. 한 줄 72자 이내로 줄바꿈합니다.
|
||||
- 한 커밋에는 하나의 논리적 변경만 담습니다.
|
||||
|
||||
### 예시
|
||||
|
||||
```
|
||||
feat(user): add profile image upload
|
||||
|
||||
프로필 이미지 업로드 요구사항에 따라 multipart 업로드 경로를 추가했습니다.
|
||||
기존 텍스트 필드 업데이트 API는 변경하지 않았습니다.
|
||||
|
||||
Refs: #123
|
||||
```
|
||||
|
||||
## Pull Request
|
||||
|
||||
### 제목
|
||||
|
||||
커밋 메시지와 동일한 컨벤션을 따릅니다. (`<type>(<scope>): <subject>`)
|
||||
|
||||
### 본문 템플릿
|
||||
|
||||
```markdown
|
||||
## 변경 사항
|
||||
- 무엇이 바뀌었는지 요약
|
||||
|
||||
## 배경 / 이유
|
||||
- 왜 이 변경이 필요했는지
|
||||
|
||||
## 테스트
|
||||
- 어떻게 검증했는지 (수동/자동 테스트 내용)
|
||||
|
||||
## 스크린샷 (UI 변경 시)
|
||||
- Before / After
|
||||
|
||||
## 체크리스트
|
||||
- [ ] 로컬에서 빌드/테스트 통과
|
||||
- [ ] 린트/포맷 통과
|
||||
- [ ] 공통 지침(fe-common-rules) 준수
|
||||
- [ ] 관련 문서 업데이트
|
||||
```
|
||||
|
||||
### 리뷰 기준
|
||||
|
||||
- 최소 1명 이상의 승인 필요
|
||||
- CI(Lint / Test / Build) 전부 통과 필요
|
||||
- 머지 전략은 **Squash and merge**를 기본으로 합니다.
|
||||
- 리뷰어는 변경 범위에 대해 질문이 남지 않도록 배경을 충분히 이해한 뒤 승인합니다.
|
||||
|
||||
### Draft PR
|
||||
|
||||
- 작업 중간 중간 피드백이 필요한 경우 Draft로 먼저 올리는 것을 권장합니다.
|
||||
- Draft 상태에서는 CI 실패가 있어도 괜찮습니다.
|
||||
@@ -1,47 +0,0 @@
|
||||
# 프레임워크 / 라이브러리 사용 규칙
|
||||
|
||||
## Vue 3 / Nuxt
|
||||
|
||||
### 컴포넌트 작성
|
||||
|
||||
- **`<script setup lang="ts">` 사용**을 기본으로 합니다. Options API는 신규 코드에서 사용하지 않습니다.
|
||||
- 컴포넌트는 단일 책임 원칙을 지키며, 200줄을 넘으면 분리를 검토합니다.
|
||||
- Props는 `defineProps<T>()` 제네릭 형태로 타입을 명시합니다.
|
||||
- Emits는 `defineEmits<{ ... }>()` 제네릭 형태로 선언합니다.
|
||||
- `ref` vs `reactive`: 원시값과 단일 객체는 `ref`, 복잡한 상태 트리는 `reactive`를 고려합니다. 일관성을 위해 팀 내에서 가능한 `ref`를 우선합니다.
|
||||
|
||||
### 상태 관리
|
||||
|
||||
- 컴포넌트 내 로컬 상태: `ref` / `reactive`
|
||||
- 여러 컴포넌트가 공유하는 상태: **Pinia** 사용
|
||||
- 서버 상태: Nuxt `useFetch` / `useAsyncData` 사용, 직접 `fetch` 호출은 지양합니다.
|
||||
|
||||
### 라우팅
|
||||
|
||||
- Nuxt의 파일 기반 라우팅을 사용합니다. 수동 라우트 정의는 특수한 경우에만 허용됩니다.
|
||||
- 동적 라우트 파라미터는 `[param].vue` 형식을 사용합니다.
|
||||
|
||||
### Composable
|
||||
|
||||
- 재사용 가능한 로직은 `composables/` 디렉토리의 `useXxx` 함수로 추출합니다.
|
||||
- Composable은 부수효과를 최소화하고, 반환 객체에 상태와 메서드를 함께 묶어 반환합니다.
|
||||
|
||||
## TypeScript
|
||||
|
||||
- `strict: true`를 유지합니다.
|
||||
- 공용 타입은 `types/` 또는 각 도메인의 `types.ts`에 모아둡니다.
|
||||
- 외부 API 응답은 반드시 타입을 정의하여 사용합니다.
|
||||
|
||||
## Tailwind CSS
|
||||
|
||||
- **유틸리티 클래스 우선** 사용. 공통 패턴은 컴포넌트로 추출합니다.
|
||||
- `@apply`는 꼭 필요한 경우에만 사용하고, 가능한 유틸리티를 직접 나열합니다.
|
||||
- 임의값 클래스(`w-[123px]`)는 디자인 시스템에 등록되지 않은 값에만 제한적으로 사용합니다.
|
||||
- 조건부 클래스는 `clsx` 또는 `cn` 유틸리티를 사용하여 가독성을 확보합니다.
|
||||
- 클래스 순서는 Tailwind 공식 프리셋(`prettier-plugin-tailwindcss`)을 따릅니다.
|
||||
|
||||
## 외부 라이브러리 도입
|
||||
|
||||
- 새로운 라이브러리를 추가할 때는 **PR 설명에 도입 이유, 번들 영향, 대안 검토 내용**을 기록합니다.
|
||||
- 동일 기능의 라이브러리를 중복 도입하지 않습니다.
|
||||
- 유지보수가 중단된 패키지(6개월 이상 업데이트 없음)는 도입하지 않습니다.
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# fe-common-rules installer
|
||||
# 현재 Git 프로젝트의 .claude/common 경로에 fe-common-rules 저장소를
|
||||
# submodule 로 추가하고, templates/ 에서 프로젝트 지침 양식과
|
||||
# CLAUDE.md 템플릿을 복사합니다.
|
||||
#
|
||||
# 사용법:
|
||||
# bash scripts/install.sh <repo-url> [<branch>]
|
||||
#
|
||||
# 예:
|
||||
# bash scripts/install.sh git@github.com:our-team/fe-common-rules.git main
|
||||
#
|
||||
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
|
||||
echo " 예: bash scripts/install.sh git@github.com:our-team/fe-common-rules.git main" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Git 프로젝트인지 확인
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "❌ 현재 디렉토리는 Git 저장소가 아닙니다. 먼저 'git init' 후 실행해주세요." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 루트로 이동
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
# 1) Submodule 추가
|
||||
if [[ -d "$TARGET_PATH" ]]; then
|
||||
echo "⚠️ '$TARGET_PATH' 경로가 이미 존재합니다. submodule 추가를 건너뜁니다."
|
||||
else
|
||||
echo "📦 fe-common-rules 를 submodule 로 추가합니다..."
|
||||
git submodule add -b "$BRANCH" "$REPO_URL" "$TARGET_PATH"
|
||||
git submodule update --init --recursive
|
||||
echo "✅ submodule 추가 완료: $TARGET_PATH"
|
||||
fi
|
||||
|
||||
# 2) 프로젝트 지침 양식 복사 (templates/project/ → .claude/project/)
|
||||
mkdir -p "$PROJECT_PATH"
|
||||
|
||||
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
|
||||
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'
|
||||
# <프로젝트 이름>
|
||||
|
||||
## 공통 지침
|
||||
@.claude/common/CLAUDE.md
|
||||
|
||||
## 프로젝트 지침
|
||||
@.claude/project/overview.md
|
||||
@.claude/project/conventions.md
|
||||
@.claude/project/architecture.md
|
||||
EOF
|
||||
echo "✅ CLAUDE.md 템플릿을 생성했습니다. (fallback)"
|
||||
fi
|
||||
else
|
||||
echo "ℹ️ 기존 CLAUDE.md 가 이미 존재합니다. 아래 블록을 수동으로 추가하세요:"
|
||||
echo ""
|
||||
echo " ## 공통 지침"
|
||||
echo " @.claude/common/CLAUDE.md"
|
||||
echo ""
|
||||
echo " ## 프로젝트 지침"
|
||||
echo " @.claude/project/overview.md"
|
||||
echo " @.claude/project/conventions.md"
|
||||
echo " @.claude/project/architecture.md"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# 4) 공통 skill 심볼릭 링크 (.claude/common/skills/* → .claude/skills/*)
|
||||
SKILLS_SRC="$TARGET_PATH/skills"
|
||||
SKILLS_DEST=".claude/skills"
|
||||
|
||||
if [[ -d "$SKILLS_SRC" ]]; then
|
||||
echo "🔗 공통 skill 을 $SKILLS_DEST 로 링크합니다..."
|
||||
mkdir -p "$SKILLS_DEST"
|
||||
for dir in "$SKILLS_SRC"/*/; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="$(basename "$dir")"
|
||||
[[ "$name" == "README"* ]] && continue
|
||||
link_src="../common/skills/$name"
|
||||
link_dest="$SKILLS_DEST/$name"
|
||||
if [[ -L "$link_dest" ]]; then
|
||||
echo " ⏭ $link_dest (이미 링크됨)"
|
||||
elif [[ -e "$link_dest" ]]; then
|
||||
echo " ⚠️ $link_dest (실제 파일/폴더 존재 - 건너뜀. link-skills.sh --force 로 덮어쓰기)"
|
||||
else
|
||||
ln -s "$link_src" "$link_dest"
|
||||
echo " ✅ $link_dest → $link_src"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 설치가 완료되었습니다."
|
||||
echo " - 공통 지침: $TARGET_PATH/CLAUDE.md"
|
||||
echo " - 프로젝트 지침: $PROJECT_PATH/"
|
||||
echo " - 공통 skill: $SKILLS_DEST/ (submodule 에 심볼릭 링크)"
|
||||
echo " - 엔트리 파일: CLAUDE.md"
|
||||
echo ""
|
||||
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'"
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# fe-common-rules skill linker
|
||||
# 공통 저장소의 skills/* 를 프로젝트의 .claude/skills/* 로 심볼릭 링크합니다.
|
||||
# 심볼릭 링크이므로 submodule 업데이트 시 skill 도 자동으로 최신 버전이 됩니다.
|
||||
#
|
||||
# 사용법:
|
||||
# bash .claude/common/scripts/link-skills.sh # 모든 skill 링크
|
||||
# bash .claude/common/scripts/link-skills.sh <skill-name> # 특정 skill 만
|
||||
# bash .claude/common/scripts/link-skills.sh --dry-run # 실제 링크 없이 미리보기
|
||||
# bash .claude/common/scripts/link-skills.sh --force # 기존 링크/폴더 덮어쓰기
|
||||
# bash .claude/common/scripts/link-skills.sh --unlink # 공통 skill 링크 제거
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
COMMON_PATH=".claude/common"
|
||||
SKILLS_SRC="$COMMON_PATH/skills"
|
||||
SKILLS_DEST=".claude/skills"
|
||||
|
||||
MODE="safe" # safe | force | dry-run | unlink
|
||||
TARGET=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) MODE="dry-run" ;;
|
||||
--force) MODE="force" ;;
|
||||
--unlink) MODE="unlink" ;;
|
||||
-h|--help)
|
||||
grep '^#' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
echo "❌ 알 수 없는 옵션: $arg" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
TARGET="$arg"
|
||||
;;
|
||||
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 "$SKILLS_SRC" ]]; then
|
||||
echo "❌ $SKILLS_SRC 가 없습니다. 먼저 submodule 을 설치/업데이트하세요:" >&2
|
||||
echo " git submodule update --init --recursive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$SKILLS_DEST"
|
||||
|
||||
# 링크 대상 결정
|
||||
declare -a skills
|
||||
if [[ -n "$TARGET" ]]; then
|
||||
if [[ ! -d "$SKILLS_SRC/$TARGET" ]]; then
|
||||
echo "❌ '$TARGET' skill 을 $SKILLS_SRC 에서 찾지 못했습니다." >&2
|
||||
echo " 사용 가능한 skill:" >&2
|
||||
ls -1 "$SKILLS_SRC" 2>/dev/null | grep -v '^README' | sed 's/^/ - /' >&2
|
||||
exit 1
|
||||
fi
|
||||
skills=("$TARGET")
|
||||
else
|
||||
shopt -s nullglob
|
||||
for dir in "$SKILLS_SRC"/*/; do
|
||||
name="$(basename "$dir")"
|
||||
# README 같은 파일은 이미 걸러지지만 추가 보호
|
||||
[[ "$name" == "README"* ]] && continue
|
||||
skills+=("$name")
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${#skills[@]} -eq 0 ]]; then
|
||||
echo "ℹ️ 링크할 skill 이 없습니다."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "🔗 공통 skill 링크 (mode=$MODE)"
|
||||
echo " source: $SKILLS_SRC"
|
||||
echo " dest: $SKILLS_DEST"
|
||||
echo ""
|
||||
|
||||
linked=0
|
||||
skipped=0
|
||||
removed=0
|
||||
|
||||
for name in "${skills[@]}"; do
|
||||
src="../common/skills/$name" # 심볼릭 링크의 상대 경로 (.claude/skills 기준)
|
||||
dest="$SKILLS_DEST/$name"
|
||||
|
||||
case "$MODE" in
|
||||
unlink)
|
||||
if [[ -L "$dest" ]]; then
|
||||
rm "$dest"
|
||||
echo " 🗑 $dest (링크 제거)"
|
||||
removed=$((removed + 1))
|
||||
else
|
||||
echo " ⏭ $dest (링크 아님 - 건너뜀)"
|
||||
fi
|
||||
;;
|
||||
dry-run)
|
||||
if [[ -e "$dest" || -L "$dest" ]]; then
|
||||
echo " ⏭ $dest (이미 존재)"
|
||||
else
|
||||
echo " ➕ ln -s $src $dest"
|
||||
fi
|
||||
;;
|
||||
force)
|
||||
rm -rf "$dest"
|
||||
ln -s "$src" "$dest"
|
||||
echo " ✅ $dest → $src (덮어씀)"
|
||||
linked=$((linked + 1))
|
||||
;;
|
||||
safe)
|
||||
if [[ -L "$dest" ]]; then
|
||||
echo " ⏭ $dest (이미 링크됨)"
|
||||
skipped=$((skipped + 1))
|
||||
elif [[ -e "$dest" ]]; then
|
||||
echo " ⚠️ $dest (실제 파일/폴더가 존재. --force 로 덮어쓰세요)"
|
||||
skipped=$((skipped + 1))
|
||||
else
|
||||
ln -s "$src" "$dest"
|
||||
echo " ✅ $dest → $src"
|
||||
linked=$((linked + 1))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
case "$MODE" in
|
||||
unlink) echo "🎉 제거 완료: $removed 개 링크 제거됨";;
|
||||
dry-run) echo "🔍 미리보기 완료 (실제 변경 없음)";;
|
||||
*) echo "🎉 완료: 링크 $linked개 / 건너뜀 $skipped개";;
|
||||
esac
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# fe-common-rules updater
|
||||
# 현재 프로젝트에 설치된 .claude/common submodule 을 최신 버전으로 갱신합니다.
|
||||
#
|
||||
# 사용법:
|
||||
# bash .claude/common/scripts/update.sh
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_PATH=".claude/common"
|
||||
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "❌ 현재 디렉토리는 Git 저장소가 아닙니다." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$TARGET_PATH" ]]; then
|
||||
echo "❌ '$TARGET_PATH' 가 존재하지 않습니다. 먼저 install.sh 로 설치하세요." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔄 fe-common-rules 를 최신 버전으로 업데이트합니다..."
|
||||
git submodule update --remote --merge "$TARGET_PATH"
|
||||
|
||||
# 변경 사항 확인
|
||||
if git diff --quiet -- "$TARGET_PATH"; then
|
||||
echo "✅ 이미 최신 상태입니다."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✅ 업데이트가 완료되었습니다. 변경된 submodule 포인터를 커밋하세요:"
|
||||
echo ""
|
||||
echo " git add $TARGET_PATH"
|
||||
echo " git commit -m 'chore: update fe-common-rules submodule'"
|
||||
@@ -1,52 +0,0 @@
|
||||
# Common Skills
|
||||
|
||||
이 폴더는 팀 공통으로 사용할 Claude **skill** 들을 모아두는 공간입니다.
|
||||
각 프로젝트는 submodule 로 이 저장소를 가져온 뒤, `.claude/common/skills/*` 를
|
||||
`.claude/skills/*` 로 **심볼릭 링크**하여 사용합니다.
|
||||
|
||||
## Skill 구조
|
||||
|
||||
각 skill 은 하나의 폴더이며, 루트에 `SKILL.md` 를 가집니다.
|
||||
|
||||
```
|
||||
skills/
|
||||
└── <skill-name>/
|
||||
├── SKILL.md # YAML frontmatter (name, description) + 지시문
|
||||
└── (선택) 추가 자료 — 예시 파일, 템플릿, 스크립트 등
|
||||
```
|
||||
|
||||
`SKILL.md` 예시:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: <skill-name>
|
||||
description: <Claude 가 이 skill 을 언제 써야 하는지 한 줄로>
|
||||
---
|
||||
|
||||
# <스킬 제목>
|
||||
|
||||
## 언제 사용하는가
|
||||
...
|
||||
|
||||
## 작업 순서
|
||||
...
|
||||
```
|
||||
|
||||
## 프로젝트에 연결하기
|
||||
|
||||
```bash
|
||||
# 공통 저장소의 skill 을 .claude/skills/ 로 전부 링크
|
||||
bash .claude/common/scripts/link-skills.sh
|
||||
|
||||
# 차이 확인
|
||||
bash .claude/common/scripts/link-skills.sh --dry-run
|
||||
|
||||
# 특정 skill 만 링크
|
||||
bash .claude/common/scripts/link-skills.sh vue-component-review
|
||||
```
|
||||
|
||||
## 새 skill 추가 프로세스
|
||||
|
||||
1. 이 저장소에서 `skills/<skill-name>/SKILL.md` 를 작성
|
||||
2. PR 을 올리고 팀 리뷰
|
||||
3. 머지 후 각 프로젝트에서 `git submodule update --remote` → `link-skills.sh` 실행
|
||||
@@ -1,78 +0,0 @@
|
||||
---
|
||||
name: conventional-commit
|
||||
description: Git 변경사항을 팀의 Conventional Commits 규칙(fe-common-rules/rules/commit-pr.md)에 맞춰 커밋 메시지로 작성할 때 사용합니다. 사용자가 "커밋 메시지 만들어줘", "commit", "커밋해줘" 등을 요청하면 트리거됩니다.
|
||||
---
|
||||
|
||||
# Conventional Commit 작성
|
||||
|
||||
이 skill 은 `git diff --staged` / `git status` 결과를 바탕으로 팀 규칙에 맞는 커밋
|
||||
메시지를 작성합니다.
|
||||
|
||||
## 팀 규칙 요약
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### type
|
||||
|
||||
- `feat`: 새로운 기능
|
||||
- `fix`: 버그 수정
|
||||
- `refactor`: 기능 변화 없는 구조 개선
|
||||
- `style`: 포맷/세미콜론 등 스타일
|
||||
- `docs`: 문서
|
||||
- `test`: 테스트 추가/수정
|
||||
- `chore`: 빌드, 설정, 패키지
|
||||
- `perf`: 성능 개선
|
||||
- `ci`: CI 설정
|
||||
|
||||
### subject
|
||||
|
||||
- 50자 이내
|
||||
- 명령형 현재 시제 (`add`, `fix` — `added`/`fixes` 아님)
|
||||
- 끝에 마침표 금지
|
||||
- 영어 소문자 시작
|
||||
|
||||
### body
|
||||
|
||||
- "무엇을"보다 **"왜"** 를 설명
|
||||
- 한 줄 72자 이내
|
||||
- 한 커밋 = 한 논리적 변경
|
||||
|
||||
## 작업 순서
|
||||
|
||||
1. **상태 확인**
|
||||
- `git status` 로 staged/unstaged 파일 파악
|
||||
- staged 변경이 없으면 사용자에게 먼저 `git add` 를 하라고 안내
|
||||
2. **변경 분석**
|
||||
- `git diff --staged` 로 실제 변경 내용 확인
|
||||
- 변경을 한 문장으로 요약 (type + scope + subject 를 정하는 근거)
|
||||
3. **메시지 작성**
|
||||
- subject 는 50자 이내, 영문 명령형
|
||||
- body 는 한국어로 "왜" 중심 설명 (한 줄 72자 제한)
|
||||
- 이슈 번호가 있으면 `Refs: #123` footer 추가
|
||||
4. **사용자 확인**
|
||||
- 작성한 메시지를 보여주고 커밋 실행 여부를 물음
|
||||
- 사용자가 명시적으로 "커밋해" 라고 할 때만 실제 `git commit` 실행
|
||||
|
||||
## 출력 예시
|
||||
|
||||
```
|
||||
feat(user): add profile image upload
|
||||
|
||||
프로필 이미지 업로드 요구사항에 따라 multipart 업로드 경로를 추가했습니다.
|
||||
기존 텍스트 필드 업데이트 API 는 변경하지 않았습니다.
|
||||
|
||||
Refs: #123
|
||||
```
|
||||
|
||||
## 주의사항
|
||||
|
||||
- **논리적으로 섞인 변경**이 감지되면(예: feat + refactor 가 같이 있음) 커밋 분리를 제안한다.
|
||||
- `package.json` / lock 파일이 함께 staged 되어 있으면 의존성 추가 사유를 body 에 명시한다.
|
||||
- 사용자가 커밋 실행을 명시적으로 허락하기 전까지 `git commit` 명령을 직접 실행하지 않는다.
|
||||
- Co-authored-by 등 footer 는 사용자가 요청할 때만 추가한다.
|
||||
@@ -1,310 +0,0 @@
|
||||
---
|
||||
name: edm-email-html
|
||||
description: |
|
||||
EDM(이메일 다이렉트 마케팅) HTML을 구현하는 전체 워크플로우 스킬.
|
||||
Figma 디자인 → HTML table 마크업 → 아웃룩 호환 → 검수까지 단계별 가이드를 제공합니다.
|
||||
|
||||
다음 상황에서 반드시 사용하세요:
|
||||
- "EDM 만들어줘", "이메일 템플릿 구현", "뉴스레터 HTML"
|
||||
- "아웃룩에서 깨지는 이메일 수정", "이메일 HTML 마크업"
|
||||
- Figma 디자인을 받고 이메일 HTML로 변환할 때
|
||||
- "메일 발송용 HTML", "eDM 퍼블리싱", "HTML 이메일"
|
||||
- 이메일 클라이언트 호환성 문제가 있을 때
|
||||
---
|
||||
|
||||
# EDM HTML 구현 가이드
|
||||
|
||||
이메일 HTML은 일반 웹과 다른 세계입니다. 2000년대 테이블 코딩이 아직도 정답이며, Flexbox와 Grid는 쓸 수 없습니다. 이 스킬은 Figma 디자인에서 시작해 모든 이메일 클라이언트에서 깨지지 않는 HTML을 만드는 과정을 안내합니다.
|
||||
|
||||
## 워크플로우
|
||||
|
||||
```
|
||||
1. Figma 디자인 파악 → 2. HTML 마크업 → 3. 아웃룩 호환 → 4. 검수
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Figma 디자인 파악
|
||||
|
||||
### Figma MCP 사용 가능 시
|
||||
Claude Code에 Figma MCP가 설정되어 있다면 Figma URL로 직접 디자인 데이터를 읽을 수 있습니다. MCP가 연결되어 있는지 먼저 확인하고, 가능하다면 자동 추출을 시도하세요.
|
||||
|
||||
추출 가능한 속성:
|
||||
- 컬러 HEX값 (RGBA → HEX 자동 변환)
|
||||
- 폰트 패밀리, 사이즈(px), 굵기, 줄간격
|
||||
- 레이아웃 치수: 너비, 높이, padding, 섹션 간격
|
||||
- 이미지 에셋 URL (CDN 업로드 필요)
|
||||
- CTA 링크 (레이어 설명 필드에서 추출)
|
||||
|
||||
### Figma MCP 없이 진행 시
|
||||
사용자에게 다음 정보를 요청하거나 스크린샷으로 파악하세요.
|
||||
|
||||
**필수 확인 항목:**
|
||||
- 전체 이메일 너비 (권장: **600px**)
|
||||
- 각 섹션 배경색, 텍스트 색상 (HEX)
|
||||
- 폰트: 패밀리, 사이즈(px), 굵기, 줄간격
|
||||
- 이미지: 가로×세로(px)
|
||||
- 여백: 섹션 간 간격, 좌우 패딩
|
||||
- CTA 버튼: 크기, 색상, 텍스트, 링크 URL
|
||||
- 푸터: 회사 정보, 수신거부 링크
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: HTML table 마크업
|
||||
|
||||
### 절대 원칙
|
||||
|
||||
이메일 HTML에서 반드시 지켜야 하는 규칙들입니다. 이 규칙을 어기면 특정 클라이언트에서 레이아웃이 무너집니다:
|
||||
|
||||
| 규칙 | 이유 |
|
||||
|------|------|
|
||||
| `table`, `tr`, `td`만 레이아웃에 사용 | div는 Outlook 등에서 무시됨 |
|
||||
| inline CSS 우선 | Gmail이 `<head>` style 태그를 제거함 |
|
||||
| `width`/`height` 속성 필수 | CSS만으론 Outlook이 무시함 |
|
||||
| `margin` 사용 금지 | 빈 `<tr>`행이나 `padding`으로 대체 |
|
||||
| `padding` 개별 속성 사용 | 단축 속성(`padding: 10px 20px`)은 일부 클라이언트 미지원 |
|
||||
| 모든 `<table>`에 `cellpadding="0" cellspacing="0" border="0"` | 브라우저 기본 스타일 초기화 |
|
||||
|
||||
### 기본 템플릿
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="format-detection" content="telephone=no, date=no, address=no">
|
||||
<title>이메일 제목</title>
|
||||
<style type="text/css">
|
||||
body { margin: 0; padding: 0; width: 100%; background-color: #f5f5f5; }
|
||||
table { border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||||
img { display: block; border: 0; outline: none; text-decoration: none; }
|
||||
|
||||
/* 미디어쿼리는 여기서만 (Outlook은 무시하지만 Gmail/Apple Mail에서 적용) */
|
||||
@media only screen and (max-width: 600px) {
|
||||
.mobile-full { width: 100% !important; display: block !important; }
|
||||
.mobile-padding { padding-left: 16px !important; padding-right: 16px !important; }
|
||||
.mobile-center { text-align: center !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f5f5f5;">
|
||||
|
||||
<!-- 외부 래퍼: 배경색과 수평 중앙 정렬 -->
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f5f5f5">
|
||||
<tr>
|
||||
<td align="center" style="padding-top: 20px; padding-bottom: 20px;">
|
||||
|
||||
<!-- 600px 컨테이너 -->
|
||||
<table width="600" cellpadding="0" cellspacing="0" border="0"
|
||||
style="width: 600px; max-width: 100%; background-color: #ffffff;">
|
||||
|
||||
<!-- 헤더 -->
|
||||
<tr>
|
||||
<td style="padding-top: 0;">
|
||||
<!-- 로고 이미지 등 -->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 본문 -->
|
||||
<tr>
|
||||
<td style="padding-top: 30px; padding-bottom: 30px;
|
||||
padding-left: 30px; padding-right: 30px;">
|
||||
<!-- 메인 콘텐츠 -->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 푸터 -->
|
||||
<tr>
|
||||
<td bgcolor="#f5f5f5"
|
||||
style="background-color: #f5f5f5;
|
||||
padding-top: 20px; padding-bottom: 20px;
|
||||
padding-left: 20px; padding-right: 20px;
|
||||
text-align: center;">
|
||||
<!-- 회사 정보 + 수신거부 링크 (필수) -->
|
||||
<p style="font-family: Arial, sans-serif; font-size: 12px;
|
||||
color: #999999; margin: 0; line-height: 1.5;">
|
||||
회사명 | 주소<br>
|
||||
<a href="[수신거부URL]"
|
||||
style="color: #999999; text-decoration: underline;">
|
||||
수신거부
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### 안전한 폰트
|
||||
|
||||
웹폰트(`@font-face`, Google Fonts)는 대부분의 이메일 클라이언트에서 지원하지 않습니다. Pretendard, Noto Sans KR 같은 폰트를 Figma에서 사용했어도 이메일에서는 안전 폰트로 대체해야 합니다.
|
||||
|
||||
```css
|
||||
/* 권장 스택 (한국어 이메일) */
|
||||
font-family: -apple-system, Arial, 'Helvetica Neue', Helvetica, sans-serif;
|
||||
|
||||
/* Outlook 전용 (MSO 조건부 주석 내) */
|
||||
font-family: Arial, sans-serif;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 아웃룩 호환성
|
||||
|
||||
아웃룩 2007~2019는 Word 엔진으로 이메일을 렌더링해서 현대 CSS를 거의 무시합니다. MSO 조건부 주석으로 아웃룩과 그 외 클라이언트를 분리해서 처리하세요.
|
||||
|
||||
### MSO 조건부 주석
|
||||
|
||||
```html
|
||||
<!--[if mso]>
|
||||
<!-- 아웃룩에서만 렌더링 -->
|
||||
<![endif]-->
|
||||
|
||||
<!--[if !mso]><!-->
|
||||
<!-- 아웃룩 제외 클라이언트에서 렌더링 -->
|
||||
<!--<![endif]-->
|
||||
```
|
||||
|
||||
### 아웃룩이 무시하는 주요 속성
|
||||
|
||||
| CSS 속성 | 아웃룩 동작 | 대체 방법 |
|
||||
|----------|-----------|---------|
|
||||
| `background-image` | 미지원 | `<img>` 태그 직접 사용 |
|
||||
| `border-radius` | 무시 | VML 사용 또는 이미지 버튼 |
|
||||
| `margin` | 무시 | `padding` 또는 빈 `<tr>` 행 |
|
||||
| `box-shadow` | 무시 | 포기 또는 이미지로 대체 |
|
||||
| `@media query` | 2007/2010 미지원 | 테이블 고정폭으로 데스크톱 설계 |
|
||||
|
||||
### VML 버튼 (반드시 사용)
|
||||
|
||||
아웃룩에서 CSS 버튼은 배경색 없는 텍스트 링크로 표시됩니다. CTA 버튼은 항상 VML을 포함하세요:
|
||||
|
||||
```html
|
||||
<div style="text-align: center;
|
||||
padding-top: 20px; padding-bottom: 20px;">
|
||||
|
||||
<!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:w="urn:schemas-microsoft-com:office:word"
|
||||
href="https://example.com"
|
||||
style="height: 44px; v-text-anchor: middle; width: 200px;"
|
||||
arcsize="5%"
|
||||
stroke="f"
|
||||
fillcolor="#FF6B6B">
|
||||
<w:anchorlock/>
|
||||
<center style="color: #ffffff; font-family: Arial, sans-serif;
|
||||
font-size: 16px; font-weight: bold;">
|
||||
지금 확인하기
|
||||
</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
|
||||
<!--[if !mso]><!-->
|
||||
<a href="https://example.com"
|
||||
style="background-color: #FF6B6B;
|
||||
color: #ffffff;
|
||||
display: inline-block;
|
||||
padding-top: 12px; padding-bottom: 12px;
|
||||
padding-left: 30px; padding-right: 30px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: bold;">
|
||||
지금 확인하기
|
||||
</a>
|
||||
<!--<![endif]-->
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
### 이미지 처리
|
||||
|
||||
이미지 차단 시에도 레이아웃이 깨지지 않도록 `alt` 텍스트와 배경색을 함께 지정하세요:
|
||||
|
||||
```html
|
||||
<td bgcolor="#FF6B6B" style="background-color: #FF6B6B;">
|
||||
<img src="https://cdn.example.com/banner.jpg"
|
||||
alt="7월 여름 세일 최대 50% 할인"
|
||||
width="600"
|
||||
height="300"
|
||||
style="display: block; width: 100%; max-width: 600px;
|
||||
height: auto; border: 0;">
|
||||
</td>
|
||||
```
|
||||
|
||||
이미지는 반드시 `https://` CDN 절대 경로를 사용하세요. 로컬 경로나 상대 경로는 이메일에서 작동하지 않습니다.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 검수 체크리스트
|
||||
|
||||
### 코드 구조 (필수)
|
||||
- [ ] 모든 `<table>`에 `cellpadding="0" cellspacing="0" border="0"`
|
||||
- [ ] 모든 `<img>`에 `width`, `height`, `alt` 속성
|
||||
- [ ] `margin` 미사용 (padding 또는 빈 `<tr>` 행으로 대체)
|
||||
- [ ] `padding` 단축 속성 제거 (개별 속성 사용)
|
||||
- [ ] CTA 버튼에 VML 코드 포함
|
||||
- [ ] 이미지 `src`가 HTTPS 절대 URL
|
||||
|
||||
### 콘텐츠 (필수)
|
||||
- [ ] 푸터에 수신거부 링크 포함
|
||||
- [ ] 모든 링크 href 유효성 확인
|
||||
- [ ] 이미지 alt 텍스트 의미있게 작성 (장식용이면 `alt=""`)
|
||||
|
||||
### Figma 디자인 대비 검수
|
||||
- [ ] 전체 너비 600px
|
||||
- [ ] 색상 HEX값 일치
|
||||
- [ ] 폰트 사이즈, 굵기 일치
|
||||
- [ ] 버튼 크기, 색상 일치
|
||||
- [ ] 섹션 간 여백 일치
|
||||
|
||||
### 테스트 도구
|
||||
|
||||
| 도구 | 용도 | 비용 |
|
||||
|------|------|------|
|
||||
| [Litmus](https://www.litmus.com) | 100+ 클라이언트 렌더링 미리보기 | 유료 |
|
||||
| [Email on Acid](https://www.emailonacid.com) | 크로스 클라이언트 + 접근성 감사 | 유료 |
|
||||
| [Mailtrap](https://mailtrap.io) | 개발 환경 샌드박스, 스팸 점수 | 무료 플랜 |
|
||||
| [SpamTest.io](https://spamtest.io/) | 스팸 점수, SPF/DKIM/DMARC 확인 | 무료 |
|
||||
|
||||
**최소 테스트 클라이언트:** Gmail 웹, Outlook (Windows), Apple Mail, 모바일 Gmail
|
||||
|
||||
---
|
||||
|
||||
## 2컬럼 레이아웃 예시
|
||||
|
||||
```html
|
||||
<!-- 데스크톱: 2열 | 모바일: 스택 -->
|
||||
<table width="600" cellpadding="0" cellspacing="0" border="0"
|
||||
style="width: 600px;">
|
||||
<tr>
|
||||
<td width="280" valign="top"
|
||||
style="width: 280px; padding-right: 20px;"
|
||||
class="mobile-full">
|
||||
<!-- 왼쪽 -->
|
||||
</td>
|
||||
<td width="280" valign="top"
|
||||
style="width: 280px; padding-left: 20px;"
|
||||
class="mobile-full">
|
||||
<!-- 오른쪽 -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 참고 자료
|
||||
|
||||
상세 내용은 references 폴더를 참조하세요:
|
||||
- `references/html-patterns.md` — 헤더/푸터/버튼/이미지 완성 코드 패턴
|
||||
- `references/verification-checklist.md` — 전체 검수 체크리스트 (시각적/기능/스팸)
|
||||
@@ -1,24 +0,0 @@
|
||||
# Example Asset File
|
||||
|
||||
This placeholder represents where asset files would be stored.
|
||||
Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed.
|
||||
|
||||
Asset files are NOT intended to be loaded into context, but rather used within
|
||||
the output Claude produces.
|
||||
|
||||
Example asset files from other skills:
|
||||
- Brand guidelines: logo.png, slides_template.pptx
|
||||
- Frontend builder: hello-world/ directory with HTML/React boilerplate
|
||||
- Typography: custom-font.ttf, font-family.woff2
|
||||
- Data: sample_data.csv, test_dataset.json
|
||||
|
||||
## Common Asset Types
|
||||
|
||||
- Templates: .pptx, .docx, boilerplate directories
|
||||
- Images: .png, .jpg, .svg, .gif
|
||||
- Fonts: .ttf, .otf, .woff, .woff2
|
||||
- Boilerplate code: Project directories, starter files
|
||||
- Icons: .ico, .svg
|
||||
- Data files: .csv, .json, .xml, .yaml
|
||||
|
||||
Note: This is a text placeholder. Actual assets can be any file type.
|
||||
@@ -1,34 +0,0 @@
|
||||
# Reference Documentation for Edm Email Html
|
||||
|
||||
This is a placeholder for detailed reference documentation.
|
||||
Replace with actual reference content or delete if not needed.
|
||||
|
||||
Example real reference docs from other skills:
|
||||
- product-management/references/communication.md - Comprehensive guide for status updates
|
||||
- product-management/references/context_building.md - Deep-dive on gathering context
|
||||
- bigquery/references/ - API references and query examples
|
||||
|
||||
## When Reference Docs Are Useful
|
||||
|
||||
Reference docs are ideal for:
|
||||
- Comprehensive API documentation
|
||||
- Detailed workflow guides
|
||||
- Complex multi-step processes
|
||||
- Information too lengthy for main SKILL.md
|
||||
- Content that's only needed for specific use cases
|
||||
|
||||
## Structure Suggestions
|
||||
|
||||
### API Reference Example
|
||||
- Overview
|
||||
- Authentication
|
||||
- Endpoints with examples
|
||||
- Error codes
|
||||
- Rate limits
|
||||
|
||||
### Workflow Guide Example
|
||||
- Prerequisites
|
||||
- Step-by-step instructions
|
||||
- Common patterns
|
||||
- Troubleshooting
|
||||
- Best practices
|
||||
@@ -1,327 +0,0 @@
|
||||
# EDM HTML 코드 패턴 모음
|
||||
|
||||
---
|
||||
|
||||
## 1컬럼 레이아웃 (전체 템플릿)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="format-detection" content="telephone=no, date=no, address=no">
|
||||
<title>이메일 제목</title>
|
||||
<!--[if mso]>
|
||||
<style type="text/css">
|
||||
body, table, td, p, a { font-family: Arial, sans-serif !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style type="text/css">
|
||||
body { margin: 0; padding: 0; width: 100%; background-color: #f5f5f5; }
|
||||
table { border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; }
|
||||
img { display: block; border: 0; outline: none; text-decoration: none; }
|
||||
a { color: inherit; }
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.container { width: 100% !important; }
|
||||
.mobile-full { width: 100% !important; display: block !important; }
|
||||
.mobile-padding { padding-left: 16px !important; padding-right: 16px !important; }
|
||||
.mobile-center { text-align: center !important; }
|
||||
.mobile-img { width: 100% !important; height: auto !important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0; background-color: #f5f5f5;">
|
||||
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f5f5f5">
|
||||
<tr>
|
||||
<td align="center" style="padding-top: 20px; padding-bottom: 20px;">
|
||||
|
||||
<table width="600" cellpadding="0" cellspacing="0" border="0"
|
||||
class="container"
|
||||
style="width: 600px; max-width: 100%; background-color: #ffffff;">
|
||||
|
||||
<!-- 헤더 -->
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="padding-top: 24px; padding-bottom: 24px;
|
||||
padding-left: 30px; padding-right: 30px;
|
||||
border-bottom: 1px solid #e5e7eb;">
|
||||
<img src="https://cdn.example.com/logo.png"
|
||||
alt="회사 로고" width="120" height="40"
|
||||
style="display: block; border: 0;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 히어로 이미지 -->
|
||||
<tr>
|
||||
<td style="padding: 0; line-height: 0;">
|
||||
<img src="https://cdn.example.com/hero.jpg"
|
||||
alt="이벤트 배너"
|
||||
width="600" height="280"
|
||||
class="mobile-img"
|
||||
style="display: block; width: 100%; max-width: 600px;
|
||||
height: auto; border: 0;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 본문 -->
|
||||
<tr>
|
||||
<td style="padding-top: 32px; padding-bottom: 32px;
|
||||
padding-left: 32px; padding-right: 32px;"
|
||||
class="mobile-padding">
|
||||
<h1 style="font-family: Arial, sans-serif;
|
||||
font-size: 24px; font-weight: bold;
|
||||
color: #111827; line-height: 1.3;
|
||||
margin: 0 0 16px 0;">
|
||||
이메일 제목이 여기 들어갑니다
|
||||
</h1>
|
||||
<p style="font-family: Arial, sans-serif;
|
||||
font-size: 15px; color: #374151;
|
||||
line-height: 1.7;
|
||||
margin: 0 0 24px 0;">
|
||||
본문 내용이 여기 들어갑니다. 가독성을 위해 line-height를
|
||||
1.5 이상으로 설정하는 것이 좋습니다.
|
||||
</p>
|
||||
|
||||
<!-- CTA 버튼 -->
|
||||
<div style="text-align: center;">
|
||||
<!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:w="urn:schemas-microsoft-com:office:word"
|
||||
href="https://example.com"
|
||||
style="height: 48px; v-text-anchor: middle; width: 200px;"
|
||||
arcsize="8%" stroke="f" fillcolor="#1a56db">
|
||||
<w:anchorlock/>
|
||||
<center style="color: #ffffff; font-family: Arial, sans-serif;
|
||||
font-size: 16px; font-weight: bold;">
|
||||
자세히 보기
|
||||
</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<a href="https://example.com"
|
||||
style="background-color: #1a56db; color: #ffffff;
|
||||
display: inline-block;
|
||||
padding-top: 14px; padding-bottom: 14px;
|
||||
padding-left: 32px; padding-right: 32px;
|
||||
text-decoration: none; border-radius: 6px;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 16px; font-weight: bold;">
|
||||
자세히 보기
|
||||
</a>
|
||||
<!--<![endif]-->
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- 푸터 -->
|
||||
<tr>
|
||||
<td bgcolor="#f9fafb"
|
||||
style="background-color: #f9fafb;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
padding-top: 24px; padding-bottom: 24px;
|
||||
padding-left: 32px; padding-right: 32px;
|
||||
text-align: center;">
|
||||
<p style="font-family: Arial, sans-serif; font-size: 12px;
|
||||
color: #9ca3af; line-height: 1.6; margin: 0 0 8px 0;">
|
||||
<strong>회사명</strong> | 서울시 강남구 테헤란로 123
|
||||
</p>
|
||||
<p style="font-family: Arial, sans-serif; font-size: 12px;
|
||||
color: #9ca3af; line-height: 1.6; margin: 0;">
|
||||
<a href="https://example.com/unsubscribe"
|
||||
style="color: #9ca3af; text-decoration: underline;">
|
||||
수신거부
|
||||
</a>
|
||||
|
|
||||
<a href="https://example.com/privacy"
|
||||
style="color: #9ca3af; text-decoration: underline;">
|
||||
개인정보처리방침
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2컬럼 이미지 + 텍스트
|
||||
|
||||
```html
|
||||
<table width="600" cellpadding="0" cellspacing="0" border="0"
|
||||
style="width: 600px;">
|
||||
<tr>
|
||||
<!-- 이미지 열 (40%) -->
|
||||
<td width="220" valign="top"
|
||||
style="width: 220px; padding-right: 0;"
|
||||
class="mobile-full">
|
||||
<img src="https://cdn.example.com/product.jpg"
|
||||
alt="상품명" width="220" height="220"
|
||||
class="mobile-img"
|
||||
style="display: block; width: 100%; height: auto; border: 0;">
|
||||
</td>
|
||||
|
||||
<!-- 간격 -->
|
||||
<td width="20" style="width: 20px; min-width: 20px;"> </td>
|
||||
|
||||
<!-- 텍스트 열 (60%) -->
|
||||
<td width="360" valign="top"
|
||||
style="width: 360px; padding-top: 8px;"
|
||||
class="mobile-full mobile-padding">
|
||||
<h2 style="font-family: Arial, sans-serif;
|
||||
font-size: 18px; font-weight: bold;
|
||||
color: #111827; margin: 0 0 8px 0;
|
||||
line-height: 1.3;">
|
||||
상품명
|
||||
</h2>
|
||||
<p style="font-family: Arial, sans-serif; font-size: 14px;
|
||||
color: #6b7280; line-height: 1.6;
|
||||
margin: 0 0 16px 0;">
|
||||
상품 설명이 들어갑니다. 간결하게 핵심만 작성하세요.
|
||||
</p>
|
||||
<p style="font-family: Arial, sans-serif; font-size: 20px;
|
||||
font-weight: bold; color: #ef4444;
|
||||
margin: 0 0 16px 0;">
|
||||
₩29,900
|
||||
</p>
|
||||
<a href="https://example.com/product"
|
||||
style="background-color: #111827; color: #ffffff;
|
||||
display: inline-block;
|
||||
padding-top: 10px; padding-bottom: 10px;
|
||||
padding-left: 20px; padding-right: 20px;
|
||||
text-decoration: none; border-radius: 4px;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 13px; font-weight: bold;">
|
||||
구매하기
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 헤더 배너 (이미지 기반)
|
||||
|
||||
이미지가 차단됐을 때도 배경색이 보이도록 `bgcolor` 속성을 함께 지정합니다:
|
||||
|
||||
```html
|
||||
<table width="600" cellpadding="0" cellspacing="0" border="0"
|
||||
style="width: 600px;">
|
||||
<tr>
|
||||
<td bgcolor="#1a56db" style="background-color: #1a56db; line-height: 0; padding: 0;">
|
||||
<img src="https://cdn.example.com/header-banner.jpg"
|
||||
alt="여름 세일 최대 70% 할인"
|
||||
width="600" height="240"
|
||||
style="display: block; width: 100%; max-width: 600px;
|
||||
height: auto; border: 0;">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 섹션 구분선
|
||||
|
||||
```html
|
||||
<!-- 섹션 간 여백 -->
|
||||
<tr>
|
||||
<td height="32" style="height: 32px; line-height: 32px;"> </td>
|
||||
</tr>
|
||||
|
||||
<!-- 수평선 -->
|
||||
<tr>
|
||||
<td style="padding-left: 32px; padding-right: 32px;">
|
||||
<table width="100%" cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td height="1" bgcolor="#e5e7eb"
|
||||
style="height: 1px; background-color: #e5e7eb; line-height: 1px;">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 아웃라인(외곽선) 버튼
|
||||
|
||||
```html
|
||||
<!--[if mso]>
|
||||
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:w="urn:schemas-microsoft-com:office:word"
|
||||
href="https://example.com"
|
||||
style="height: 44px; v-text-anchor: middle; width: 180px;"
|
||||
arcsize="5%"
|
||||
stroke="t"
|
||||
strokeweight="2px"
|
||||
strokecolor="#1a56db"
|
||||
fillcolor="#ffffff">
|
||||
<w:anchorlock/>
|
||||
<center style="color: #1a56db; font-family: Arial, sans-serif;
|
||||
font-size: 14px; font-weight: bold;">
|
||||
더 알아보기
|
||||
</center>
|
||||
</v:roundrect>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<a href="https://example.com"
|
||||
style="background-color: #ffffff;
|
||||
color: #1a56db;
|
||||
display: inline-block;
|
||||
padding-top: 12px; padding-bottom: 12px;
|
||||
padding-left: 24px; padding-right: 24px;
|
||||
text-decoration: none;
|
||||
border: 2px solid #1a56db;
|
||||
border-radius: 5px;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;">
|
||||
더 알아보기
|
||||
</a>
|
||||
<!--<![endif]-->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 소셜 아이콘 행
|
||||
|
||||
```html
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<tr>
|
||||
<td style="padding-right: 8px;">
|
||||
<a href="https://instagram.com/example" style="text-decoration: none;">
|
||||
<img src="https://cdn.example.com/icon-instagram.png"
|
||||
alt="Instagram" width="32" height="32"
|
||||
style="display: block; border: 0;">
|
||||
</a>
|
||||
</td>
|
||||
<td style="padding-right: 8px;">
|
||||
<a href="https://facebook.com/example" style="text-decoration: none;">
|
||||
<img src="https://cdn.example.com/icon-facebook.png"
|
||||
alt="Facebook" width="32" height="32"
|
||||
style="display: block; border: 0;">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://youtube.com/example" style="text-decoration: none;">
|
||||
<img src="https://cdn.example.com/icon-youtube.png"
|
||||
alt="YouTube" width="32" height="32"
|
||||
style="display: block; border: 0;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Example helper script for edm-email-html
|
||||
|
||||
This is a placeholder script that can be executed directly.
|
||||
Replace with actual implementation or delete if not needed.
|
||||
|
||||
Example real scripts from other skills:
|
||||
- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields
|
||||
- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images
|
||||
"""
|
||||
|
||||
def main():
|
||||
print("This is an example script for edm-email-html")
|
||||
# TODO: Add actual script logic here
|
||||
# This could be data processing, file conversion, API calls, etc.
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
name: vue-component-review
|
||||
description: Vue 3 / Nuxt 컴포넌트 파일을 팀 공통 지침(fe-common-rules) 기준으로 리뷰할 때 사용합니다. 사용자가 "이 컴포넌트 리뷰해줘", "컨벤션 맞는지 봐줘", "컴포넌트 체크" 등을 요청하면 트리거됩니다.
|
||||
---
|
||||
|
||||
# Vue 컴포넌트 리뷰
|
||||
|
||||
이 skill 은 `.vue` 파일 하나 또는 여러 개에 대해 팀 공통 지침 기준으로 리뷰 체크리스트를
|
||||
수행합니다. 프로젝트 전용 컨벤션(`.claude/project/conventions.md`)이 있으면 그 규칙을
|
||||
**우선**합니다.
|
||||
|
||||
## 체크리스트
|
||||
|
||||
### 1. 파일 구조
|
||||
|
||||
- [ ] `<script setup lang="ts">` 를 사용하는가? (Options API 금지)
|
||||
- [ ] 파일 길이가 팀/프로젝트 제한을 넘지 않는가? (공통 200줄, 프로젝트별 오버라이드 확인)
|
||||
- [ ] 하나의 컴포넌트가 단일 책임을 지키는가?
|
||||
|
||||
### 2. Props / Emits
|
||||
|
||||
- [ ] `defineProps<T>()` 제네릭 형태로 타입을 명시했는가?
|
||||
- [ ] `defineEmits<{ ... }>()` 제네릭 형태로 선언했는가?
|
||||
- [ ] Props 개수가 많다면 객체 props 로 묶여 있는가?
|
||||
- [ ] 불리언 prop 은 `is`/`has`/`can`/`should` 로 시작하는가?
|
||||
|
||||
### 3. 반응성
|
||||
|
||||
- [ ] `ref` 와 `reactive` 를 팀 규칙대로 사용하고 있는가?
|
||||
- [ ] 불필요한 `reactive` 래핑이 없는가?
|
||||
- [ ] 계산된 값은 `computed` 로 뽑아냈는가?
|
||||
|
||||
### 4. 스타일 (Tailwind)
|
||||
|
||||
- [ ] 임의값 클래스(`w-[123px]`) 남용이 없는가?
|
||||
- [ ] 색상/간격 토큰을 사용하는가? (임의 색상 금지)
|
||||
- [ ] 조건부 클래스는 `clsx` / `cn` 로 가독성 확보되었는가?
|
||||
- [ ] 클래스 순서는 `prettier-plugin-tailwindcss` 규칙을 따르는가?
|
||||
|
||||
### 5. 네이밍
|
||||
|
||||
- [ ] 파일명: `PascalCase.vue`
|
||||
- [ ] 이벤트 핸들러: `handle*` 또는 `on*` 접두사
|
||||
- [ ] 상수: `UPPER_SNAKE_CASE`
|
||||
|
||||
### 6. 의존성 / 로직
|
||||
|
||||
- [ ] 비즈니스 로직이 컴포넌트에 직접 박혀있지 않고 composable 로 추출되었는가?
|
||||
- [ ] `$fetch` / `fetch` 직접 호출이 없는가? (프로젝트 규칙에 따라 api wrapper 사용)
|
||||
- [ ] `any` 타입 사용이 없는가?
|
||||
|
||||
## 작업 순서
|
||||
|
||||
1. 리뷰 대상 파일을 읽는다. 여러 파일이면 하나씩 순차 처리한다.
|
||||
2. `.claude/project/conventions.md` 가 있으면 먼저 읽고, 공통 규칙과의 차이를 기억한다.
|
||||
3. 위 체크리스트를 항목별로 점검하고, 위반 사항을 발견하면 **파일명:라인번호** 와
|
||||
함께 문제 요약 + 수정 예시를 제시한다.
|
||||
4. 단순 포맷 이슈는 "Prettier/ESLint 로 자동 해결 가능" 이라고 덧붙인다.
|
||||
5. 마지막에 우선순위별 요약(Critical / Warning / Nit)을 출력한다.
|
||||
|
||||
## 출력 형식
|
||||
|
||||
```
|
||||
## 리뷰 결과: <파일명>
|
||||
|
||||
### 🚨 Critical (반드시 수정)
|
||||
- [라인 23] Props 타입이 `any` 로 선언됨. `defineProps<{ id: string }>()` 로 변경
|
||||
- ...
|
||||
|
||||
### ⚠️ Warning (수정 권장)
|
||||
- [라인 45] 파일 길이 210줄. 하위 컴포넌트 2개로 분리 검토
|
||||
- ...
|
||||
|
||||
### 💡 Nit (선택)
|
||||
- [라인 12] 클래스 순서가 Tailwind 프리셋과 다름
|
||||
- ...
|
||||
|
||||
### ✅ 좋은 점
|
||||
- 간단한 컴포넌트에 잘 맞는 단일 책임 구조
|
||||
- ...
|
||||
```
|
||||
|
||||
## 주의사항
|
||||
|
||||
- 사용자가 리팩토링을 **요청하지 않은 경우** 코드를 직접 수정하지 말고 리뷰만 수행한다.
|
||||
- 프로젝트 지침과 공통 지침이 충돌하면 프로젝트 지침을 따르되, 차이를 사용자에게 알린다.
|
||||
- 실제 코드 동작 변경(기능 수정)은 리뷰 범위가 아니다. 별도 작업으로 분리 제안한다.
|
||||
@@ -1,9 +0,0 @@
|
||||
# <프로젝트 이름>
|
||||
|
||||
## 공통 지침
|
||||
@.claude/common/CLAUDE.md
|
||||
|
||||
## 프로젝트 지침
|
||||
@.claude/project/overview.md
|
||||
@.claude/project/conventions.md
|
||||
@.claude/project/architecture.md
|
||||
@@ -1,46 +0,0 @@
|
||||
# 아키텍처
|
||||
|
||||
> 이 파일은 `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 정책이 있다면 함께 기술
|
||||
@@ -1,44 +0,0 @@
|
||||
# 프로젝트 전용 컨벤션
|
||||
|
||||
> 이 파일은 `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__ 폴더>
|
||||
- 최소 커버리지: <숫자>
|
||||
@@ -1,39 +0,0 @@
|
||||
# 프로젝트 개요
|
||||
|
||||
> 이 파일은 `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 스펙: <링크>
|
||||
@@ -1,42 +0,0 @@
|
||||
# 아키텍처
|
||||
|
||||
> 양식 출처: `.claude/common/templates/project/architecture.md`
|
||||
> 샘플 프로젝트의 예시로 채워진 버전입니다.
|
||||
|
||||
## 레이어 구조
|
||||
|
||||
```
|
||||
┌───────────────────────────────────┐
|
||||
│ pages (Nuxt) │ ← 라우팅, 데이터 페칭 진입점
|
||||
├───────────────────────────────────┤
|
||||
│ components │ ← 재사용 UI (도메인/공용 분리)
|
||||
├───────────────────────────────────┤
|
||||
│ composables (use*) │ ← 상태 + 로직 (뷰 독립)
|
||||
├───────────────────────────────────┤
|
||||
│ composables/api (wrapper) │ ← 서버 통신 단일 창구
|
||||
├───────────────────────────────────┤
|
||||
│ server/api │ ← Nuxt 서버 라우트
|
||||
└───────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 의존 규칙
|
||||
|
||||
- 하위 레이어는 상위 레이어를 import 할 수 없습니다. (단방향 의존)
|
||||
- `components` 는 비즈니스 로직을 직접 수행하지 않고, composable 에 위임합니다.
|
||||
- 전역 상태는 필요한 경우에만 Pinia store 로 승격합니다.
|
||||
|
||||
## 데이터 흐름
|
||||
|
||||
1. 사용자 이벤트 → `components` 에서 `composable` 호출
|
||||
2. `composable` → `composables/api` 래퍼 호출
|
||||
3. 래퍼 → `$fetch` 로 Nuxt `server/api` 또는 외부 API 요청
|
||||
4. 응답은 타입이 보장된 상태로 composable → component 로 전달
|
||||
|
||||
## 상태 관리 가이드
|
||||
|
||||
| 상태 종류 | 권장 위치 |
|
||||
| -------------------- | --------------------------------- |
|
||||
| 컴포넌트 로컬 상태 | `ref` / `reactive` |
|
||||
| 페이지 단위 공유 상태| `provide/inject` 또는 composable |
|
||||
| 앱 전역 상태 | Pinia store |
|
||||
| 서버 데이터 | `useAsyncData` / `useFetch` |
|
||||
@@ -1,34 +0,0 @@
|
||||
# 프로젝트 전용 컨벤션
|
||||
|
||||
> 양식 출처: `.claude/common/templates/project/conventions.md`
|
||||
> 샘플 프로젝트의 예시로 채워진 버전입니다.
|
||||
> 공통 지침(`.claude/common/`)과 충돌할 경우 이 문서가 우선합니다.
|
||||
|
||||
## 디렉토리 규칙
|
||||
|
||||
- `components/` — 재사용 컴포넌트. 도메인별 하위 폴더(`user/`, `order/`)로 분리
|
||||
- `composables/` — `useXxx` 형태의 재사용 로직
|
||||
- `pages/` — Nuxt 파일 기반 라우팅
|
||||
- `server/api/` — 서버 라우트 핸들러
|
||||
- `types/` — 전역/공통 타입 정의
|
||||
|
||||
## 컴포넌트 규칙 (공통 규칙 오버라이드)
|
||||
|
||||
- 컴포넌트 파일 길이 제한: **150줄** (공통 200줄보다 엄격)
|
||||
- 컴포넌트 당 `defineProps` 최대 **7개**. 초과 시 객체 props 로 묶기
|
||||
|
||||
## 스타일
|
||||
|
||||
- 색상은 `tailwind.config.ts` 의 `theme.extend.colors` 에 등록된 토큰만 사용 (임의 색상 금지)
|
||||
- 다크모드 클래스 prefix: `dark:`
|
||||
|
||||
## 네트워크
|
||||
|
||||
- API 호출은 반드시 `composables/api/` 의 래퍼를 통해 수행
|
||||
- 직접 `$fetch` / `fetch` 사용 금지 (테스트 목적 제외)
|
||||
|
||||
## 테스트
|
||||
|
||||
- Vitest 를 기본 테스트 러너로 사용
|
||||
- 테스트 파일은 소스 옆에 `*.spec.ts` 로 배치
|
||||
- 공통 유틸과 composable 은 커버리지 80% 이상 유지
|
||||
@@ -1,33 +0,0 @@
|
||||
# 프로젝트 개요
|
||||
|
||||
> 양식 출처: `.claude/common/templates/project/overview.md`
|
||||
> 샘플 프로젝트의 예시로 채워진 버전입니다.
|
||||
|
||||
## 서비스
|
||||
|
||||
- **이름**: Sample Nuxt App
|
||||
- **설명**: fe-common-rules 도입 예시용 샘플 프로젝트
|
||||
- **배포 환경**: 로컬 전용 (샘플)
|
||||
- **저장소**: git@github.com:<org>/sample-nuxt-project.git
|
||||
|
||||
## 기술 스택
|
||||
|
||||
- **Framework**: Nuxt 4
|
||||
- **UI**: Vue 3 (`<script setup lang="ts">`)
|
||||
- **Language**: TypeScript (strict)
|
||||
- **Styling**: Tailwind CSS
|
||||
- **상태관리**: Pinia (필요 시)
|
||||
- **테스트**: Vitest
|
||||
- **패키지매니저**: pnpm
|
||||
|
||||
## 주요 기능
|
||||
|
||||
- 메인 페이지 (`/`)
|
||||
- 샘플 컴포넌트 `HelloCard.vue`
|
||||
- 공통 skill `vue-component-review` 를 활용한 컴포넌트 점검
|
||||
- 공통 skill `conventional-commit` 을 활용한 커밋 메시지 작성
|
||||
|
||||
## 팀 / 오너
|
||||
|
||||
- 오너: Frontend Team
|
||||
- 문의 채널: #fe-chapter (Slack)
|
||||
@@ -1 +0,0 @@
|
||||
../common/skills/conventional-commit
|
||||
@@ -1 +0,0 @@
|
||||
../common/skills/edm-email-html
|
||||
@@ -1 +0,0 @@
|
||||
../common/skills/vue-component-review
|
||||
24
examples/sample-nuxt-project/.gitignore
vendored
24
examples/sample-nuxt-project/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
# Nuxt dev/build outputs
|
||||
.output
|
||||
.data
|
||||
.nuxt
|
||||
.nitro
|
||||
.cache
|
||||
dist
|
||||
|
||||
# Node dependencies
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.fleet
|
||||
.idea
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
@@ -1,7 +0,0 @@
|
||||
# 실제 프로젝트에서는 이 파일이 .gitmodules 로 생성됩니다.
|
||||
# git submodule add <repo-url> .claude/common 명령 실행 후 자동 생성되며,
|
||||
# 아래와 같은 형태로 저장됩니다. (샘플은 실제 submodule 이 아니므로 예시로 포함)
|
||||
[submodule ".claude/common"]
|
||||
path = .claude/common
|
||||
url = git@github.com:<your-org>/fe-common-rules.git
|
||||
branch = main
|
||||
@@ -1,16 +0,0 @@
|
||||
# sample-nuxt-project
|
||||
|
||||
Nuxt 4 + Vue 3 + TypeScript + Tailwind CSS 기반의 샘플 프로젝트입니다.
|
||||
이 프로젝트는 `fe-common-rules` 공통 지침과 skill 을 submodule 형태로 연결하여 사용합니다.
|
||||
|
||||
## 공통 지침
|
||||
|
||||
@.claude/common/CLAUDE.md
|
||||
|
||||
## 프로젝트 지침
|
||||
|
||||
@.claude/project/overview.md
|
||||
|
||||
@.claude/project/conventions.md
|
||||
|
||||
@.claude/project/architecture.md
|
||||
@@ -1,107 +0,0 @@
|
||||
# sample-nuxt-project
|
||||
|
||||
`fe-common-rules` 공통 지침과 skill 을 실제 프로젝트에서 어떻게 사용하는지 보여주는 **샘플**입니다.
|
||||
|
||||
> 💡 이 샘플은 실제로 실행 가능한 Nuxt 앱이 아니라, **디렉토리 구조와 CLAUDE.md / skill 연동 방식**을 보여주기 위한 최소 구성입니다.
|
||||
|
||||
## 구조
|
||||
|
||||
```
|
||||
sample-nuxt-project/
|
||||
├── CLAUDE.md # 공통 + 프로젝트 지침 @import 엔트리
|
||||
├── .claude/
|
||||
│ ├── common/ # fe-common-rules submodule
|
||||
│ │ ├── CLAUDE.md
|
||||
│ │ ├── rules/*.md # 코딩/프레임워크/커밋/워크플로 지침
|
||||
│ │ ├── templates/project/*.md # 프로젝트 지침 양식
|
||||
│ │ ├── skills/ # 팀 공용 Claude skill
|
||||
│ │ │ ├── vue-component-review/SKILL.md
|
||||
│ │ │ └── conventional-commit/SKILL.md
|
||||
│ │ └── scripts/
|
||||
│ │ ├── install.sh
|
||||
│ │ ├── init-project.sh
|
||||
│ │ ├── link-skills.sh
|
||||
│ │ └── update.sh
|
||||
│ ├── project/ # 프로젝트 고유 지침 (양식에서 복사 후 편집)
|
||||
│ │ ├── overview.md
|
||||
│ │ ├── conventions.md
|
||||
│ │ └── architecture.md
|
||||
│ └── skills/ # 공통 skill 링크 + 프로젝트 고유 skill
|
||||
│ ├── vue-component-review → ../common/skills/vue-component-review
|
||||
│ └── conventional-commit → ../common/skills/conventional-commit
|
||||
├── package.json # Nuxt 4 + Vue 3 + TS + Tailwind 의존성
|
||||
├── nuxt.config.ts
|
||||
├── tsconfig.json
|
||||
├── tailwind.config.ts
|
||||
├── app.vue
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
## 실제 프로젝트에서는 어떻게 만드나요?
|
||||
|
||||
### 방법 A: install.sh 한 번으로 세팅
|
||||
|
||||
```bash
|
||||
cd my-project
|
||||
git init && git add . && git commit -m "chore: init"
|
||||
curl -fsSL https://<raw>/scripts/install.sh | bash -s -- git@github.com:<org>/fe-common-rules.git
|
||||
```
|
||||
|
||||
스크립트가 아래를 한 번에 처리합니다.
|
||||
|
||||
1. `.claude/common/` 에 fe-common-rules 를 submodule 로 추가
|
||||
2. `.claude/common/templates/project/*.md` → `.claude/project/` 로 복사
|
||||
3. `.claude/common/templates/CLAUDE.md.tpl` → 루트 `CLAUDE.md` 로 복사
|
||||
4. `.claude/common/skills/*` → `.claude/skills/*` 로 심볼릭 링크
|
||||
|
||||
### 방법 B: 수동 설정
|
||||
|
||||
```bash
|
||||
git submodule add git@github.com:<org>/fe-common-rules.git .claude/common
|
||||
git submodule update --init --recursive
|
||||
|
||||
# 프로젝트 지침 양식 복사
|
||||
mkdir -p .claude/project
|
||||
cp .claude/common/templates/project/*.md .claude/project/
|
||||
|
||||
# 루트 CLAUDE.md 템플릿 복사
|
||||
cp .claude/common/templates/CLAUDE.md.tpl CLAUDE.md
|
||||
|
||||
# 공통 skill 링크
|
||||
bash .claude/common/scripts/link-skills.sh
|
||||
```
|
||||
|
||||
## 공통 지침 / skill 업데이트
|
||||
|
||||
```bash
|
||||
# submodule 을 최신으로 당기기
|
||||
git submodule update --remote --merge .claude/common
|
||||
|
||||
# 새로 추가된 skill 이 있다면 자동 링크
|
||||
bash .claude/common/scripts/link-skills.sh
|
||||
|
||||
git add .gitmodules .claude
|
||||
git commit -m "chore: sync fe-common-rules"
|
||||
```
|
||||
|
||||
심볼릭 링크 방식이므로 **이미 링크된 기존 skill 의 내용**은 submodule 업데이트만으로
|
||||
자동 최신화됩니다. `link-skills.sh` 는 "새로 추가된 skill 이 있을 때" 한 번씩 실행하면 됩니다.
|
||||
|
||||
## 프로젝트 지침 양식 업데이트
|
||||
|
||||
```bash
|
||||
# 양식 차이 확인
|
||||
bash .claude/common/scripts/init-project.sh --diff
|
||||
|
||||
# 없는 양식만 새로 복사 (기존 파일 보존)
|
||||
bash .claude/common/scripts/init-project.sh
|
||||
|
||||
# 강제 덮어쓰기
|
||||
bash .claude/common/scripts/init-project.sh --force
|
||||
```
|
||||
|
||||
## Claude 가 읽는 순서
|
||||
|
||||
1. 루트 `CLAUDE.md` → `@.claude/common/CLAUDE.md` → `@rules/*.md` → `@.claude/project/*.md`
|
||||
2. Claude 가 특정 작업을 할 때 `.claude/skills/<skill>/SKILL.md` 의 description 과 매칭되면 해당 skill 을 자동 트리거
|
||||
3. 충돌이 있을 경우 **프로젝트 지침이 우선**합니다.
|
||||
@@ -1,16 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const title = 'fe-common-rules 연동 샘플'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex min-h-screen items-center justify-center bg-brand-50">
|
||||
<div class="rounded-2xl bg-white px-8 py-10 shadow-md">
|
||||
<h1 class="text-2xl font-bold text-brand-700">{{ title }}</h1>
|
||||
<p class="mt-2 text-slate-600">
|
||||
공통 지침은 <code>.claude/common</code>,
|
||||
프로젝트 지침은 <code>.claude/project</code>,
|
||||
공통 skill 은 <code>.claude/skills</code> 에서 확인하세요.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,19 +0,0 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-01-01',
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxtjs/tailwindcss'],
|
||||
typescript: {
|
||||
strict: true,
|
||||
typeCheck: true,
|
||||
},
|
||||
app: {
|
||||
head: {
|
||||
title: 'Sample Nuxt Project',
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "sample-nuxt-project",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"preview": "nuxt preview",
|
||||
"lint": "eslint .",
|
||||
"format": "prettier --write .",
|
||||
"typecheck": "nuxt typecheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"nuxt": "^4.0.0",
|
||||
"vue": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxtjs/tailwindcss": "^6.12.0",
|
||||
"@types/node": "^20.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
"prettier": "^3.3.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.0",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"typescript": "^5.5.0",
|
||||
"vitest": "^2.0.0"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
export default {
|
||||
content: [
|
||||
'./components/**/*.{vue,js,ts}',
|
||||
'./layouts/**/*.vue',
|
||||
'./pages/**/*.vue',
|
||||
'./app.vue',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: {
|
||||
50: '#eef2ff',
|
||||
500: '#6366f1',
|
||||
700: '#4338ca',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
} satisfies Config
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user