Initial commit
This commit is contained in:
837
skills/code-style-reviewer/EXAMPLES.md
Normal file
837
skills/code-style-reviewer/EXAMPLES.md
Normal file
@@ -0,0 +1,837 @@
|
||||
# Code Style Examples - 좋은 코드 vs 나쁜 코드
|
||||
|
||||
## TypeScript / JavaScript 예제
|
||||
|
||||
### 예제 1: 단일책임원칙 (SRP)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
class UserManager {
|
||||
// 여러 책임을 가짐: 사용자 데이터, 검증, 저장, 이메일 발송
|
||||
|
||||
async createUser(userData: any) {
|
||||
// 1. 검증
|
||||
if (!userData.email) throw new Error("Email required");
|
||||
if (!userData.name) throw new Error("Name required");
|
||||
if (userData.email.length < 5) throw new Error("Invalid email");
|
||||
|
||||
// 2. 데이터 변환
|
||||
const user = {
|
||||
id: Math.random().toString(),
|
||||
email: userData.email,
|
||||
name: userData.name,
|
||||
createdAt: new Date(),
|
||||
role: 'user'
|
||||
};
|
||||
|
||||
// 3. 저장
|
||||
const db = require('./db');
|
||||
db.save('users', user);
|
||||
|
||||
// 4. 이메일 발송
|
||||
const mailer = require('nodemailer');
|
||||
await mailer.send({
|
||||
to: user.email,
|
||||
subject: 'Welcome',
|
||||
html: `<h1>Welcome ${user.name}</h1>`
|
||||
});
|
||||
|
||||
// 5. 로깅
|
||||
console.log(`User created: ${user.id}`);
|
||||
const logger = require('./logger');
|
||||
logger.log('user_created', { userId: user.id, email: user.email });
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 각각의 책임을 분리
|
||||
class UserValidator {
|
||||
validate(userData: UserInput): void {
|
||||
if (!userData.email) throw new Error("Email required");
|
||||
if (!userData.name) throw new Error("Name required");
|
||||
if (!this.isValidEmail(userData.email)) throw new Error("Invalid email");
|
||||
}
|
||||
|
||||
private isValidEmail(email: string): boolean {
|
||||
return email.includes('@') && email.length >= 5;
|
||||
}
|
||||
}
|
||||
|
||||
class UserFactory {
|
||||
create(userData: UserInput): User {
|
||||
return {
|
||||
id: this.generateId(),
|
||||
email: userData.email,
|
||||
name: userData.name,
|
||||
createdAt: new Date(),
|
||||
role: 'user'
|
||||
};
|
||||
}
|
||||
|
||||
private generateId(): string {
|
||||
return Math.random().toString();
|
||||
}
|
||||
}
|
||||
|
||||
class UserRepository {
|
||||
async save(user: User): Promise<void> {
|
||||
const db = require('./db');
|
||||
await db.save('users', user);
|
||||
}
|
||||
}
|
||||
|
||||
class UserNotificationService {
|
||||
async notifyNewUser(user: User): Promise<void> {
|
||||
const mailer = require('nodemailer');
|
||||
await mailer.send({
|
||||
to: user.email,
|
||||
subject: 'Welcome',
|
||||
html: `<h1>Welcome ${user.name}</h1>`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class UserLogger {
|
||||
logCreation(user: User): void {
|
||||
const logger = require('./logger');
|
||||
logger.log('user_created', { userId: user.id, email: user.email });
|
||||
}
|
||||
}
|
||||
|
||||
class UserService {
|
||||
constructor(
|
||||
private validator: UserValidator,
|
||||
private factory: UserFactory,
|
||||
private repository: UserRepository,
|
||||
private notificationService: UserNotificationService,
|
||||
private logger: UserLogger
|
||||
) {}
|
||||
|
||||
async createUser(userData: UserInput): Promise<User> {
|
||||
this.validator.validate(userData);
|
||||
const user = this.factory.create(userData);
|
||||
await this.repository.save(user);
|
||||
await this.notificationService.notifyNewUser(user);
|
||||
this.logger.logCreation(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 예제 2: DRY (Don't Repeat Yourself)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
// 반복되는 검증 로직
|
||||
function validateUserEmail(email: string): boolean {
|
||||
if (!email) return false;
|
||||
if (email.trim().length === 0) return false;
|
||||
if (!email.includes('@')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateProductEmail(email: string): boolean {
|
||||
if (!email) return false;
|
||||
if (email.trim().length === 0) return false;
|
||||
if (!email.includes('@')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateOrganizationEmail(email: string): boolean {
|
||||
if (!email) return false;
|
||||
if (email.trim().length === 0) return false;
|
||||
if (!email.includes('@')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 반복되는 API 호출 패턴
|
||||
async function getUsersFromAPI(apiKey: string): Promise<any> {
|
||||
const response = await fetch('https://api.example.com/users', {
|
||||
headers: { 'Authorization': `Bearer ${apiKey}` }
|
||||
});
|
||||
if (!response.ok) throw new Error('API Error');
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function getProductsFromAPI(apiKey: string): Promise<any> {
|
||||
const response = await fetch('https://api.example.com/products', {
|
||||
headers: { 'Authorization': `Bearer ${apiKey}` }
|
||||
});
|
||||
if (!response.ok) throw new Error('API Error');
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function getOrdersFromAPI(apiKey: string): Promise<any> {
|
||||
const response = await fetch('https://api.example.com/orders', {
|
||||
headers: { 'Authorization': `Bearer ${apiKey}` }
|
||||
});
|
||||
if (!response.ok) throw new Error('API Error');
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 공통 검증 함수
|
||||
function isValidEmail(email: string): boolean {
|
||||
if (!email) return false;
|
||||
if (email.trim().length === 0) return false;
|
||||
return email.includes('@');
|
||||
}
|
||||
|
||||
// 모든 곳에서 재사용
|
||||
const isUserEmailValid = isValidEmail(userEmail);
|
||||
const isProductEmailValid = isValidEmail(productEmail);
|
||||
const isOrgEmailValid = isValidEmail(orgEmail);
|
||||
|
||||
// 공통 API 클라이언트
|
||||
class ApiClient {
|
||||
constructor(private apiKey: string, private baseUrl: string) {}
|
||||
|
||||
async fetch<T>(endpoint: string): Promise<T> {
|
||||
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
||||
headers: { 'Authorization': `Bearer ${this.apiKey}` }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API Error: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// 사용
|
||||
const apiClient = new ApiClient(apiKey, 'https://api.example.com');
|
||||
const users = await apiClient.fetch<User[]>('/users');
|
||||
const products = await apiClient.fetch<Product[]>('/products');
|
||||
const orders = await apiClient.fetch<Order[]>('/orders');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 예제 3: 단순화 우선 (Simplicity First)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
// 불필요한 추상화와 복잡한 로직
|
||||
interface DataProcessor {
|
||||
process(data: any[]): any[];
|
||||
}
|
||||
|
||||
class ComplexDataProcessor implements DataProcessor {
|
||||
process = (data: any[]): any[] =>
|
||||
[data]
|
||||
.reduce((acc: any, val: any) =>
|
||||
[val]
|
||||
.reduce((a: any, item: any) =>
|
||||
[item]
|
||||
.reduce((b: any, i: any) =>
|
||||
({
|
||||
...b,
|
||||
...Object.entries(i).reduce((c: any, [k, v]: any) =>
|
||||
({
|
||||
...c,
|
||||
[k]: typeof v === 'number' ? v * 2 : v
|
||||
}), {})
|
||||
}), {}), [])[0], {});
|
||||
}
|
||||
|
||||
// 깊은 중첩
|
||||
function checkPermissions(user: User): boolean {
|
||||
if (user) {
|
||||
if (user.isActive) {
|
||||
if (user.roles) {
|
||||
if (user.roles.length > 0) {
|
||||
if (user.roles.some((role: Role) => role.name === 'admin')) {
|
||||
if (user.roles[0].permissions) {
|
||||
if (user.roles[0].permissions.includes('create_user')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 단순하고 명확한 구현
|
||||
function doubleNumbers<T extends Record<string, any>>(data: T[]): T[] {
|
||||
return data.map(item => {
|
||||
const result = { ...item };
|
||||
Object.entries(result).forEach(([key, value]) => {
|
||||
if (typeof value === 'number') {
|
||||
result[key] = value * 2;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Early return으로 중첩 제거
|
||||
function hasCreateUserPermission(user: User): boolean {
|
||||
if (!user?.isActive) return false;
|
||||
if (!user.roles || user.roles.length === 0) return false;
|
||||
|
||||
const adminRole = user.roles.find(role => role.name === 'admin');
|
||||
return adminRole?.permissions?.includes('create_user') ?? false;
|
||||
}
|
||||
|
||||
// Optional chaining과 nullish coalescing
|
||||
function canCreateUser(user: User): boolean {
|
||||
return user?.isActive &&
|
||||
user?.roles?.some(r => r.name === 'admin' && r.permissions?.includes('create_user')) ?? false;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 예제 4: YAGNI (You Aren't Gonna Need It)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
// 사용되지 않는 기능들
|
||||
interface UserService {
|
||||
// 현재 사용: 필수
|
||||
getUser(id: string): Promise<User>;
|
||||
createUser(email: string, name: string): Promise<User>;
|
||||
|
||||
// 사용 안 함: "나중에 필요할 것 같아서" 추가
|
||||
archiveUser(id: string): Promise<void>;
|
||||
restoreUser(id: string): Promise<void>;
|
||||
bulkCreateUsers(users: UserInput[]): Promise<User[]>;
|
||||
exportUsersToCSV(filter?: any): Promise<string>;
|
||||
generateUserReport(startDate: Date, endDate: Date): Promise<Report>;
|
||||
predictChurn(userId: string): Promise<number>;
|
||||
}
|
||||
|
||||
// 사용되지 않는 매개변수
|
||||
function fetchUserData(
|
||||
userId: string,
|
||||
includeProfilePicture?: boolean, // 사용 안 함
|
||||
includeFollowers?: boolean, // 사용 안 함
|
||||
analyzeActivity?: boolean, // 사용 안 함
|
||||
includeFuturePreferences?: boolean // 사용 안 함
|
||||
): User {
|
||||
const user = getUser(userId);
|
||||
// userId와 기본 정보만 사용
|
||||
return user;
|
||||
}
|
||||
|
||||
// 주석 처리된 코드
|
||||
function saveData(data: any) {
|
||||
// const oldWay = JSON.stringify(data);
|
||||
// localStorage.setItem('data', oldWay);
|
||||
|
||||
// if (someLegacyCondition) {
|
||||
// sendToOldServer(data);
|
||||
// }
|
||||
|
||||
sendToNewServer(data);
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 필요한 기능만 정의
|
||||
interface UserService {
|
||||
getUser(id: string): Promise<User>;
|
||||
createUser(email: string, name: string): Promise<User>;
|
||||
// 필요하면 나중에 추가
|
||||
}
|
||||
|
||||
// 필요한 매개변수만
|
||||
function fetchUserData(userId: string): User {
|
||||
return getUser(userId);
|
||||
}
|
||||
|
||||
// 필요하면 나중에 새 함수로 추가
|
||||
function fetchUserWithFollowers(userId: string): UserWithFollowers {
|
||||
const user = getUser(userId);
|
||||
const followers = getFollowers(userId);
|
||||
return { ...user, followers };
|
||||
}
|
||||
|
||||
// 깔끔한 코드: 주석 처리 제거
|
||||
function saveData(data: any) {
|
||||
sendToNewServer(data);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 예제 5: 타입 안전성 (Type Safety)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
// any 타입 남용
|
||||
function processData(data: any): any {
|
||||
return data.map((item: any) => {
|
||||
return {
|
||||
name: item.name,
|
||||
value: item.value * 2
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 타입이 없는 함수
|
||||
function calculateTotal(items) {
|
||||
return items.reduce((sum, item) => sum + item.price, 0);
|
||||
}
|
||||
|
||||
// 암묵적 any
|
||||
function filterData(array, key, value) {
|
||||
return array.filter(item => item[key] === value);
|
||||
}
|
||||
|
||||
// 잘못된 타입 사용
|
||||
type UserData = {
|
||||
[key: string]: any // 이것도 any와 마찬가지
|
||||
};
|
||||
|
||||
const user: UserData = {
|
||||
name: 'John',
|
||||
age: 30,
|
||||
email: true // 타입 체크 불가
|
||||
};
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 명확한 타입 정의
|
||||
interface DataItem {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
function processData(data: DataItem[]): DataItem[] {
|
||||
return data.map(item => ({
|
||||
name: item.name,
|
||||
value: item.value * 2
|
||||
}));
|
||||
}
|
||||
|
||||
// 명시적 타입과 반환형
|
||||
interface CartItem {
|
||||
price: number;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
function calculateTotal(items: CartItem[]): number {
|
||||
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||
}
|
||||
|
||||
// 제네릭으로 유연성 유지
|
||||
function filterByProperty<T>(
|
||||
array: T[],
|
||||
key: keyof T,
|
||||
value: T[keyof T]
|
||||
): T[] {
|
||||
return array.filter(item => item[key] === value);
|
||||
}
|
||||
|
||||
// 명확한 타입 정의
|
||||
interface User {
|
||||
name: string;
|
||||
age: number;
|
||||
email: string;
|
||||
}
|
||||
|
||||
const user: User = {
|
||||
name: 'John',
|
||||
age: 30,
|
||||
email: 'john@example.com'
|
||||
};
|
||||
|
||||
// unknown으로 안전하게 처리
|
||||
function process(data: unknown): void {
|
||||
if (typeof data === 'string') {
|
||||
console.log(data.toUpperCase());
|
||||
} else if (typeof data === 'number') {
|
||||
console.log(data * 2);
|
||||
} else if (Array.isArray(data)) {
|
||||
console.log(data.length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 예제 6: 명명규칙 (Naming Conventions)
|
||||
|
||||
#### ❌ 나쁜 예
|
||||
```typescript
|
||||
// 의미없는 이름
|
||||
const x = 10;
|
||||
const temp = user;
|
||||
const tmp1 = calculateTotal(items);
|
||||
const a = getData();
|
||||
const fn = (b) => b * 2;
|
||||
|
||||
// 일관성 없는 명명
|
||||
const max_count = 100;
|
||||
const itemTotal = 50;
|
||||
const MaxValue = 200;
|
||||
const user_status = 'active';
|
||||
|
||||
// 너무 긴 이름
|
||||
const userDataThatWillBeProcessedAndStoredButNotDeletedOrModifiedInAnyWayShapeOrForm = user;
|
||||
|
||||
// 약어 남용
|
||||
const usr = getUser();
|
||||
const adr = getAddress();
|
||||
const phn = getPhone();
|
||||
const nps = calculateScore();
|
||||
|
||||
// 부정확한 이름
|
||||
const userData = items; // userData인데 items를 저장?
|
||||
const getTotalPrice = () => items.length; // getTotalPrice인데 길이를 반환?
|
||||
```
|
||||
|
||||
#### ✅ 좋은 예
|
||||
```typescript
|
||||
// 명확한 이름
|
||||
const maxRetries = 10;
|
||||
const userProfile = user;
|
||||
const cartTotal = calculateTotal(items);
|
||||
const userData = getData();
|
||||
const doubleValue = (value: number) => value * 2;
|
||||
|
||||
// 일관성 있는 명명 (camelCase)
|
||||
const maxCount = 100;
|
||||
const itemTotal = 50;
|
||||
const maxValue = 200;
|
||||
const userStatus = 'active';
|
||||
|
||||
// 적절한 길이의 이름
|
||||
const userForProcessing = user;
|
||||
|
||||
// 완전한 이름 사용
|
||||
const user = getUser();
|
||||
const address = getAddress();
|
||||
const phone = getPhone();
|
||||
const netPromoterScore = calculateScore();
|
||||
|
||||
// 정확한 이름
|
||||
const cartItems = items;
|
||||
const totalPrice = items.reduce((sum, item) => sum + item.price, 0);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 함수 크기 비교
|
||||
|
||||
### ❌ 나쁜 예 - 너무 큰 함수
|
||||
```typescript
|
||||
// 함수가 50줄 이상
|
||||
async function handleUserRegistration(request: any) {
|
||||
const { email, password, firstName, lastName, phone, address, city, state, zip, country, dateOfBirth, acceptTerms } = request.body;
|
||||
|
||||
if (!email) throw new Error('Email is required');
|
||||
if (!password) throw new Error('Password is required');
|
||||
// ... 더 많은 검증
|
||||
|
||||
const hashedPassword = await hashPassword(password);
|
||||
const user = {
|
||||
email,
|
||||
password: hashedPassword,
|
||||
firstName,
|
||||
lastName,
|
||||
phone,
|
||||
address,
|
||||
city,
|
||||
state,
|
||||
zip,
|
||||
country,
|
||||
dateOfBirth,
|
||||
acceptTerms,
|
||||
createdAt: new Date(),
|
||||
lastLogin: null,
|
||||
isActive: true,
|
||||
preferences: {}
|
||||
};
|
||||
|
||||
const existingUser = await db.findUser(email);
|
||||
if (existingUser) throw new Error('User already exists');
|
||||
|
||||
await db.save('users', user);
|
||||
|
||||
const emailToken = generateToken();
|
||||
await db.save('email_tokens', { userId: user.id, token: emailToken });
|
||||
|
||||
const mailer = require('nodemailer');
|
||||
await mailer.send({
|
||||
to: email,
|
||||
subject: 'Verify your email',
|
||||
html: `<a href="...">Verify</a>`
|
||||
});
|
||||
|
||||
// ... 더 많은 코드
|
||||
|
||||
return { success: true, userId: user.id };
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ 좋은 예 - 작고 집중된 함수들
|
||||
```typescript
|
||||
async function handleUserRegistration(request: RegisterRequest): Promise<RegisterResponse> {
|
||||
const userData = request.body;
|
||||
|
||||
validateUserInput(userData);
|
||||
const existingUser = await userRepository.findByEmail(userData.email);
|
||||
if (existingUser) throw new Error('User already exists');
|
||||
|
||||
const user = await userService.registerUser(userData);
|
||||
await emailService.sendVerificationEmail(user);
|
||||
|
||||
return { success: true, userId: user.id };
|
||||
}
|
||||
|
||||
// 각 함수는 한 가지 일만 함
|
||||
function validateUserInput(userData: UserInput): void {
|
||||
if (!userData.email) throw new Error('Email is required');
|
||||
if (!userData.password) throw new Error('Password is required');
|
||||
}
|
||||
|
||||
async function registerUser(userData: UserInput): Promise<User> {
|
||||
const hashedPassword = await hashPassword(userData.password);
|
||||
return userRepository.save({ ...userData, password: hashedPassword });
|
||||
}
|
||||
|
||||
async function sendVerificationEmail(user: User): Promise<void> {
|
||||
const token = generateToken();
|
||||
await tokenRepository.save({ userId: user.id, token });
|
||||
await mailer.send({
|
||||
to: user.email,
|
||||
subject: 'Verify your email',
|
||||
html: `<a href="...">Verify</a>`
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 종합 예제: 실제 개선 사례
|
||||
|
||||
### 요구사항
|
||||
"상품 주문 처리 함수를 작성하세요"
|
||||
|
||||
#### ❌ 나쁜 구현 (30줄 이상)
|
||||
```typescript
|
||||
async function processOrder(orderData: any) {
|
||||
// SRP 위반: 여러 책임
|
||||
// DRY 위반: 반복되는 검증
|
||||
// YAGNI 위반: 불필요한 기능
|
||||
// 명명규칙: 일관성 없음
|
||||
// 타입 안전: any 사용
|
||||
|
||||
if (!orderData) throw new Error("Order data required");
|
||||
if (!orderData.userId) throw new Error("User ID required");
|
||||
if (!orderData.items || orderData.items.length === 0) throw new Error("Items required");
|
||||
|
||||
let total = 0;
|
||||
for (let i = 0; i < orderData.items.length; i++) {
|
||||
if (!orderData.items[i].productId) throw new Error("Product ID required");
|
||||
if (!orderData.items[i].quantity || orderData.items[i].quantity <= 0) throw new Error("Quantity must be > 0");
|
||||
|
||||
const product = await getProduct(orderData.items[i].productId);
|
||||
total += product.price * orderData.items[i].quantity;
|
||||
}
|
||||
|
||||
if (total <= 0) throw new Error("Total must be > 0");
|
||||
|
||||
const user = await getUser(orderData.userId);
|
||||
if (!user) throw new Error("User not found");
|
||||
|
||||
const order = {
|
||||
id: Math.random().toString(),
|
||||
userId: user.id,
|
||||
items: orderData.items,
|
||||
total: total,
|
||||
status: 'pending',
|
||||
createdAt: new Date(),
|
||||
completedAt: null
|
||||
};
|
||||
|
||||
const result = await db.insert('orders', order);
|
||||
|
||||
const message = `Order created: ${order.id}. Total: $${total}`;
|
||||
console.log(message);
|
||||
|
||||
return { orderId: order.id, total: total, message: message };
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 좋은 구현 (SRP, DRY, 단순화, YAGNI, 타입 안전)
|
||||
```typescript
|
||||
// === 타입 정의 ===
|
||||
interface OrderItem {
|
||||
productId: string;
|
||||
quantity: number;
|
||||
}
|
||||
|
||||
interface CreateOrderRequest {
|
||||
userId: string;
|
||||
items: OrderItem[];
|
||||
}
|
||||
|
||||
interface Order {
|
||||
id: string;
|
||||
userId: string;
|
||||
items: OrderItem[];
|
||||
total: number;
|
||||
status: 'pending' | 'completed' | 'cancelled';
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
interface CreateOrderResponse {
|
||||
orderId: string;
|
||||
total: number;
|
||||
}
|
||||
|
||||
// === 검증 계층 (SRP: 검증만 담당) ===
|
||||
class OrderValidator {
|
||||
validateCreateRequest(request: CreateOrderRequest): void {
|
||||
this.validateNotEmpty(request, 'Order data');
|
||||
this.validateNotEmpty(request.userId, 'User ID');
|
||||
this.validateItems(request.items);
|
||||
}
|
||||
|
||||
private validateNotEmpty<T>(value: T, fieldName: string): void {
|
||||
if (!value) throw new Error(`${fieldName} is required`);
|
||||
}
|
||||
|
||||
private validateItems(items: OrderItem[]): void {
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
throw new Error('At least one item is required');
|
||||
}
|
||||
|
||||
items.forEach((item, index) => {
|
||||
if (!item.productId) throw new Error(`Item ${index}: Product ID is required`);
|
||||
if (!item.quantity || item.quantity <= 0) throw new Error(`Item ${index}: Quantity must be > 0`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// === 가격 계산 계층 (SRP: 계산만 담당, DRY: 중복 로직 제거) ===
|
||||
class OrderPricingService {
|
||||
async calculateTotal(items: OrderItem[]): Promise<number> {
|
||||
let total = 0;
|
||||
|
||||
for (const item of items) {
|
||||
const product = await this.getProduct(item.productId);
|
||||
total += product.price * item.quantity;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private async getProduct(productId: string): Promise<{ price: number }> {
|
||||
return getProduct(productId); // 외부 함수 호출
|
||||
}
|
||||
}
|
||||
|
||||
// === 주문 생성 계층 (SRP: 주문 생성만 담당) ===
|
||||
class OrderFactory {
|
||||
create(userId: string, items: OrderItem[], total: number): Order {
|
||||
return {
|
||||
id: this.generateId(),
|
||||
userId,
|
||||
items,
|
||||
total,
|
||||
status: 'pending',
|
||||
createdAt: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
private generateId(): string {
|
||||
return `order_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// === 저장소 계층 (SRP: 저장만 담당) ===
|
||||
class OrderRepository {
|
||||
async save(order: Order): Promise<void> {
|
||||
await db.insert('orders', order);
|
||||
}
|
||||
|
||||
async findById(orderId: string): Promise<Order | null> {
|
||||
return db.findById('orders', orderId);
|
||||
}
|
||||
}
|
||||
|
||||
// === 메인 서비스 (조율만 담당) ===
|
||||
class OrderService {
|
||||
constructor(
|
||||
private validator: OrderValidator,
|
||||
private pricingService: OrderPricingService,
|
||||
private factory: OrderFactory,
|
||||
private repository: OrderRepository
|
||||
) {}
|
||||
|
||||
async createOrder(request: CreateOrderRequest): Promise<CreateOrderResponse> {
|
||||
// 1. 검증
|
||||
this.validator.validateCreateRequest(request);
|
||||
|
||||
// 2. 사용자 확인
|
||||
const user = await getUser(request.userId);
|
||||
if (!user) throw new Error('User not found');
|
||||
|
||||
// 3. 가격 계산
|
||||
const total = await this.pricingService.calculateTotal(request.items);
|
||||
|
||||
// 4. 주문 생성
|
||||
const order = this.factory.create(request.userId, request.items, total);
|
||||
|
||||
// 5. 저장
|
||||
await this.repository.save(order);
|
||||
|
||||
// 6. 응답
|
||||
return {
|
||||
orderId: order.id,
|
||||
total: order.total
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// === 사용 예 ===
|
||||
const orderService = new OrderService(
|
||||
new OrderValidator(),
|
||||
new OrderPricingService(),
|
||||
new OrderFactory(),
|
||||
new OrderRepository()
|
||||
);
|
||||
|
||||
const response = await orderService.createOrder({
|
||||
userId: 'user123',
|
||||
items: [
|
||||
{ productId: 'prod1', quantity: 2 },
|
||||
{ productId: 'prod2', quantity: 1 }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
**개선 사항:**
|
||||
|
||||
1. **SRP**: 각 클래스가 하나의 책임만 가짐
|
||||
2. **DRY**: 검증 로직이 한 곳에서만 정의됨
|
||||
3. **단순화**: 메인 로직이 명확하고 읽기 쉬움
|
||||
4. **YAGNI**: 필요한 기능만 구현
|
||||
5. **타입 안전**: 모든 함수에 명확한 타입 정의
|
||||
6. **명명규칙**: 일관성 있는 네이밍
|
||||
7. **테스트 용이**: 각 클래스를 독립적으로 테스트 가능
|
||||
|
||||
---
|
||||
|
||||
이 예제들을 참고하여 코드를 작성하고 검토해보세요!
|
||||
445
skills/code-style-reviewer/PRINCIPLES.md
Normal file
445
skills/code-style-reviewer/PRINCIPLES.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# Code Style Principles - 상세 가이드
|
||||
|
||||
## 1. 단일책임원칙 (Single Responsibility Principle - SRP)
|
||||
|
||||
### 개념
|
||||
"하나의 클래스나 함수는 하나의 변경 이유만 가져야 한다"
|
||||
|
||||
### 왜 중요한가?
|
||||
- 코드 이해와 유지보수가 쉬워집니다
|
||||
- 테스트가 간단해집니다
|
||||
- 재사용성이 높아집니다
|
||||
- 변경 영향도가 최소화됩니다
|
||||
|
||||
### 체크리스트
|
||||
- [ ] 함수가 한 가지 작업만 수행하는가?
|
||||
- [ ] 함수가 여러 이유로 변경될 가능성이 있는가?
|
||||
- [ ] 클래스가 여러 책임을 가지고 있는가?
|
||||
- [ ] 함수의 이름이 무엇을 하는지 정확히 설명하는가?
|
||||
- [ ] 함수를 한 문장으로 설명할 수 있는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// 여러 책임을 가진 함수
|
||||
function processUserOrder(userId: string, items: Item[]) {
|
||||
// 1. 사용자 검증
|
||||
const user = validateUser(userId);
|
||||
|
||||
// 2. 주문 생성
|
||||
const order = createOrder(user, items);
|
||||
|
||||
// 3. 결제 처리
|
||||
processPayment(order);
|
||||
|
||||
// 4. 이메일 발송
|
||||
sendEmail(user.email, "Order confirmed");
|
||||
|
||||
// 5. 로깅
|
||||
logTransaction(order);
|
||||
}
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 각 함수가 하나의 책임만 가짐
|
||||
function processUserOrder(userId: string, items: Item[]) {
|
||||
const user = validateUser(userId);
|
||||
const order = createOrder(user, items);
|
||||
handlePayment(order);
|
||||
notifyUser(user, order);
|
||||
recordTransaction(order);
|
||||
}
|
||||
|
||||
// 결제만 담당
|
||||
function handlePayment(order: Order): void {
|
||||
processPayment(order);
|
||||
}
|
||||
|
||||
// 알림만 담당
|
||||
function notifyUser(user: User, order: Order): void {
|
||||
sendEmail(user.email, "Order confirmed");
|
||||
}
|
||||
|
||||
// 로깅만 담당
|
||||
function recordTransaction(order: Order): void {
|
||||
logTransaction(order);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. DRY (Don't Repeat Yourself)
|
||||
|
||||
### 개념
|
||||
"같은 코드는 한 번만 작성해야 한다"
|
||||
|
||||
### 왜 중요한가?
|
||||
- 버그 수정이 한 곳에서만 필요합니다
|
||||
- 코드가 간결해집니다
|
||||
- 유지보수 비용이 감소합니다
|
||||
- 일관성이 보장됩니다
|
||||
|
||||
### 체크리스트
|
||||
- [ ] 반복되는 패턴이나 로직이 있는가?
|
||||
- [ ] 유사한 함수들이 통합될 수 있는가?
|
||||
- [ ] 공통 로직이 유틸리티 함수로 추출되었는가?
|
||||
- [ ] 복사-붙여넣기한 코드가 있는가?
|
||||
- [ ] 같은 정규식이나 로직이 여러 곳에 있는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// 반복되는 검증 로직
|
||||
function createUser(email: string, name: string) {
|
||||
if (!email || email.length === 0) {
|
||||
throw new Error("Email is required");
|
||||
}
|
||||
if (!name || name.length === 0) {
|
||||
throw new Error("Name is required");
|
||||
}
|
||||
// 사용자 생성...
|
||||
}
|
||||
|
||||
function createPost(title: string, content: string) {
|
||||
if (!title || title.length === 0) {
|
||||
throw new Error("Title is required");
|
||||
}
|
||||
if (!content || content.length === 0) {
|
||||
throw new Error("Content is required");
|
||||
}
|
||||
// 포스트 생성...
|
||||
}
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 검증 로직을 한 곳에서 관리
|
||||
function validateRequired(value: string, fieldName: string): void {
|
||||
if (!value || value.length === 0) {
|
||||
throw new Error(`${fieldName} is required`);
|
||||
}
|
||||
}
|
||||
|
||||
function createUser(email: string, name: string) {
|
||||
validateRequired(email, "Email");
|
||||
validateRequired(name, "Name");
|
||||
// 사용자 생성...
|
||||
}
|
||||
|
||||
function createPost(title: string, content: string) {
|
||||
validateRequired(title, "Title");
|
||||
validateRequired(content, "Content");
|
||||
// 포스트 생성...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 단순화 우선 (Simplicity First)
|
||||
|
||||
### 개념
|
||||
"복잡한 추상화보다는 이해하기 쉬운 단순한 코드를 우선한다"
|
||||
|
||||
### 왜 중요한가?
|
||||
- 새로운 팀원이 빨리 코드를 이해합니다
|
||||
- 버그가 적어집니다
|
||||
- 유지보수가 쉬워집니다
|
||||
- 과도한 설계를 피합니다
|
||||
|
||||
### 체크리스트
|
||||
- [ ] 코드를 읽고 쉽게 이해할 수 있는가?
|
||||
- [ ] 불필요한 추상화가 있는가?
|
||||
- [ ] 깊은 중첩이 있는가? (3단계 이상)
|
||||
- [ ] 과도하게 우아한(clever) 코드가 있는가?
|
||||
- [ ] 한 줄의 코드로 너무 많은 작업을 하는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// 불필요한 추상화와 복잡성
|
||||
interface Strategy {
|
||||
execute(data: any): any;
|
||||
}
|
||||
|
||||
class ComplexStrategy implements Strategy {
|
||||
execute = (data) => {
|
||||
return [data].reduce((acc, val) => ({
|
||||
...acc,
|
||||
...Object.entries(val).reduce((a, [k, v]) =>
|
||||
({ ...a, [k]: typeof v === 'number' ? v * 2 : v }), {})
|
||||
}), {});
|
||||
};
|
||||
}
|
||||
|
||||
// 깊은 중첩
|
||||
if (user) {
|
||||
if (user.isActive) {
|
||||
if (user.permissions) {
|
||||
if (user.permissions.includes('admin')) {
|
||||
// 실제 작업
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 단순하고 명확한 구현
|
||||
function doubleNumbers(obj: Record<string, any>): Record<string, any> {
|
||||
return Object.entries(obj).reduce((acc, [key, value]) => {
|
||||
acc[key] = typeof value === 'number' ? value * 2 : value;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
}
|
||||
|
||||
// Early return으로 중첩 제거
|
||||
function checkAdminPermission(user: User): boolean {
|
||||
if (!user) return false;
|
||||
if (!user.isActive) return false;
|
||||
if (!user.permissions) return false;
|
||||
return user.permissions.includes('admin');
|
||||
}
|
||||
|
||||
// 더 나은 방식
|
||||
function isAdmin(user: User): boolean {
|
||||
return user?.isActive && user?.permissions?.includes('admin') ?? false;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. YAGNI (You Aren't Gonna Need It)
|
||||
|
||||
### 개념
|
||||
"현재 필요하지 않은 기능은 구현하지 않는다"
|
||||
|
||||
### 왜 중요한가?
|
||||
- 코드 복잡도를 낮춥니다
|
||||
- 유지보수해야 할 코드의 양을 줄입니다
|
||||
- 미래 변경에 더 유연하게 대응합니다
|
||||
- 불필요한 버그의 원인을 제거합니다
|
||||
|
||||
### 체크리스트
|
||||
- [ ] 사용되지 않는 코드가 있는가?
|
||||
- [ ] 사용되지 않는 매개변수가 있는가?
|
||||
- [ ] "나중에 필요할 것 같아서" 추가한 기능이 있는가?
|
||||
- [ ] 테스트되지 않는 코드가 있는가?
|
||||
- [ ] 주석 처리된 코드가 있는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// 사용되지 않는 기능과 매개변수
|
||||
interface UserService {
|
||||
// 현재는 사용하지 않지만, 나중에 필요할 것 같아서 추가
|
||||
createUser(email: string, name: string, preferredLanguage?: string): User;
|
||||
updateUser(id: string, data: Partial<User>): User;
|
||||
deleteUser(id: string): void;
|
||||
// 나중에 필요할 것 같아서 추가
|
||||
suspendUser(id: string, reason: string): User;
|
||||
archiveUser(id: string): User;
|
||||
}
|
||||
|
||||
function getUserFullInfo(userId: string, includeAnalytics?: boolean, includeHistory?: boolean, includeFuturePredictions?: boolean) {
|
||||
// 현재는 includeAnalytics와 includeFuturePredictions를 사용하지 않음
|
||||
const user = getUser(userId);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 필요한 기능만 구현
|
||||
interface UserService {
|
||||
createUser(email: string, name: string): User;
|
||||
updateUser(id: string, data: Partial<User>): User;
|
||||
deleteUser(id: string): void;
|
||||
// 필요해지면 그때 추가
|
||||
}
|
||||
|
||||
function getUserFullInfo(userId: string) {
|
||||
const user = getUser(userId);
|
||||
// 필요한 정보만 반환
|
||||
return {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name
|
||||
};
|
||||
}
|
||||
|
||||
// 미래에 필요하면 그때 추가
|
||||
function getUserWithAnalytics(userId: string) {
|
||||
const user = getUser(userId);
|
||||
const analytics = getAnalytics(userId);
|
||||
return { ...user, analytics };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 타입 안전성 (Type Safety)
|
||||
|
||||
### 개념
|
||||
"`any` 타입을 피하고, 명확한 타입 정의를 사용한다" (TypeScript)
|
||||
|
||||
### 왜 중요한가?
|
||||
- 런타임 에러를 개발 단계에서 발견합니다
|
||||
- IDE의 자동완성이 정확해집니다
|
||||
- 코드 리팩토링이 안전합니다
|
||||
- 의도가 명확해집니다
|
||||
|
||||
### 체크리스트
|
||||
- [ ] `any` 타입이 사용되었는가?
|
||||
- [ ] 함수의 매개변수에 타입이 모두 정의되었는가?
|
||||
- [ ] 함수의 반환 타입이 명시되었는가?
|
||||
- [ ] 객체의 모양이 `interface`로 정의되었는가?
|
||||
- [ ] `unknown` 타입을 사용할 때 타입 가드가 있는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// any 타입 남용
|
||||
function processData(data: any): any {
|
||||
return data.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
value: item.value * 2
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 타입이 없는 반환값
|
||||
function getUserData(id) {
|
||||
// ...
|
||||
return user;
|
||||
}
|
||||
|
||||
// 암묵적 any
|
||||
function filterItems(items, predicate) {
|
||||
return items.filter(predicate);
|
||||
}
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 명확한 타입 정의
|
||||
interface DataItem {
|
||||
id: string;
|
||||
value: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function processData(data: DataItem[]): DataItem[] {
|
||||
return data.map(item => ({
|
||||
...item,
|
||||
value: item.value * 2
|
||||
}));
|
||||
}
|
||||
|
||||
// 명시적 반환 타입
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function getUserData(id: string): User {
|
||||
// ...
|
||||
return user;
|
||||
}
|
||||
|
||||
// 제네릭으로 유연성 유지
|
||||
function filterItems<T>(items: T[], predicate: (item: T) => boolean): T[] {
|
||||
return items.filter(predicate);
|
||||
}
|
||||
|
||||
// unknown을 사용할 때는 타입 가드 필수
|
||||
function process(data: unknown): void {
|
||||
if (typeof data === 'string') {
|
||||
console.log(data.toUpperCase());
|
||||
} else if (Array.isArray(data)) {
|
||||
console.log(data.length);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 명명규칙 (Naming Conventions)
|
||||
|
||||
### 개념
|
||||
"코드는 인간을 위해 작성되며, 명확한 이름이 코드의 의도를 전달한다"
|
||||
|
||||
### 체크리스트
|
||||
- [ ] 변수명이 의미있고 명확한가?
|
||||
- [ ] 함수명이 동사로 시작하는가?
|
||||
- [ ] 클래스명이 명사이고 PascalCase인가?
|
||||
- [ ] 상수가 UPPER_SNAKE_CASE인가?
|
||||
- [ ] 명명규칙이 일관성 있는가?
|
||||
|
||||
### 나쁜 예
|
||||
```typescript
|
||||
// 의미없는 이름
|
||||
const x = 10;
|
||||
const temp = userData;
|
||||
const fn = (a) => a * 2;
|
||||
|
||||
// 일관성 없는 명명
|
||||
const max_count = 100;
|
||||
const itemTotal = 50;
|
||||
const MaxValue = 200;
|
||||
|
||||
// 너무 긴 이름
|
||||
const userDataForProcessingAndStorageButNotForDeletionOrModification = user;
|
||||
```
|
||||
|
||||
### 좋은 예
|
||||
```typescript
|
||||
// 명확한 이름
|
||||
const maxRetries = 10;
|
||||
const userProfile = userData;
|
||||
const doubleNumber = (value: number) => value * 2;
|
||||
|
||||
// 일관성 있는 명명 (camelCase for variables/functions)
|
||||
const maxCount = 100;
|
||||
const itemTotal = 50;
|
||||
const maxValue = 200;
|
||||
|
||||
// 적절한 길이
|
||||
const userForProcessing = user;
|
||||
```
|
||||
|
||||
### 명명 규칙 요약
|
||||
|
||||
| 대상 | 규칙 | 예시 |
|
||||
|------|------|------|
|
||||
| 변수 | camelCase | `userName`, `isActive`, `itemCount` |
|
||||
| 함수 | camelCase + 동사 | `getUserData`, `validateEmail`, `calculateTotal` |
|
||||
| 클래스 | PascalCase | `UserService`, `ValidationHelper`, `ApiClient` |
|
||||
| 상수 | UPPER_SNAKE_CASE | `MAX_RETRY`, `DEFAULT_TIMEOUT`, `API_KEY` |
|
||||
| 파일 | lowercase + kebab-case | `user-service.ts`, `api-client.ts` |
|
||||
| 인터페이스 | PascalCase + I prefix (선택) | `IUser` 또는 `User` |
|
||||
| Enum | PascalCase | `UserStatus`, `PaymentMethod` |
|
||||
|
||||
---
|
||||
|
||||
## 종합 체크리스트
|
||||
|
||||
코드 리뷰 시 확인할 항목들:
|
||||
|
||||
### 구조 (Structure)
|
||||
- [ ] 함수/클래스가 하나의 책임만 가지는가? (SRP)
|
||||
- [ ] 반복되는 코드가 추출되었는가? (DRY)
|
||||
- [ ] 코드가 간단하고 이해하기 쉬운가? (Simplicity)
|
||||
|
||||
### 기능 (Functionality)
|
||||
- [ ] 필요한 기능만 구현되었는가? (YAGNI)
|
||||
- [ ] 사용되지 않는 코드가 없는가?
|
||||
- [ ] 타입이 안전하게 정의되었는가? (Type Safety)
|
||||
|
||||
### 스타일 (Style)
|
||||
- [ ] 명명규칙이 일관성 있는가?
|
||||
- [ ] 코드 형식이 일관성 있는가?
|
||||
- [ ] 주석이 적절히 작성되었는가?
|
||||
|
||||
### 테스트 (Testing)
|
||||
- [ ] 함수가 테스트 가능한 크기인가?
|
||||
- [ ] 엣지 케이스가 처리되었는가?
|
||||
- [ ] 에러 처리가 적절한가?
|
||||
140
skills/code-style-reviewer/SKILL.md
Normal file
140
skills/code-style-reviewer/SKILL.md
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
name: code-style-reviewer
|
||||
description: 코드 스타일 원칙 기반 리뷰 - 단일책임원칙(SRP), DRY(Don't Repeat Yourself), 단순화 우선, YAGNI(You Aren't Gonna Need It), 타입 안전성을 검사합니다. 코드 구조와 명명규칙도 함께 평가합니다. 코드 리뷰가 필요할 때 자동으로 사용됩니다.
|
||||
allowed-tools: Read, Grep, Glob
|
||||
---
|
||||
|
||||
# Code Style Reviewer
|
||||
|
||||
코드 스타일 원칙에 따른 전문적인 코드 리뷰를 제공하는 Skill입니다. Claude가 직접 코드를 분석하여 5가지 핵심 원칙을 중심으로 상세한 리포트를 생성합니다.
|
||||
|
||||
## 검사 원칙
|
||||
|
||||
### 1. 단일책임원칙 (Single Responsibility Principle)
|
||||
클래스, 함수, 모듈은 하나의 책임만 가져야 합니다. 복잡한 함수는 여러 작은 함수로 분리되어야 합니다.
|
||||
|
||||
### 2. DRY (Don't Repeat Yourself)
|
||||
같은 로직이 반복되면 안 됩니다. 공통 로직은 별도의 함수나 유틸리티로 추출해야 합니다.
|
||||
|
||||
### 3. 단순화 우선
|
||||
복잡한 추상화보다는 이해하기 쉬운 단순한 코드를 우선합니다. 과도한 설계는 피합니다.
|
||||
|
||||
### 4. YAGNI (You Aren't Gonna Need It)
|
||||
현재 필요하지 않은 기능은 추가하지 않습니다. 미래를 대비한 불필요한 코드는 제거해야 합니다.
|
||||
|
||||
### 5. 타입 안전성
|
||||
`any` 타입 사용을 최소화합니다. TypeScript를 사용할 때는 명확한 타입을 정의해야 합니다.
|
||||
|
||||
## Instructions
|
||||
|
||||
### 리뷰 프로세스
|
||||
|
||||
1. **대상 파일 파악**
|
||||
- 검토할 코드 파일을 Read 도구로 읽습니다
|
||||
- 파일 구조와 전체 범위를 파악합니다
|
||||
|
||||
2. **원칙별 분석**
|
||||
- 각 파일에 대해 5가지 원칙을 체계적으로 검토합니다
|
||||
- Grep을 사용하여 반복되는 패턴을 찾습니다
|
||||
- 명명규칙의 일관성을 확인합니다
|
||||
|
||||
3. **상세 리포트 생성**
|
||||
- 파일별로 구분된 리포트를 작성합니다
|
||||
- 각 문제점에 대해 구체적인 개선 방안을 제시합니다
|
||||
- 우선순위를 표시합니다:
|
||||
- **Critical**: 반드시 수정해야 함
|
||||
- **Warning**: 개선이 필요함
|
||||
- **Suggestion**: 고려해볼 만함
|
||||
|
||||
4. **코드 예시 제공**
|
||||
- 각 문제에 대해 "문제 코드" vs "개선 코드" 예시를 제시합니다
|
||||
- 변경의 이유를 명확히 설명합니다
|
||||
|
||||
## 리뷰 체크리스트
|
||||
|
||||
### 단일책임원칙 검사
|
||||
- [ ] 함수가 하나의 작업만 수행하는가?
|
||||
- [ ] 클래스가 하나의 책임만 가지는가?
|
||||
- [ ] 복잡한 로직이 작은 함수로 분리되어 있는가?
|
||||
- [ ] 함수의 길이가 적절한가? (권장: 20줄 이하)
|
||||
|
||||
### DRY 검사
|
||||
- [ ] 반복되는 코드가 있는가?
|
||||
- [ ] 공통 로직이 추출되었는가?
|
||||
- [ ] 설정 값이 하드코딩되지 않았는가?
|
||||
- [ ] 유사한 구조의 코드가 통합될 수 있는가?
|
||||
|
||||
### 단순화 우선 검사
|
||||
- [ ] 불필요한 추상화가 있는가?
|
||||
- [ ] 복잡한 문법 대신 단순한 표현을 사용했는가?
|
||||
- [ ] 깊은 중첩 구조가 있는가? (권장: 3단계 이내)
|
||||
- [ ] 과도하게 우아한(overly clever) 코드가 있는가?
|
||||
|
||||
### YAGNI 검사
|
||||
- [ ] 사용되지 않는 코드가 있는가?
|
||||
- [ ] 미래를 대비한 불필요한 기능이 있는가?
|
||||
- [ ] 제거할 수 있는 매개변수가 있는가?
|
||||
- [ ] 죽은(dead) 코드나 주석이 있는가?
|
||||
|
||||
### 타입 안전성 검사 (TypeScript)
|
||||
- [ ] `any` 타입이 사용되었는가?
|
||||
- [ ] 모든 함수의 매개변수에 타입이 정의되었는가?
|
||||
- [ ] 반환 타입이 명시적인가?
|
||||
- [ ] `interface`와 `type`을 적절히 사용했는가?
|
||||
|
||||
### 명명규칙 검사
|
||||
- [ ] 변수명이 명확하고 의미있는가?
|
||||
- [ ] 함수명이 동사로 시작하는가?
|
||||
- [ ] 클래스명이 명사이고 PascalCase인가?
|
||||
- [ ] 상수가 UPPER_SNAKE_CASE인가?
|
||||
- [ ] 명명규칙이 일관성 있는가?
|
||||
|
||||
## 예시
|
||||
|
||||
자세한 예시와 패턴은 [EXAMPLES.md](EXAMPLES.md) 참고
|
||||
상세한 원칙 설명은 [PRINCIPLES.md](PRINCIPLES.md) 참고
|
||||
|
||||
## 리뷰 출력 형식
|
||||
|
||||
```
|
||||
# Code Style Review Report
|
||||
|
||||
## 📄 파일: [filename]
|
||||
|
||||
### ✅ 좋은 점
|
||||
- [좋은 사례들]
|
||||
|
||||
### ⚠️ Critical Issues
|
||||
**문제 1: [제목]**
|
||||
- 위치: [라인 또는 함수명]
|
||||
- 원칙: [해당 원칙]
|
||||
- 설명: [상세 설명]
|
||||
- 개선 방법:
|
||||
```
|
||||
// Before
|
||||
[현재 코드]
|
||||
|
||||
// After
|
||||
[개선된 코드]
|
||||
```
|
||||
|
||||
### 📢 Warnings
|
||||
[경고 수준 문제들]
|
||||
|
||||
### 💡 Suggestions
|
||||
[제안 수준의 개선 사항들]
|
||||
|
||||
## 📊 종합 평가
|
||||
- 전체 코드 품질 점수: [X/10]
|
||||
- 가장 중요한 개선 사항: [상위 3개]
|
||||
```
|
||||
|
||||
## 사용 시나리오
|
||||
|
||||
이 Skill은 다음 상황에 자동으로 활용됩니다:
|
||||
|
||||
- 코드 리뷰 요청 시
|
||||
- 코드 품질 분석 요청 시
|
||||
- 코드 구조 개선 조언 필요 시
|
||||
- 새로운 파일의 스타일 검사 시
|
||||
- 기존 코드의 리팩토링 제안 시
|
||||
Reference in New Issue
Block a user