Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:31:31 +08:00
commit 7e99b9e13d
5 changed files with 744 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"name": "code-skills",
"description": "code skills with claude",
"version": "1.0.0",
"author": {
"name": "hynu",
"email": "khw1031@gmail.com"
},
"skills": [
"./skills"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# code-skills
code skills with claude

49
plugin.lock.json Normal file
View File

@@ -0,0 +1,49 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:khw1031/claude-marketplace:plugins/code-skills",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "737a96141b7e3e614db81873f0315085e7e9d7a6",
"treeHash": "251fc79965b5e16602a05aecce7853a5eb752bc5a1e642915967c2e419042314",
"generatedAt": "2025-11-28T10:19:29.947694Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "code-skills",
"description": "code skills with claude",
"version": "1.0.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "546ac5a4ece241a3848a5d028340250bb2f1b4671c41a538668670d78d2cc67f"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "8411918a1753761fb3d02c1ea218d34e6761afc4e9efae71fa66d3f3a01d02fb"
},
{
"path": "skills/review-react/SKILL.md",
"sha256": "4cc5f92f0abc2929b8cc302e39f739d343d9e7f86badafe4c74ff0a83bd9964c"
},
{
"path": "skills/review-react/references/typescript.md",
"sha256": "c7e375937c423fa3223aa01ac01f59b2e47d29ced34fd222395dace5b7785aef"
}
],
"dirSha256": "251fc79965b5e16602a05aecce7853a5eb752bc5a1e642915967c2e419042314"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,190 @@
---
name: review-react
description: Expert-level frontend code review specialist for production-grade TypeScript/React applications. Use this skill when reviewing pull requests, performing code audits, or analyzing frontend codebases for type safety, performance, security, and maintainability issues. Focuses on React/TypeScript stack with emphasis on runtime safety and production readiness.
---
# Frontend Code Review Expert
당신은 10년 이상의 실무 경험을 보유한 시니어 프론트엔드 개발자입니다. TypeScript, React, 성능 최적화, 접근성, 보안에 대한 깊은 이해를 바탕으로 코드 리뷰를 수행합니다.
## 핵심 검토 우선순위
1. **타입 안전성** - 런타임 에러 방지가 최우선
2. **코드 아키텍처** - 유지보수성과 확장성
3. **성능 최적화** - 불필요한 리렌더링 방지
4. **보안 취약점** - XSS, 주입 공격 방지
5. **접근성** - WCAG 2.1 AA 준수
## 필수 참조 문서
코드 리뷰 수행 전 다음 문서들을 반드시 확인하세요:
- `./references/typescript.md` - TypeScript 코딩 규칙 및 팀 컨벤션
- `./references/react-patterns.md` - React 패턴 및 베스트 프랙티스 (존재 시)
- `./references/security-guidelines.md` - 보안 체크리스트 (존재 시)
**중요**: 참조 문서가 없는 경우, 업계 표준 베스트 프랙티스를 적용하되 반드시 그 사실을 리뷰 결과에 명시하세요.
## 리뷰 프로세스
### 1단계: 구조 분석 (먼저 실행)
변경된 파일들의 전체 구조를 파악:
- 파일/모듈 구조의 적절성
- 관심사의 분리 (Separation of Concerns)
- 의존성 관리 및 순환 참조 여부
### 2단계: 타입 안전성 검증
**Critical 체크리스트**:
- [ ] any 타입 사용 여부 - 대안 제시 필수
- [ ] Non-null assertion 연산자 남용 - 안전한 체크로 대체
- [ ] 타입 단언(as) 남용 - 타입 가드로 개선
- [ ] Optional chaining 누락 - 런타임 에러 가능성
- [ ] Enum vs Union Type 선택 적절성
참조: `./references/typescript.md`의 타입 정의 규칙 준수 여부
### 3단계: React 패턴 분석
**검토 항목**:
- 불필요한 리렌더링 (React DevTools Profiler 추천)
- 메모이제이션 필요 여부 (useMemo, useCallback, React.memo)
- Effect 의존성 배열 정확성 - 무한 루프 위험
- 커스텀 훅 추출 가능성 - 재사용성
- Props drilling vs Context 사용 적절성
### 4단계: 보안 검증
**필수 체크**:
- [ ] XSS 취약점: dangerouslySetInnerHTML 사용 시 sanitization
- [ ] 사용자 입력 검증: Zod/Yup 스키마 적용 여부
- [ ] 민감 정보 노출: API 키, 토큰 하드코딩 확인
- [ ] CSRF 방어: 상태 변경 API의 토큰 검증
- [ ] 안전하지 않은 의존성: npm audit 권장
### 5단계: 성능 및 최적화
- 번들 사이즈 영향도 (대용량 라이브러리 import)
- 비동기 로직 최적화 (병렬 처리 가능 여부)
- 이미지/에셋 최적화 (lazy loading, WebP 사용)
- Code splitting 적용 가능성
## 리뷰 결과 형식
각 발견사항은 다음 템플릿을 사용하세요:
[심각도] 카테고리: 문제 요약
위치: 파일명:라인번호
현재 코드:
[코드 블록]
문제점:
- 참조 문서 규칙 위반 여부 (규칙명 명시)
- 구체적인 문제 설명
- 발생 가능한 부작용 (런타임 에러, 성능 저하 등)
개선 방안:
[수정된 코드 예시]
우선순위: 높음 / 중간 / 낮음
**심각도 분류**:
- 🔴 **Critical**: 즉시 수정 필요 (보안, 치명적 버그, 런타임 에러)
- 🟡 **Warning**: 개선 권장 (성능, 유지보수성, 타입 안전성)
- 🔵 **Suggestion**: 선택적 개선 (코드 스타일, 마이너 최적화)
## 리뷰 완료 체크리스트
리뷰 마무리 전 다음을 확인:
- [ ] typescript.md 규칙 준수율 산출
- [ ] Critical 이슈가 있다면 Top 3 우선순위 명시
- [ ] 각 지적사항에 "왜" 문제인지 명확히 설명
- [ ] 구체적인 코드 예시 제공 (가능한 경우)
- [ ] 장기 개선 제안 (아키텍처 레벨) 포함
## 최종 요약 형식
```markdown
## 종합 평가
### 전체 평가
[코드의 전반적인 품질 평가 - 객관적이고 건설적인 톤]
### 주요 발견사항
- Critical: X건
- Warning: Y건
- Suggestion: Z건
### 참조 문서 준수율
- typescript.md: X% 준수 (위반 항목: [...])
### 우선 조치 항목 (Top 3)
1. [가장 심각한 이슈 - 즉시 수정 필요 이유]
2. [두 번째 우선순위]
3. [세 번째 우선순위]
### 장기 개선 제안
- [아키텍처/패턴 레벨의 개선 방향]
- [팀 전체에 적용 가능한 베스트 프랙티스]
추가 지침
톤 및 스타일
- 비판적이되 건설적인 피드백
- "이렇게 하면 안 됩니다" 대신 "이렇게 하면 더 안전합니다" 표현
- 주니어 개발자도 이해할 수 있도록 설명
컨텍스트 관리
- 500줄 이상의 대규모 변경은 파일별로 분할 리뷰
- 관련 파일들을 그룹핑하여 순차적으로 검토
코드 예시 작성 시
- 변경 전/후 비교를 명확히
- 주석으로 핵심 개선 포인트 표시
- 실행 가능한 코드 제공 (컴파일 에러 없이)
기술 스택별 특화 규칙
React + TypeScript
- Functional Component 우선, Class Component 지양
- Props 인터페이스는 컴포넌트와 같은 파일에 정의
- Generics 활용하여 타입 재사용성 향상
상태 관리
- Local state vs Global state 선택 기준 명확히
- Zustand/Jotai: atom 분리 원칙
- React Query: stale time, cache time 설정 적절성
자동화 도구 추천
리뷰 완료 후 다음 도구 실행을 권장하세요:
- eslint --fix - 자동 수정 가능한 이슈
- prettier --write - 코드 포맷팅
- tsc --noEmit - 타입 체크
- npm audit - 보안 취약점 스캔
---
참고: 이 스킬은 코드 리뷰에 집중합니다. 코드 작성이나 기능 구현이 필요한 경우,
해당 작업을 먼저 완료하고 별도의 리뷰 요청을 하세요.

View File

@@ -0,0 +1,490 @@
# default exports
프레임워크에서 명시적으로 요구하지 않는 한, default exports를 사용하지 마세요.
```ts
// BAD
export default function myFunction() {
return <div>Hello</div>;
}
```
```ts
// GOOD
export function myFunction() {
return <div>Hello</div>;
}
```
Default exports는 가져오는 파일에서 혼란을 야기합니다.
```ts
// BAD
import myFunction from "./myFunction";
```
```ts
// GOOD
import { myFunction } from "./myFunction";
```
프레임워크가 default export를 요구하는 특정 상황이 있습니다. 예를 들어, Next.js, Expo Router는 페이지 또는 스크린에 대해 default export를 요구합니다.
```tsx
// This is fine, if required by the framework
export default function MyPage() {
return <div>Hello</div>;
}
```
# any inside generic functions
제네릭 함수를 빌드할 때, 함수 본문 내부에서 any를 사용해야 할 수도 있습니다.
이는 TypeScript가 종종 런타임 로직을 타입 수준 로직과 일치시키지 못하기 때문입니다.
One example:
```ts
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye",
>(
input: TInput,
): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello"; // Error!
} else {
return "goodbye"; // Error!
}
};
```
타입 레벨(그리고 런타임)에서, 이 함수는 입력이 `hello`일 때 `goodbye`를 반환합니다.
TypeScript에서 이를 간결하게 작동시킬 방법이 없습니다.
따라서 `any`를 사용하는 것이 가장 간결한 해결책입니다:
```ts
const youSayGoodbyeISayHello = <
TInput extends "hello" | "goodbye",
>(
input: TInput,
): TInput extends "hello" ? "goodbye" : "hello" => {
if (input === "goodbye") {
return "hello" as any;
} else {
return "goodbye" as any;
}
};
```
제네릭 함수 외부에서는 `any`를 극히 드물게 사용하세요.
# discriminated unions
데이터가 여러 다른 형태 중 하나일 수 있는 경우를 모델링하기 위해 적극적으로 판별 유니온(discriminated unions)을 사용하세요.
예를 들어, 환경 간에 이벤트를 전송할 때:
```ts
type UserCreatedEvent = {
type: "user.created";
data: { id: string; email: string };
};
type UserDeletedEvent = {
type: "user.deleted";
data: { id: string };
};
type Event = UserCreatedEvent | UserDeletedEvent;
```
판별 유니온(discriminated unions)의 결과를 처리하기 위해 switch 문을 사용하세요:
```ts
const handleEvent = (event: Event) => {
switch (event.type) {
case "user.created":
console.log(event.data.email);
break;
case "user.deleted":
console.log(event.data.id);
break;
}
};
```
'선택적 속성들의 모음' 문제를 방지하기 위해 판별 유니온(discriminated unions)을 사용하세요.
예를 들어, 데이터 가져오기 상태를 설명할 때:
```ts
// BAD - allows impossible states
type FetchingState<TData> = {
status: "idle" | "loading" | "success" | "error";
data?: TData;
error?: Error;
};
// GOOD - prevents impossible states
type FetchingState<TData> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: TData }
| { status: "error"; error: Error };
```
# enums
코드베이스에 새로운 enum을 도입하지 마세요. 기존 enum은 유지하세요.
enum과 유사한 동작이 필요한 경우, `as const` 객체를 사용하세요:
```ts
const backendToFrontendEnum = {
xs: "EXTRA_SMALL",
sm: "SMALL",
md: "MEDIUM",
} as const;
type LowerCaseEnum = keyof typeof backendToFrontendEnum; // "xs" | "sm" | "md"
type UpperCaseEnum =
(typeof backendToFrontendEnum)[LowerCaseEnum]; // "EXTRA_SMALL" | "SMALL" | "MEDIUM"
```
숫자형 열거형(numeric enums)은 문자열 열거형(string enums)과 다르게 동작한다는 점을 기억하세요. 숫자형 열거형은 역방향 매핑(reverse mapping)을 생성합니다:
```ts
enum Direction {
Up,
Down,
Left,
Right,
}
const direction = Direction.Up; // 0
const directionName = Direction[0]; // "Up"
```
이는 위의 `Direction` enum이 네 개가 아닌 여덟 개의 키를 가지게 된다는 것을 의미합니다.
```ts
enum Direction {
Up,
Down,
Left,
Right,
}
Object.keys(Direction).length; // 8
```
# import type
타입을 가져올 때는 항상 import type을 사용하세요.
인라인 `import { type ... }` 보다 최상위 레벨 `import type`을 선호하세요.
```ts
// BAD
import { type User } from "./user";
```
```ts
// GOOD
import type { User } from "./user";
```
이러한 이유는 특정 환경에서는 첫 번째 버전의 import가 제거되지 않기 때문입니다. 따라서 다음과 같이 남게 됩니다:
```ts
// Before transpilation
import { type User } from "./user";
// After transpilation
import "./user";
```
# installing libraries
라이브러리를 설치할 때, 자신의 학습 데이터에 의존하지 마세요.
당신의 학습 데이터는 특정 시점에 끊겨 있습니다. 당신은 아마도 JavaScript와 TypeScript 세계의 최신 개발 사항을 모두 알지 못할 것입니다.
이는 수동으로 버전을 선택하는 대신(`package.json` 파일을 업데이트하는 방식으로), 라이브러리의 최신 버전을 설치하기 위해 스크립트를 사용해야 한다는 것을 의미합니다.
```bash
# pnpm
pnpm add -D @typescript-eslint/eslint-plugin
# yarn
yarn add -D @typescript-eslint/eslint-plugin
# npm
npm install --save-dev @typescript-eslint/eslint-plugin
```
이렇게 하면 항상 최신 버전을 사용할 수 있습니다.
# interface extends
상속을 모델링할 때는 항상 interface를 선호하세요.
TypeScript에서 `&` 연산자는 성능이 매우 좋지 않습니다. `interface extends`가 불가능한 경우에만 사용하세요.
```ts
// BAD
type A = {
a: string;
};
type B = {
b: string;
};
type C = A & B;
```
```ts
// GOOD
interface A {
a: string;
}
interface B {
b: string;
}
interface C extends A, B {
// Additional properties can be added here
}
```
# jsdoc comments
함수와 타입을 주석 처리하기 위해 JSDoc 주석을 사용하세요.
JSDoc 주석에서는 간결하게 작성하고, 함수의 동작이 명확하지 않은 경우에만 JSDoc 주석을 제공하세요.
같은 파일 내의 다른 함수와 타입에 링크하려면 JSDoc 인라인 `@link` 태그를 사용하세요.
```ts
/**
* Subtracts two numbers
*/
const subtract = (a: number, b: number) => a - b;
/**
* Does the opposite to {@link subtract}
*/
const add = (a: number, b: number) => a + b;
```
# naming conventions
- 파일 이름에는 kebab-case를 사용하세요 (예: `my-component.ts`)
- 변수와 함수 이름에는 camelCase를 사용하세요 (예: `myVariable`, `myFunction()`)
- 클래스, 타입, 인터페이스에는 UpperCamelCase(PascalCase)를 사용하세요 (예: `MyClass`, `MyInterface`)
- 상수와 enum 값에는 ALL_CAPS를 사용하세요 (예: `MAX_COUNT`, `Color.RED`)
- 제네릭 타입, 함수 또는 클래스 내부에서는 타입 매개변수에 `T` 접두사를 붙이세요 (예: `TKey`, `TValue`)
```ts
type RecordOfArrays<TItem> = Record<string, TItem[]>;
```
# noUncheckedIndexedAccess
사용자가 `tsconfig.json`에서 이 규칙을 활성화한 경우, 객체와 배열의 인덱싱은 예상과 다르게 동작할 수 있습니다.
```ts
const obj: Record<string, string> = {};
// With noUncheckedIndexedAccess, value will
// be `string | undefined`
// Without it, value will be `string`
const value = obj.key;
```
```ts
const arr: string[] = [];
// With noUncheckedIndexedAccess, value will
// be `string | undefined`
// Without it, value will be `string`
const value = arr[0];
```
# optional properties
선택적 속성(optional properties)은 극히 드물게 사용하세요. 속성이 진정으로 선택적인 경우에만 사용하고, 속성을 전달하지 않아 발생할 수 있는 버그를 고려하세요.
아래 예시에서는 항상 사용자 ID를 `AuthOptions`에 전달하고자 합니다. 이는 코드베이스의 어딘가에서 이를 전달하는 것을 잊어버리면 함수가 인증되지 않는 상태가 될 수 있기 때문입니다.
```ts
// BAD
type AuthOptions = {
userId?: string;
};
const func = (options: AuthOptions) => {
const userId = options.userId;
};
```
```ts
// GOOD
type AuthOptions = {
userId: string | undefined;
};
const func = (options: AuthOptions) => {
const userId = options.userId;
};
```
# readonly properties
기본적으로 객체 타입에 `readonly` 속성을 사용하세요. 이는 런타임에 우발적인 변경을 방지합니다.
속성이 진정으로 변경 가능한 경우에만 `readonly`를 생략하세요.
```ts
// BAD
type User = {
id: string;
};
const user: User = {
id: "1",
};
user.id = "2";
```
```ts
// GOOD
type User = {
readonly id: string;
};
const user: User = {
id: "1",
};
user.id = "2"; // Error
```
# return types
모듈의 최상위 레벨에서 함수를 선언할 때, 반환 타입을 명시하세요. 이는 미래의 AI 어시스턴트가 함수의 목적을 이해하는 데 도움이 됩니다.
```ts
const myFunc = (): string => {
return "hello";
};
```
이에 대한 한 가지 예외는 JSX를 반환하는 컴포넌트입니다. 컴포넌트의 반환 타입을 선언할 필요가 없습니다, 항상 JSX이기 때문입니다.
```tsx
const MyComponent = () => {
return <div>Hello</div>;
};
```
# throwing
오류를 발생시키는 코드를 구현하기 전에 신중하게 생각하세요.
발생한 오류가 시스템에서 바람직한 결과를 생성한다면, 그렇게 하세요. 예를 들어, 백엔드 프레임워크의 요청 핸들러 내에서 사용자 정의 오류를 발생시키는 경우가 있습니다.
그러나 수동 try catch가 필요한 코드의 경우, 대신 결과 타입(result type)을 사용하는 것을 고려하세요:
```ts
type Result<T, E extends Error> =
| { ok: true; value: T }
| { ok: false; error: E };
```
예를 들어, JSON을 파싱할 때:
```ts
const parseJson = (
input: string,
): Result<unknown, Error> => {
try {
return { ok: true, value: JSON.parse(input) };
} catch (error) {
return { ok: false, error: error as Error };
}
};
```
이런 방식으로 호출자에서 오류를 처리할 수 있습니다:
```ts
const result = parseJson('{"name": "John"}');
if (result.ok) {
console.log(result.value);
} else {
console.error(result.error);
}
```
# 타입 네이밍 규칙
타입 네이밍은 단수형을 사용해주세요.
```ts
// ❌ 잘못된 방식
type Routes = "user" | "admin/user" | "admin";
function goToRoute(route: Routes) { // route는 하나인데 Routes는 복수형
// ...
}
// 올바른 방식
type Route = "user" | "admin/user" | "admin";
function goToRoute(route: Route) {
// ...
}
// 배열이 필요한 경우
type RouteArray = Route[];
// 또는
const routes: Route[] = ["user", "admin"];
```
Union 타입도 실제로는 하나의 값만 나타내므로 단수형으로 네이밍하는 것이 올바름
변수와 타입의 케이스를 다르게 사용해주세요
```ts
// 권장 방식
type Route = "user" | "admin/user" | "admin";
const route: Route = "user"; // 변수는 camelCase, 타입은 PascalCase
// 작동하지만 비추천
type route = "user" | "admin/user" | "admin";
const route: route = "user"; // 문법 하이라이팅이 혼란스러워짐
```
문법 하이라이팅과 가독성 향상, TypeScript가 런타임과 타입 레벨을 구분할 수 있음
I, T 접두사로 interface/type 구분 금지
```ts
// ❌ 구식 Java 관례 - 비추천
interface IUser {
name: string;
}
type TOrganization = {
name: string;
}
// 권장 방식
interface User {
name: string;
}
type Organization = {
name: string;
}
```
- Java 관례의 잔재
- 호버로 interface/type 구분 가능
- 타입을 interface로 바꿀 때 이름도 변경해야 하는 번거로움