| name | code-refactoring |
| description | Simplify and refactor code while preserving behavior, improving clarity, and reducing complexity. Use when simplifying complex code, removing duplication, or applying design patterns. Handles Extract Method, DRY principle, SOLID principles, behavior validation, and refactoring patterns. |
| tags | ["refactoring","code-quality","DRY","SOLID","design-patterns","clean-code","simplification","behavior-preservation"] |
| platforms | ["Claude","ChatGPT","Gemini","Codex"] |
| allowed-tools | ["Read","Write","Edit","Bash","Task"] |
Code Refactoring
When to use this skill
- ์ฝ๋ ๋ฆฌ๋ทฐ: ๋ณต์กํ๊ฑฐ๋ ์ค๋ณต๋ ์ฝ๋ ๋ฐ๊ฒฌ
- ์ ๊ธฐ๋ฅ ์ถ๊ฐ ์ : ๊ธฐ์กด ์ฝ๋ ์ ๋ฆฌ
- ๋ฒ๊ทธ ์์ ํ: ๊ทผ๋ณธ ์์ธ ์ ๊ฑฐ
- ๊ธฐ์ ๋ถ์ฑ ํด์: ์ ๊ธฐ์ ์ธ ๋ฆฌํฉํ ๋ง
Instructions
Step 1: Extract Method (๋ฉ์๋ ์ถ์ถ)
Before (๊ธด ํจ์):
function processOrder(order: Order) {
if (!order.items || order.items.length === 0) {
throw new Error('Order must have items');
}
if (!order.customerId) {
throw new Error('Order must have customer');
}
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
const tax = total * 0.1;
const shipping = total > 100 ? 0 : 10;
const finalTotal = total + tax + shipping;
for (const item of order.items) {
const product = await db.product.findUnique({ where: { id: item.productId } });
if (product.stock < item.quantity) {
throw new Error(`Insufficient stock for ${product.name}`);
}
}
const newOrder = await db.order.create({
data: {
customerId: order.customerId,
items: order.items,
total: finalTotal,
status: 'pending'
}
});
return newOrder;
}
After (๋ฉ์๋ ์ถ์ถ):
async function processOrder(order: Order) {
validateOrder(order);
const total = calculateTotal(order);
await checkInventory(order);
return await createOrder(order, total);
}
function validateOrder(order: Order) {
if (!order.items || order.items.length === 0) {
throw new Error('Order must have items');
}
if (!order.customerId) {
throw new Error('Order must have customer');
}
}
function calculateTotal(order: Order): number {
const subtotal = order.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = subtotal * 0.1;
const shipping = subtotal > 100 ? 0 : 10;
return subtotal + tax + shipping;
}
async function checkInventory(order: Order) {
for (const item of order.items) {
const product = await db.product.findUnique({ where: { id: item.productId } });
if (product.stock < item.quantity) {
throw new Error(`Insufficient stock for ${product.name}`);
}
}
}
async function createOrder(order: Order, total: number) {
return await db.order.create({
data: {
customerId: order.customerId,
items: order.items,
total,
status: 'pending'
}
});
}
Step 2: Remove Duplication (์ค๋ณต ์ ๊ฑฐ)
Before (์ค๋ณต):
async function getActiveUsers() {
return await db.user.findMany({
where: { status: 'active', deletedAt: null },
select: { id: true, name: true, email: true }
});
}
async function getActivePremiumUsers() {
return await db.user.findMany({
where: { status: 'active', deletedAt: null, plan: 'premium' },
select: { id: true, name: true, email: true }
});
}
After (๊ณตํต ๋ก์ง ์ถ์ถ):
type UserFilter = {
plan?: string;
};
async function getActiveUsers(filter: UserFilter = {}) {
return await db.user.findMany({
where: {
status: 'active',
deletedAt: null,
...filter
},
select: { id: true, name: true, email: true }
});
}
const allActiveUsers = await getActiveUsers();
const premiumUsers = await getActiveUsers({ plan: 'premium' });
Step 3: Replace Conditional with Polymorphism
Before (๊ธด if-else):
class PaymentProcessor {
process(payment: Payment) {
if (payment.method === 'credit_card') {
const cardToken = this.tokenizeCard(payment.card);
const charge = this.chargeCreditCard(cardToken, payment.amount);
return charge;
} else if (payment.method === 'paypal') {
const paypalOrder = this.createPayPalOrder(payment.amount);
const approval = this.getPayPalApproval(paypalOrder);
return approval;
} else if (payment.method === 'bank_transfer') {
const transfer = this.initiateBankTransfer(payment.account, payment.amount);
return transfer;
}
}
}
After (๋คํ์ฑ):
interface PaymentMethod {
process(payment: Payment): Promise<PaymentResult>;
}
class CreditCardPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
const cardToken = await this.tokenizeCard(payment.card);
return await this.chargeCreditCard(cardToken, payment.amount);
}
}
class PayPalPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
const order = await this.createPayPalOrder(payment.amount);
return await this.getPayPalApproval(order);
}
}
class BankTransferPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
return await this.initiateBankTransfer(payment.account, payment.amount);
}
}
class PaymentProcessor {
private methods: Map<string, PaymentMethod> = new Map([
['credit_card', new CreditCardPayment()],
['paypal', new PayPalPayment()],
['bank_transfer', new BankTransferPayment()]
]);
async process(payment: Payment): Promise<PaymentResult> {
const method = this.methods.get(payment.method);
if (!method) {
throw new Error(`Unknown payment method: ${payment.method}`);
}
return await method.process(payment);
}
}
Step 4: Introduce Parameter Object
Before (๋ง์ ํ๋ผ๋ฏธํฐ):
function createUser(
name: string,
email: string,
password: string,
age: number,
country: string,
city: string,
postalCode: string,
phoneNumber: string
) {
}
After (๊ฐ์ฒด๋ก ๊ทธ๋ฃนํ):
interface UserProfile {
name: string;
email: string;
password: string;
age: number;
}
interface Address {
country: string;
city: string;
postalCode: string;
}
interface CreateUserParams {
profile: UserProfile;
address: Address;
phoneNumber: string;
}
function createUser(params: CreateUserParams) {
const { profile, address, phoneNumber } = params;
}
createUser({
profile: { name: 'John', email: 'john@example.com', password: 'xxx', age: 30 },
address: { country: 'US', city: 'NYC', postalCode: '10001' },
phoneNumber: '+1234567890'
});
Step 5: SOLID ์์น ์ ์ฉ
Single Responsibility (๋จ์ผ ์ฑ
์):
class User {
constructor(public name: string, public email: string) {}
save() {
}
sendEmail(subject: string, body: string) {
}
generateReport() {
}
}
class User {
constructor(public name: string, public email: string) {}
}
class UserRepository {
save(user: User) {
}
}
class EmailService {
send(to: string, subject: string, body: string) {
}
}
class UserReportGenerator {
generate(user: User) {
}
}
Output format
๋ฆฌํฉํ ๋ง ์ฒดํฌ๋ฆฌ์คํธ
- [ ] ํจ์๋ ํ ๊ฐ์ง ์ผ๋ง ํ๋ค (SRP)
- [ ] ํจ์ ์ด๋ฆ์ด ํ๋ ์ผ์ ๋ช
ํํ ์ค๋ช
ํ๋ค
- [ ] ํจ์๋ 20์ค ์ดํ (๊ฐ์ด๋๋ผ์ธ)
- [ ] ๋งค๊ฐ๋ณ์๋ 3๊ฐ ์ดํ
- [ ] ์ค๋ณต ์ฝ๋ ์์ (DRY)
- [ ] if ์ค์ฒฉ์ 2๋จ๊ณ ์ดํ
- [ ] ๋งค์ง ๋๋ฒ ์์ (์์๋ก ์ถ์ถ)
- [ ] ์ฃผ์ ์์ด๋ ์ดํด ๊ฐ๋ฅ (์๊ธฐ ๋ฌธ์ํ)
Constraints
ํ์ ๊ท์น (MUST)
- ํ
์คํธ ๋จผ์ : ๋ฆฌํฉํ ๋ง ์ ํ
์คํธ ์์ฑ
- ์์ ๋จ๊ณ: ํ ๋ฒ์ ํ๋์ฉ ๋ณ๊ฒฝ
- ๋์ ๋ณด์กด: ๊ธฐ๋ฅ ๋ณ๊ฒฝ ์์
๊ธ์ง ์ฌํญ (MUST NOT)
- ๋์์ ์ฌ๋ฌ ์์
: ๋ฆฌํฉํ ๋ง + ๊ธฐ๋ฅ ์ถ๊ฐ ๋์ ๊ธ์ง
- ํ
์คํธ ์์ด ๋ฆฌํฉํ ๋ง: ํ๊ท ์ํ
Best practices
- Boy Scout Rule: ์ฝ๋๋ฅผ ๋ฐ๊ฒฌํ์ ๋๋ณด๋ค ๊นจ๋ํ๊ฒ
- ๋ฆฌํฉํ ๋ง ํ์ด๋ฐ: Red-Green-Refactor (TDD)
- ์ ์ง์ ๊ฐ์ : ์๋ฒฝ๋ณด๋ค ๊พธ์คํ
- ํ๋ ๋ณด์กด: ๋ฆฌํฉํ ๋ง์ ๊ธฐ๋ฅ ๋ณ๊ฒฝ ์์
- ์์ ์ปค๋ฐ: ํฌ์ปค์ค๋ ๋จ์๋ก ์ปค๋ฐ
Behavior Validation (Code Simplifier Integration)
Step A: Understand Current Behavior
๋ฆฌํฉํ ๋ง ์ ํ์ฌ ๋์ ์์ ํ ์ดํด:
## Behavior Analysis
### Inputs
- [์
๋ ฅ ํ๋ผ๋ฏธํฐ ๋ชฉ๋ก]
- [ํ์
๋ฐ ์ ์ฝ์ฌํญ]
### Outputs
- [๋ฐํ๊ฐ]
- [๋ถ์ ํจ๊ณผ (side effects)]
### Invariants
- [ํญ์ ์ฐธ์ด์ด์ผ ํ๋ ์กฐ๊ฑด๋ค]
- [๊ฒฝ๊ณ ์กฐ๊ฑด (edge cases)]
### Dependencies
- [์ธ๋ถ ์์กด์ฑ]
- [์ํ ์์กด์ฑ]
Step B: Validate After Refactoring
npm test -- --coverage
npx tsc --noEmit
npm run lint
npm test -- --updateSnapshot
Step C: Document Changes
## Refactoring Summary
### Changes Made
1. [๋ณ๊ฒฝ 1]: [์ด์ ]
2. [๋ณ๊ฒฝ 2]: [์ด์ ]
### Behavior Preserved
- [x] ๋์ผํ ์
๋ ฅ โ ๋์ผํ ์ถ๋ ฅ
- [x] ๋ถ์ ํจ๊ณผ ๋์ผ
- [x] ์๋ฌ ์ฒ๋ฆฌ ๋์ผ
### Risks & Follow-ups
- [์ ์ฌ์ ์ํ]
- [ํ์ ์์
]
### Test Status
- [ ] Unit tests: passing
- [ ] Integration tests: passing
- [ ] E2E tests: passing
Troubleshooting
Issue: Tests fail after refactor
Cause: ๋์ ๋ณ๊ฒฝ์ด ๋ฐ์ํจ
Solution: ๋๋๋ฆฌ๊ณ ๋ณ๊ฒฝ์ ๊ฒฉ๋ฆฌํ์ฌ ์ฌ์๋
Issue: Code still complex
Cause: ํ๋์ ํจ์์ ์ฌ๋ฌ ์ฑ
์ ํผํฉ
Solution: ๋ช
ํํ ๊ฒฝ๊ณ๋ก ๋ ์์ ๋จ์ ์ถ์ถ
Issue: Performance regression
Cause: ๋นํจ์จ์ ์ธ ์ถ์ํ ๋์
Solution: ํ๋กํ์ผ๋ง ํ ํซ ํจ์ค ์ต์ ํ
Multi-Agent Workflow
Validation & Retrospectives
- Round 1 (Orchestrator): ํ๋ ๋ณด์กด ์ฒดํฌ๋ฆฌ์คํธ ๊ฒ์ฆ
- Round 2 (Analyst): ๋ณต์ก๋ ๋ฐ ์ค๋ณต ๋ถ์
- Round 3 (Executor): ํ
์คํธ ๋๋ ์ ์ ๋ถ์ ๊ฒ์ฆ
Agent Roles
| Agent | Role |
|---|
| Claude | ๋ฆฌํฉํ ๋ง ๊ณํ, ์ฝ๋ ๋ณํ |
| Gemini | ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค ๋ถ์, ํจํด ํ์ง |
| Codex | ํ
์คํธ ์คํ, ๋น๋ ๊ฒ์ฆ |
Workflow Example
ask-gemini "@src/ ๋ณต์ก๋ ๋์ ํจ์ ๋ชฉ๋ก ์ถ์ถ"
codex-cli shell "npm test && npm run lint"
References
Metadata
๋ฒ์
- ํ์ฌ ๋ฒ์ : 1.0.0
- ์ต์ข
์
๋ฐ์ดํธ: 2025-01-01
- ํธํ ํ๋ซํผ: Claude, ChatGPT, Gemini
๊ด๋ จ ์คํฌ
ํ๊ทธ
#refactoring #code-quality #DRY #SOLID #design-patterns #clean-code
Examples
Example 1: Basic usage
Example 2: Advanced usage