Skip to content

Commit 74e357d

Browse files
authored
[공통] SonarCloud 연동 및 Claude Code AI 파이프라인 하네스 구성 (#1236)
1 parent 74dfd13 commit 74e357d

18 files changed

Lines changed: 1575 additions & 189 deletions

File tree

.claude/CLAUDE.md

Lines changed: 51 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,213 +1,75 @@
11
# CLAUDE.md
22

3-
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3+
## 프로젝트 개요
44

5-
## Project Overview
5+
KOIN은 한국기술교육대학교(KOREATECH) 재학생을 위한 캠퍼스 서비스 웹앱이다. 시간표, 버스, 식당, 가게, 커뮤니티, 동아리, 분실물, 졸업 계산기를 제공한다.
66

7-
KOIN is a campus service web application for Korea University of Technology and Education (KOREATECH). It provides timetable management, bus schedules, cafeteria menus, store/shop listings, community articles, clubs, lost & found, and graduation calculators.
7+
**기술 스택:** Next.js 15 (Pages Router) · React 19 · TypeScript strict · Yarn 4 (Berry) PnP · Node 20.11.1
88

9-
Built with **Next.js 15 (Pages Router)**, **React 19**, and **TypeScript** (strict mode).
10-
11-
- Package manager: **Yarn 4 (Berry)** with PnP. Never use `npm install`.
12-
- Node version: **20.11.1**
13-
14-
## Commands
9+
## 주요 명령어
1510

1611
```bash
17-
yarn start # Dev server (next dev)
18-
yarn build # Type-check (tsc) then production build
12+
yarn start # 개발 서버 (next dev)
13+
yarn build # 타입 체크(tsc) + 프로덕션 빌드
1914
yarn lint # ESLint + Stylelint
20-
yarn lint:eslint # ESLint only (src/)
21-
yarn lint:stylelint # Stylelint only (src/**/*.scss)
22-
yarn log # Generate analytics logging hooks from Notion spec
23-
```
24-
25-
## Architecture
26-
27-
### Routing (Pages Router)
28-
29-
File-based routing in `src/pages/`. Type-safe route builder in `src/static/routes.ts`:
30-
31-
```typescript
32-
ROUTES.StoreDetail({ id: '123' }); // → '/store/123'
33-
```
34-
35-
Pages can declare static properties:
36-
37-
```typescript
38-
Page.getLayout = (page: React.ReactNode) => <SSRLayout>{page}</SSRLayout>;
39-
Page.requireAuth = true;
40-
Page.title = 'Page Title';
41-
```
42-
43-
### API Layer
44-
45-
`src/api/` uses a class-based pattern with `APIClient` wrapper (`src/utils/ts/apiClient.ts`):
46-
47-
1. Define request class in `APIDetail.ts` implementing `APIRequest<ResponseType>`
48-
2. Define request/response types in `entity.ts`
49-
3. Export callable function via `APIClient.of(DetailClass)` in `index.ts`
50-
51-
```typescript
52-
export class Login implements APIRequest<LoginResponse> {
53-
path = '/user/login';
54-
method = HTTP_METHOD.POST;
55-
data: LoginRequest;
56-
}
57-
// index.ts
58-
export const login = APIClient.of(Login);
15+
yarn lint:eslint # ESLint (src/)
16+
yarn lint:stylelint # Stylelint (src/**/*.scss)
17+
yarn log # Notion 스펙에서 분석 로깅 훅 생성
5918
```
6019

61-
The APIClient handles token refresh (401), maintenance mode (503), and user type verification (403) automatically.
62-
63-
### State Management
64-
65-
- **Server state**: TanStack React Query (`staleTime: 60000`, `retry: false`). SSR via `getServerSideProps` + `HydrationBoundary`.
66-
- **Client state**: Zustand stores in `src/utils/zustand/`. Many stores separate `State` and `Actions` types — follow the existing pattern of each file.
67-
68-
Zustand stores export selectors:
69-
70-
```typescript
71-
export const useStateSelector = () => useStore((state) => state.prop);
72-
export const useActions = () => useStore((state) => state.action);
73-
```
74-
75-
### Styling
76-
77-
SCSS with CSS Modules (`[Component].module.scss`) using BEM methodology. Desktop-first approach with mobile overrides via responsive mixins in `src/utils/scss/`:
78-
79-
```scss
80-
@include media.media-breakpoint(mobile) {
81-
/* breakpoint: 576px */
82-
}
83-
```
84-
85-
### Component Organization
86-
87-
Feature-based: `src/components/[Feature]/` with co-located hooks in `hooks/` subdirectory and styles. Split responsive views into `MobileView/` and `PCView/` directories when layouts differ. Shared UI in `src/components/ui/`, layouts in `src/components/layout/`, modals in `src/components/modal/`.
88-
89-
### Layout
90-
91-
Two layout components in `src/components/layout/`:
92-
93-
- **`SSRLayout`**: No Suspense wrapping. Used for SSR pages via `getLayout`.
94-
- **`Layout`** (default): Wraps Header/children in Suspense boundaries. Hides Footer in native WebView.
20+
**패키지 매니저:** Yarn 4만 사용. `npm install` 금지.
9521

96-
Misusing these causes hydration errors.
22+
## 아키텍처 핵심 포인터
9723

98-
### Custom Hooks
99-
100-
Located in `src/utils/hooks/`, organized by category:
101-
102-
- `auth/` — useAuth, useAutoLogin, useLoginRedirect, useLogout
103-
- `ui/` — useBodyScrollLock, useOutsideClick, useEscapeKeyDown
104-
- `state/` — useBooleanState, useLocalStorage, useWebStorage, useMount
105-
- `layout/` — useMediaQuery, useModalPortal
106-
- `analytics/` — useLogger, useScrollLogging
107-
108-
### Internal Packages
109-
110-
- **`@bcsdlab/koin`**: `isKoinError()` type guard, `sendClientError()` (sends errors to internal Slack).
111-
- **`@bcsdlab/utils`**: `cn()` (className merger), `sha256()` (Web Crypto hashing).
112-
113-
These are internal BCSD Lab packages — do not suggest replacing them with external alternatives.
114-
115-
### Cookie Management
116-
117-
Cookie keys are environment-aware via `IS_STAGE` flag in `src/static/url.ts`. Stage and production use different cookie key names (e.g., `STAGE_AUTH_TOKEN_KEY` vs `AUTH_TOKEN_KEY`) and different domains (`.stage.koreatech.in` vs `.koreatech.in`). Always use `COOKIE_KEY` constants and `getCookieDomain()` — never hard-code cookie names or domains.
118-
119-
### iOS Native Bridge
120-
121-
WebKit message handlers for token sync between web and native app via `window.webkit.messageHandlers`. Bridge functions in `src/utils/ts/iosBridge.ts`.
122-
123-
### Analytics
124-
125-
Generated logging hooks in `src/generated/analytics/` from Notion spec via `yarn log`. Google Analytics + GTM + Sentry error tracking.
126-
127-
## Code Conventions
128-
129-
### Imports
130-
131-
Absolute imports via `*``src/*` path mapping. Use `import X from 'components/...'` not `'../../../components/...'`. Relative parent imports (`../*`) are forbidden by ESLint.
132-
133-
Import order (enforced): React/Next → builtins → external packages → internal (`@/**`) → parent/sibling → types → styles (`.scss`).
134-
135-
### Naming
136-
137-
- Components/directories: PascalCase (`ArticleList.tsx`)
138-
- Utilities: camelCase (`apiClient.ts`)
139-
- Hooks: `use[Name].ts`
140-
- Styles: `[Component].module.scss`
141-
- API types: `entity.ts`, API classes: `APIDetail.ts`
142-
143-
### Formatting
144-
145-
Prettier: 120 char width, single quotes, trailing commas, 2-space indent. Stylelint enforces `stylelint-config-standard-scss`.
146-
147-
## PR Review Rules (for claude-code-action)
148-
149-
Write all review comments in Korean.
150-
151-
Focus on correctness, regression risk, security, and performance before style.
152-
153-
Use this output format for every finding:
154-
155-
- Severity: `[P0]` (blocks merge), `[P1]` (should fix), `[P2]` (suggestion)
156-
- Location: `file:line`
157-
- Why it matters
158-
- Minimal fix suggestion
159-
160-
### Error Handling
161-
162-
- Always use `isKoinError()` type guard before accessing error properties on API errors.
163-
- In ErrorBoundary, use `isAxiosError()` type guard to branch handling.
164-
165-
```typescript
166-
onError: (error) => {
167-
if (isKoinError(error)) {
168-
showToast('error', error.message || 'fallback message');
169-
} else {
170-
showToast('error', 'fallback message');
171-
}
172-
};
173-
```
24+
| 영역 | 패턴 요약 | 위치 |
25+
|------|-----------|------|
26+
| 라우팅 | Pages Router, `ROUTES` 헬퍼 사용 | `src/static/routes.ts` |
27+
| API | `APIRequest<T>` 클래스 → `APIClient.of()` export | `src/api/[domain]/` |
28+
| 서버 상태 | React Query (`staleTime: 60000, retry: false`) | `src/api/[domain]/queries.ts` |
29+
| 클라이언트 상태 | Zustand (`State`/`Actions` 타입 분리) | `src/utils/zustand/` |
30+
| 스타일 | SCSS Modules + BEM, 데스크톱 우선 | `[Component].module.scss` |
31+
| 레이아웃 | SSR 페이지 → `SSRLayout`, 클라이언트 → `Layout` | `src/components/layout/` |
32+
| 내부 패키지 | `@bcsdlab/koin` (isKoinError, sendClientError), `@bcsdlab/utils` (cn, sha256) | 교체 금지 |
17433

175-
### React Query
34+
## 필수 준수 규칙
17635

177-
- Query keys as arrays: `['resource', 'action', params]`.
178-
- Prefer `useSuspenseQuery` when a Suspense boundary wraps the component for blocking UI. Use `useQuery` for conditional fetching (`enabled`), background refresh, or non-blocking patterns.
179-
- Invalidate cache via `queryClient.invalidateQueries()` after mutations.
180-
- Every mutation `onError` must follow the error handling pattern above.
36+
1. **임포트:** 절대 경로 사용 (`import X from 'components/...'`). 상위 경로(`../`) ESLint 금지.
37+
2. **에러 핸들링:** mutation `onError`에 반드시 `isKoinError()` 타입 가드 → `showToast()` 패턴 적용.
38+
3. **쿠키:** `COOKIE_KEY` 상수 + `getCookieDomain()` 사용. 쿠키명/도메인 하드코딩 금지.
39+
4. **SSR 안전성:** `window`/`document`/`localStorage` 접근 시 `typeof window !== 'undefined'` 체크 필수.
40+
5. **라우팅:** `ROUTES` 헬퍼 사용. 경로 문자열 하드코딩 금지.
41+
6. **로깅:** `console.log` 금지. `console.warn`/`console.error`만 허용.
42+
7. **토스트:** `showToast(type, message)` 사용. `toast()` 직접 호출 금지.
43+
8. **iOS 브릿지:** `window.webkit` optional chaining 유지 (`src/utils/ts/iosBridge.ts`).
44+
9. **분석 로깅:** 주요 사용자 인터랙션마다 `useLogger()` 훅 포함.
18145

182-
### Analytics Logging
46+
## PR 리뷰 규칙 (claude-code-action용)
18347

184-
- All user interactions (click, swipe, page load) must include logging.
185-
- Use the `useLogger()` hook with `team`, `event_label`, `value` structure.
186-
- Define logging constants (`loggingTitle`, `loggingValue`) at the top of the component.
48+
리뷰 댓글은 **한국어**로 작성. 정확성 > 회귀 위험 > 보안 > 성능 > 스타일 순으로 검토.
18749

188-
### General
50+
**출력 형식 (모든 발견에 적용):**
51+
- 심각도: `[P0]` (머지 차단) · `[P1]` (수정 필요) · `[P2]` (제안)
52+
- 위치: `file:line`
53+
- 영향과 최소 수정안
18954

190-
- No `console.log` (ESLint warn). Only `console.warn` and `console.error` allowed.
191-
- Import SVGs as React components via `@svgr/webpack`.
192-
- Use `showToast(type, message)` utility instead of calling `toast()` directly. Type: `'success' | 'error' | 'info' | 'warning' | 'default'`.
55+
**검토 제외:**
56+
- lint/import-order 포매팅 노이즈
57+
- 자동 생성 파일만 변경 (`src/generated/**`, `analytics.events.json`)
58+
- `@bcsdlab/koin`, `@bcsdlab/utils` 교체 제안
19359

194-
### Project-Specific Must-Checks
60+
## 유효성 검사 정책
19561

196-
- **SSR safety**: Guard `window`, `document`, `localStorage`, `sessionStorage` with browser checks. Verify correct layout usage (`SSRLayout` for SSR pages, `Layout` for client pages).
197-
- **React Query SSR hydration**: Verify prefetch query keys and hydration state keys are consistent.
198-
- **Auth/API stability**: Do not break token refresh lock, 401/403/503 handling, or retry flow.
199-
- **Cookie safety**: Must use `COOKIE_KEY` constants and `getCookieDomain()` — never hard-code cookie names or domains. Verify stage/production separation is preserved.
200-
- **iOS bridge stability**: Preserve `window.webkit` optional chaining and native callback contract.
201-
- **Routing consistency**: Prefer `ROUTES` helpers over hard-coded path strings.
202-
- **Next.js performance**: Flag async waterfalls, unnecessary heavy static imports, and missed dynamic imports.
62+
- 기본: `yarn lint`
63+
- `yarn build` 실패는 환경 제약(API 접근 불가 등)이 원인일 수 있으므로, 변경 코드가 직접 원인이 아니면 비차단 처리.
20364

204-
### Validation Policy
65+
## 하네스: KOIN AI 파이프라인
20566

206-
- Primary check: `yarn lint`.
207-
- `yarn build` may fail due to environment constraints (missing API access, sandbox limitations); treat as non-blocking unless the changed code directly caused the failure.
67+
**목표:** SonarCloud 코드 품질 분석 및 자동 수정, 기능 구현 자동화 PR 생성
20868

209-
### Do Not Review
69+
**트리거:** "SonarCloud 분석", "SonarCloud 이슈 수정", "코드 품질 개선", "기능 구현해줘", "새 기능 추가해줘", "PR 만들어줘", "파이프라인 실행", "다시 실행", "재실행", "이전 결과 개선", "업데이트" 등의 요청 시 `koin-pipeline` 스킬을 사용하라.
21070

211-
- Pure formatting/import-order noise already covered by lint.
212-
- Generated artifacts only (`analytics.events.json`, `src/generated/**`) unless generation logic changed.
213-
- `@bcsdlab/koin` and `@bcsdlab/utils` are internal packages — do not suggest external replacements.
71+
**변경 이력:**
72+
| 날짜 | 변경 내용 | 대상 | 사유 |
73+
|------|----------|------|------|
74+
| 2026-04-21 | 초기 구성 | 전체 | SonarCloud 품질 관리 + 기능 구현 자동화 파이프라인 |
75+
| 2026-04-21 | 전체 재작성 | CLAUDE.md | 영어→한국어, 스킬 중복 제거, 분량 축소 (224줄→80줄) |
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
name: code-quality-fixer
3+
description: SonarCloud 이슈 분석 결과를 바탕으로 KOIN 프로젝트의 코드 품질 이슈를 실제로 수정하는 에이전트. TypeScript 타입 오류, React 패턴 개선, 보안 이슈를 KOIN 컨벤션에 맞게 수정한다.
4+
model: sonnet
5+
---
6+
7+
# Code Quality Fixer
8+
9+
## 핵심 역할
10+
11+
sonarcloud-analyzer가 생성한 이슈 배치를 처리하여, KOIN 프로젝트 컨벤션에 맞게 코드를 수정한다. `code-quality-fix` 스킬의 가이드를 따른다.
12+
13+
## 작업 원칙
14+
15+
1. 반드시 `code-quality-fix` 스킬을 로딩하여 KOIN 컨벤션과 수정 패턴을 확인한다.
16+
2. 수정 전 대상 파일을 먼저 읽고, 변경 범위를 최소화한다.
17+
3. 수정은 이슈 단위로 진행하며, 각 수정 후 변경 내용을 기록한다.
18+
4. `yarn lint:eslint`가 통과하는 수정만 최종 반영한다.
19+
5. 기존 기능을 깨지 않는 수정만 한다 — 비즈니스 로직 변경 금지.
20+
6. KOIN 프로젝트 특수 규칙을 반드시 지킨다:
21+
- `isKoinError()` 타입 가드 사용
22+
- `COOKIE_KEY` 상수 사용 (하드코딩 금지)
23+
- `ROUTES` 헬퍼 사용 (경로 하드코딩 금지)
24+
- SSR 안전성 보장 (`window`, `localStorage` 접근 시 브라우저 체크)
25+
26+
## 입력
27+
28+
- `_workspace/01_analyzer_issues.json` (sonarcloud-analyzer 출력)
29+
- 처리할 배치 번호 (없으면 배치 1부터 순차 처리)
30+
31+
## 출력
32+
33+
`_workspace/02_fixer_result.json` 파일:
34+
```json
35+
{
36+
"fixed": [
37+
{
38+
"file": "src/components/...",
39+
"issue_key": "...",
40+
"description": "수정 내용 요약"
41+
}
42+
],
43+
"failed": [],
44+
"skipped": [],
45+
"changed_files": ["src/components/...", "..."]
46+
}
47+
```
48+
49+
## 에러 핸들링
50+
51+
- 파일을 찾을 수 없을 때: skipped 목록에 추가, 계속 진행
52+
- 수정 후 lint 실패 시: 원본으로 되돌리고 failed 목록에 추가
53+
- 타입 에러 발생 시: 수정 취소하고 수동 검토 필요 표시
54+
55+
## 협업
56+
57+
- **입력 출처**: sonarcloud-analyzer (`_workspace/01_analyzer_issues.json`)
58+
- **출력 대상**: pr-creator (`_workspace/02_fixer_result.json` + 실제 수정된 파일)

0 commit comments

Comments
 (0)