Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .claude/decisions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Decision History

이 폴더는 AI 에이전트와 개발자가 프로젝트의 중요한 결정 맥락을 잃지 않도록 관리하는 기록입니다.

## 기록 대상

아래 중 하나에 해당할 때만 decision 문서를 추가합니다.

- 디렉터리 구조, 레이어 경계, 파일 배치 기준을 바꿀 때
- API client, 상태 관리, form 처리, 테스트 전략 같은 반복 패턴을 정할 때
- 라이브러리를 추가하거나 기존 선택지를 바꿀 때
- 기존 규칙과 다른 예외를 허용할 때
- 나중에 다시 논쟁할 가능성이 높은 trade-off가 있을 때

아래 작업은 기록하지 않습니다.

- 단순 버그 수정
- 오타, 문구, 스타일만 바꾸는 변경
- 기존 규칙을 그대로 따르는 CRUD 구현
- 한 번 쓰고 끝나는 페이지 내부 구현 세부사항

## 파일명

```text
NNN-short-title.md
```

예시:

```text
001-admin-api-reference.md
002-query-key-policy.md
003-page-folder-boundary.md
```

## 작성 형식

새 decision은 `.claude/decisions/template.md`를 복사한 형식으로 작성합니다.

문서는 길게 쓰지 않습니다. 결정 이유, 버린 대안, 이후 수정자가 지켜야 할 점만 남깁니다.
28 changes: 28 additions & 0 deletions .claude/decisions/template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# NNN. 결정 제목

## 상태

- Proposed | Accepted | Superseded

## 맥락

어떤 문제를 해결하려는지, 어떤 제약이 있었는지 적습니다.

## 결정

선택한 방향을 짧게 적습니다.

## 근거

- 이 선택이 프로젝트에 맞는 이유를 적습니다.
- 중요한 trade-off가 있다면 함께 적습니다.

## 대안

- 검토했지만 선택하지 않은 대안과 이유를 적습니다.

## 영향

- 앞으로 코드를 작성할 때 따라야 하는 기준을 적습니다.
- 관련 파일이나 규칙이 있으면 링크합니다.

36 changes: 36 additions & 0 deletions .claude/hooks/guard-rules.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
$inputText = $env:CLAUDE_TOOL_INPUT
if (-not $inputText) {
exit 0
Comment on lines +1 to +3

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Hook env var usage =="
fd -a '.*\.ps1$' .claude/hooks | xargs -r rg -n '\$env:(TOOL_INPUT|CLAUDE_TOOL_INPUT)'

echo
echo "== Hook registrations =="
sed -n '1,80p' .claude/settings.json
echo
sed -n '1,80p' .codex/hooks.json

Repository: kusitms-com/makers-admin-fe

Length of output: 2461


입력 payload env var 계약이 어긋납니다. guard-rules.ps1CLAUDE_TOOL_INPUT만 읽는데, 같은 훅 계열과 등록 설정은 TOOL_INPUT 기준입니다. 입력이 비면 바로 종료되어 guard가 무력화되므로 둘 다 fallback으로 받게 해주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/hooks/guard-rules.ps1 around lines 1 - 3, The input env var contract
in guard-rules.ps1 is mismatched: the script only reads CLAUDE_TOOL_INPUT while
the rest of the hook setup uses TOOL_INPUT, so the guard can exit early with no
input. Update the guard logic in the script to read TOOL_INPUT with fallback to
CLAUDE_TOOL_INPUT (or vice versa) before the empty check, and keep the existing
exit behavior only when both are unavailable.

}

$filePath = ''
$content = $inputText

try {
$parsed = $inputText | ConvertFrom-Json
if ($parsed.file_path) { $filePath = $parsed.file_path }
if ($parsed.new_string) { $content = $parsed.new_string }
elseif ($parsed.content) { $content = $parsed.content }
} catch { }

$isCodeFile = $filePath -match '\.(ts|tsx|js|jsx)$'

if ($isCodeFile -and ($content -match 'console\.log')) {
Write-Error 'Avoid console.log. Use the project logging/debugging approach instead.'
exit 1
}

if ($isCodeFile -and ($content -match ':\s*any([^A-Za-z]|$)|<any>|as any')) {
Write-Error 'Avoid explicit any. Use a specific type or unknown.'
exit 1
}

if ($content -match '(^|\s)(npm|yarn)\s') {
Write-Error 'Use pnpm in this project.'
exit 1
}

if ($filePath -match 'tailwind\.config\.') {
Write-Error 'This is a Tailwind CSS v4 project. Do not create tailwind.config unless explicitly requested.'
exit 1
}
15 changes: 15 additions & 0 deletions .claude/hooks/lint-check.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
$toolInput = $env:TOOL_INPUT
if (-not $toolInput) {
exit 0
}

try {
$json = $toolInput | ConvertFrom-Json
$filePath = $json.file_path
} catch {
exit 0
}

if ($filePath -and ($filePath.EndsWith('.ts') -or $filePath.EndsWith('.tsx'))) {
pnpm exec eslint --quiet "$filePath" 2>&1 | Select-Object -Last 5
}
42 changes: 42 additions & 0 deletions .claude/hooks/skill-suggest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
$prompt = $env:CLAUDE_USER_PROMPT
if (-not $prompt) {
exit 0
}

$prompt = $prompt.ToLowerInvariant()
$suggestions = New-Object System.Collections.Generic.List[string]

if ($prompt -match 'commit|staged|push') {
$suggestions.Add('/commit-kr - create a Korean commit message')
}

if ($prompt -match '\bpr\b|pull request') {
$suggestions.Add('/create-pr - create a PR title and body draft')
}

if ($prompt -match 'refactor|cleanup|dedupe|simplify') {
$suggestions.Add('/refactor - analyze refactoring candidates')
}

if ($prompt -match 'test|vitest|tdd|coverage') {
$suggestions.Add('/gen-test - generate tests')
}

if ($prompt -match 'swagger|openapi|api generate|api client|api scaffold|admin api') {
$suggestions.Add('/scaffold-api - generate API functions and Query hooks')
}

if ($prompt -match 'figma|design|screen|layout|component') {
$suggestions.Add('/figma-to-component - implement a Figma-based component')
}

if ($prompt -match 'review') {
$suggestions.Add('/code-review - review changed code')
}

if ($suggestions.Count -gt 0) {
Write-Output 'Suggested local skills:'
foreach ($suggestion in $suggestions) {
Write-Output " $suggestion"
}
}
97 changes: 97 additions & 0 deletions .claude/manifest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Claude Manifest

이 문서는 AI 코딩 에이전트가 `kusitms-admin`에서 작업하기 전에 어떤 지침을 읽어야 하는지 정의합니다.

## 부트 순서

1. `CLAUDE.md` 또는 `AGENTS.md`에서 프로젝트 개요를 읽습니다.
2. 이 manifest를 읽습니다.
3. 코드 변경 작업에서는 Tier 1 규칙을 로드합니다.
4. Tier 2 규칙과 admin API/domain reference는 작업에 필요한 경우에만 로드합니다.

## Tier 1: 기본 로드

| 파일 | 목적 |
| --- | --- |
| `.claude/rules/code-quality.md` | 코드 품질 원칙 |
| `.claude/rules/project-constraints.md` | 프로젝트 금지/제약 조건 |
| `.claude/rules/workflow.md` | 기본 inspect-edit-verify 작업 흐름 |
| `.claude/rules/verification.md` | 검증 명령 선택 기준 |

Tier 1은 코드 변경, 리뷰, 리팩터링처럼 실제 작업이 시작될 때만 로드합니다. 단순 질문이나 설명 요청에서는 `CLAUDE.md`와 이 manifest만으로 답합니다.

## Tier 2: 필요 시 로드

| 작업 | 로드할 문서 |
| --- | --- |
| 파일 배치, import, 구조 판단 | `.claude/rules/architecture.md` |
| TypeScript, React, 네이밍, 스타일 판단 | `.claude/rules/code-style.md` |
| 재사용 UI 생성 또는 수정 | `.claude/rules/component-guide.md`, `.claude/references/component-patterns.md` |
| 서버 상태, API client, form 작업 | `.claude/rules/data-fetching.md` |
| 테스트 추가 또는 수정 | `.claude/rules/testing.md` |
| SVG 아이콘 또는 barrel index 생성물 작업 | `.claude/rules/generated-workflows.md` |
| 어드민 API 연동 추가 | `.claude/references/api/admin.md`와 관련 admin API/domain 문서 |
| 폼 또는 검증 로직 추가 | `.claude/rules/data-fetching.md`와 관련 domain 문서 |
| PR/커밋 문구 작성 | `.claude/rules/workflow.md`, `.claude/rules/commit-convention.md` |
| 구조, 라이브러리, 규칙 변경 결정 | `.claude/decisions/README.md` |

## Skills

| Skill | 용도 |
| --- | --- |
| `/scaffold-api` | Swagger 기반 API 함수와 TanStack Query hook 생성 |
| `/gen-test` | Vitest + Testing Library 테스트 생성 |
| `/code-review` | 변경 코드 리뷰 |
| `/commit-kr` | 한국어 커밋 메시지 제안 |
| `/create-pr` | PR 제목과 본문 초안 작성 |
| `/figma-to-component` | Figma 디자인을 프로젝트 컴포넌트로 구현 |
| `/refactor` | 동작 변경 없는 리팩터링 후보 분석과 적용 |

## Hooks

Claude Code 환경에서는 `.claude/settings.json`이 아래 hook을 사용합니다.

- `guard-rules.ps1`: `console.log`, 명시적 `any`, `npm`/`yarn`, Tailwind v3 설정 생성을 차단합니다.
- `lint-check.ps1`: TypeScript 파일 수정 후 가능한 경우 `pnpm exec eslint --quiet <file>`을 실행합니다.
- `skill-suggest.ps1`: 사용자 프롬프트 키워드에 맞는 local skill을 제안합니다.

## API Reference

관련 admin 파일만 로드합니다. 엔드포인트가 아직 문서화되지 않았다면 Swagger에서 admin 관련 endpoint만 확인하고 reference를 업데이트한 뒤 구현합니다.

| 영역 | 파일 | 트리거 키워드 |
| --- | --- | --- |
| 어드민 API 개요 | `.claude/references/api/admin.md` | admin, swagger, API client, axios, fetch |
| 어드민 인증 | `.claude/references/api/admin-auth.md` | auth, login, token, session |
| 어드민 멤버 승인 | `.claude/references/api/admin-members.md` | member, pending, approve, reject |
| 어드민 프로젝트 | `.claude/references/api/admin-projects.md` | project, meetup, corporate |
| 어드민 학회원 후기 | `.claude/references/api/admin-reviews.md` | review, member review |
| 어드민 블로그 후기 | `.claude/references/api/admin-blog-reviews.md` | blog review, blog |
| 어드민 학회 소개 | `.claude/references/api/admin-introductions.md` | introduction, intro, banner |

## Domain Reference

| 도메인 | 파일 | 트리거 키워드 |
| --- | --- | --- |
| 멤버 승인 | `.claude/references/domain/members.md` | pending member, approve, reject |
| 프로젝트 관리 | `.claude/references/domain/projects.md` | project, meetup, corporate |
| 학회원 후기 관리 | `.claude/references/domain/reviews.md` | review, member review |
| 블로그 후기 관리 | `.claude/references/domain/blog-reviews.md` | blog review |
| 학회 소개 관리 | `.claude/references/domain/introductions.md` | introduction, intro, banner |

## Decision History

프로젝트 구조, 라이브러리, API 처리 방식, 공통 규칙처럼 나중에 반복 논쟁이 생길 수 있는 결정은 `.claude/decisions/`에 기록합니다.

- 단순 구현, 오타 수정, 일회성 UI 조정은 기록하지 않습니다.
- 새 decision을 만들기 전 `.claude/decisions/README.md`를 먼저 확인합니다.
- 파일명은 `NNN-short-title.md` 형식을 사용합니다.

## 금지 패턴

- 모든 reference 파일을 기본으로 로드하지 않습니다.
- Swagger 전체를 일반 API 기준으로 해석하지 않고, admin 기능에 필요한 endpoint만 사용합니다.
- 엔드포인트 경로, request body, response shape를 추측해서 만들지 않습니다.
- `src/services` 또는 `src/views`를 만들지 않고 기존 구조를 사용합니다.
- 새 UI 라이브러리나 패키지 매니저를 도입하지 않습니다.
- Tailwind v4 프로젝트에 Tailwind v3 설정을 추가하지 않습니다.
23 changes: 23 additions & 0 deletions .claude/references/api/admin-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 어드민 인증 API Reference

관리자 로그인, 토큰, 세션, 권한 확인 endpoint 작업에는 이 파일을 사용합니다.

## 구현 전 확인

- Swagger에서 관리자 인증 endpoint인지 확인합니다.
- 일반 사용자 인증 API를 관리자 인증으로 대체 사용하지 않습니다.
- 관리자 API가 Bearer token, cookie session, 또는 다른 인증 방식을 요구하는지 확인합니다.

## 문서화할 항목

- 관리자 로그인 endpoint path와 method
- request body
- response body
- token refresh 또는 logout endpoint
- error response shape

## 구현 메모

- 인증 API 함수는 `src/api/auth.ts` 또는 admin 인증 도메인 파일에 둡니다.
- 서버 상태는 필요한 경우 TanStack Query로 관리합니다.
- token 저장 방식은 구현 전 명확히 확인합니다.
35 changes: 35 additions & 0 deletions .claude/references/api/admin-blog-reviews.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 어드민 블로그 후기 API Reference

Swagger 출처: `https://kusitms.herokuapp.com/api-docs/json`

블로그 후기 CRUD에는 이 파일을 사용합니다.

## Endpoints

| Method | Path | Operation ID | Request | Response |
| --- | --- | --- | --- | --- |
| `GET` | `/admin/blog-review` | `getBlogReviews` | 없음 | `BaseResponseObject` |
| `POST` | `/admin/blog-review` | `addBlogReview` | `BlogReviewRequest` multipart | `BaseResponseObject` |
| `PUT` | `/admin/blog-review` | `updateBlogReview` | `BlogReviewRequest` multipart | `BaseResponseObject` |
| `DELETE` | `/admin/blog-review/{id}` | `deleteBlogReview` | path: `id` | `BaseResponseObject` |

## 구현 메모

- API 함수는 `src/api/blogReviews.ts`에 둡니다.
- query key는 `['admin', 'blog-reviews']`를 우선합니다.
- 등록/수정은 `multipart/form-data`입니다.
- 등록/수정/삭제 이후 list query를 invalidate합니다.

## Request Fields

### `BlogReviewRequest`

| Field | Type |
| --- | --- |
| `blogReviewId` | `number` |
| `cardinal` | `number` |
| `part` | `string` |
| `activity` | `string` |
| `thumbnailFile` | `File` |
| `title` | `string` |
| `previewText` | `string` |
45 changes: 45 additions & 0 deletions .claude/references/api/admin-introductions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 어드민 학회 소개 API Reference

Swagger 출처: `https://kusitms.herokuapp.com/api-docs/json`

학회 소개 페이지의 관리자 조회/수정에는 이 파일을 사용합니다.

## Endpoints

| Method | Path | Operation ID | Request | Response |
| --- | --- | --- | --- | --- |
| `GET` | `/admin/introductions` | `getIntroduction_1` | 없음 | `BaseResponseObject` |
| `PUT` | `/admin/introductions` | `updateIntroduction` | `IntroRequest` multipart | `BaseResponseObject` |

## 구현 메모

- API 함수는 `src/api/introductions.ts`에 둡니다.
- query key는 `['admin', 'introductions']`를 우선합니다.
- 수정은 `multipart/form-data`입니다.
- 수정 이후 소개 조회 query를 invalidate합니다.

## Request Fields

### `IntroRequest`

| Field | Type |
| --- | --- |
| `bannerCardinal` | `number` |
| `bannerStatus` | `string` |
| `slogan` | `string` |
| `bannerImageFile` | `File` |
| `memberCount` | `number` |
| `projectCount` | `number` |
| `universityCount` | `number` |
| `introYoutubeLink` | `string` |
| `planningImage` | `File` |
| `designImage` | `File` |
| `frontendImage` | `File` |
| `backendImage` | `File` |
| `teams` | `ManagementTeamRequest[]` |
| `expertLecture` | `ExpertLectureRequest[]` |
| `obLecture` | `OBLectureRequest[]` |
| `partnerLogoFiles` | `string[]` 또는 파일 배열, Swagger schema 재확인 필요 |
| `meetupImages` | `string[]` 또는 파일 배열, Swagger schema 재확인 필요 |
| `activities` | `ActivityRequest[]` |
| `sponsors` | `string[]` 또는 파일 배열, Swagger schema 재확인 필요 |
Loading
Loading