/refactor 커맨드로 실행되는 Rust 코드 리팩토링 자동화 스킬.
`.claude/rules/coding-style.md` 19개 섹션을 권위 문서로 삼아 Behavior Preserving·Domain First·Small Safe Steps 원칙을 적용한다.
지원 옵션:
/refactor 프로젝트 전체 리팩토링
/refactor [scope] 모듈·디렉토리·파일·glob 패턴 단위 리팩토링
/refactor [scope] --goal <goal> 목표 지정 (readability|maintainability|testability|domain-model|complexity)
/refactor [scope] --level <level> 강도 지정 (safe|moderate|aggressive)
/refactor [scope] --with-tests characterization·regression·edge case 테스트 함께 작성
/refactor [scope] --dry-run 실제 수정 없이 code smell·영향 범위·위험도 분석만 출력
실행 흐름 (6단계):
STEP 0 사전 조건 브랜치 확인·빌드+테스트 baseline
STEP 1 준비 목표·옵션·scope 정의
STEP 2 범위 식별 변경 범위·기존 테스트 확인
STEP 3 일괄 점검 coding-style.md §1~19 체크리스트·Code Smell 분석(Rust 특화 포함)
STEP 4 전략 수립 리스크 분석(High/Medium/Low 분기)·goal×level→항목 매핑으로 전략 선택
STEP 5 수행 선택 항목 순서대로 리팩토링→테스트→커밋 반복
브랜치: feature/refactor-{모듈명} / --dry-run 시 이 단계 없이 종료
STEP 6 검증 cargo cl
/refactor 커맨드로 실행되는 Rust 코드 리팩토링 자동화 스킬.
`.claude/rules/coding-style.md` 19개 섹션을 권위 문서로 삼아 Behavior Preserving·Domain First·Small Safe Steps 원칙을 적용한다.
지원 옵션:
/refactor 프로젝트 전체 리팩토링
/refactor [scope] 모듈·디렉토리·파일·glob 패턴 단위 리팩토링
/refactor [scope] --goal <goal> 목표 지정 (readability|maintainability|testability|domain-model|complexity)
/refactor [scope] --level <level> 강도 지정 (safe|moderate|aggressive)
/refactor [scope] --with-tests characterization·regression·edge case 테스트 함께 작성
/refactor [scope] --dry-run 실제 수정 없이 code smell·영향 범위·위험도 분석만 출력
실행 흐름 (6단계):
STEP 0 사전 조건 브랜치 확인·빌드+테스트 baseline
STEP 1 준비 목표·옵션·scope 정의
STEP 2 범위 식별 변경 범위·기존 테스트 확인
STEP 3 일괄 점검 coding-style.md §1~19 체크리스트·Code Smell 분석(Rust 특화 포함)
STEP 4 전략 수립 리스크 분석(High/Medium/Low 분기)·goal×level→항목 매핑으로 전략 선택
STEP 5 수행 선택 항목 순서대로 리팩토링→테스트→커밋 반복
브랜치: feature/refactor-{모듈명} / --dry-run 시 이 단계 없이 종료
STEP 6 검증 cargo clippy --fix→-D warnings→fmt → cargo test --all →
/security-full-scan(옵션)→ 아키텍처 리뷰→ complexity 측정→ diff 검토
피드백 분류:
🚫 Blocking behavior change·transaction 손상·architecture 위반·security regression 등 즉시 수정 필수
⚠️ Recommended long method·duplicate code·testability 부족 등 강력 권장
💡 Suggestions 선택적 개선 아이디어 (extensibility·performance·domain refinement 등)
📝 Tech Debt 현재 범위 초과 구조적 부채 → 별도 이슈로 추적
핵심 제약:
- 기능 추가와 리팩토링을 같은 커밋에 혼합 금지
- behavior change 발생 시 즉시 중단
- 테스트 미통과 상태로 커밋 금지
- aggressive 레벨에서도 behavior verification 반드시 유지
/refactor 커맨드 스킬
본 문서는 Axum Rusty 프로젝트의 코딩 철학과 표준을 반영한 리팩토링 실행 절차 및 지침을 제공한다.
--with-tests 옵션이 있으면 6.2.2 /test-align 명령을 수행해 테스트 갭 분석 및 보완을 진행한다.
--with-tests 명령은 ! ls ~/.claude/skills/`` 또는 ! ls .claude/skills/를 입력해 /test-align` 명령이 있는지 확인해 없으면 사용자에게 확인한다.
보안 정책
--with-security 옵션이 있으면 6.3 Security Scan을 수행해 보안 갭 분석 및 보완을 진행한다.
--with-security 전제 조건: claude-security-scan 이 설치되어 있어야 한다. ! ls ~/.claude/skills/`` 또는 ! ls .claude/skills/를 입력해 /security-full-scan, /security-scan`이 있는지 확인해 없으면 사용자에게 확인한다.
Dry Run 정책
--dry-run 옵션이 있으면 실제 수정 대신:
code smell 분석
architecture issue 분석
dependency risk 분석
영향 범위 분석
incremental 실행 계획
예상 위험도
를 우선 출력한다.
기본 실행 정책
옵션이 없더라도 Claude는 본 가이드의:
사전 조건 체크 (STEP 0)
Refactoring 준비 (STEP 1)
변경 범위 식별과 기존 테스트 확인 (STEP 2)
coding-style.md 기준 일괄 점검 (STEP 3)
리스크 분석과 리팩토링 전략 선택 (STEP 4)
리팩토링 수행 (STEP 5)
Verification & Cleanup (섹션 6)
피드백 작성 가이드라인 (섹션 7)
PR 준비 정책 (섹션 8)
을 자동으로 적용한다.
STEP 0 사전 조건 체크
리팩토링 시작 전 아래를 순서대로 확인한다. 하나라도 실패하면 사용자에게 보고하고 중단한다.
git fetch origin && git status && git log --oneline -5 && git log --oneline origin/main -5 # 최신 브랜치 확인
cargo build # 빌드 통과 여부 확인
cargo test --all # 기존 테스트 baseline 확인
확인 항목:
최신 브랜치 확인
cargo build 통과
cargo test --all 통과 (리팩토링 전 baseline 확보)
scope가 명시되지 않아 프로젝트 전체가 대상인 경우:
파일 수와 예상 작업량을 사용자에게 보고하고 계속 진행 여부를 확인한다.
STEP 1 Refactoring 준비
커맨드 옵션을 파악해 리팩토링의 목표를 명확히 정의한다.
STEP 1 산출물 (다음 단계로 전달):
적용 goal: <readability|maintainability|testability|domain-model|complexity> (기본값: readability)
unsafe 블록 필요성 재검토 및 // SAFETY: 주석, panic 기반 DoS 가능 경로 없음, serde deserialize 시 입력 검증 존재
3.4 .claude/rules/test-style.md 기반 테스트 코드 점검
리팩토링 과정에서 변경 범위의 테스트 코드 품질을 아래 기준으로 점검한다.
섹션
주요 체크
§2 Behavior First
상호작용 검증만 있고 상태 검증 없는 테스트 없음, 내부 구현에 의존하는 테스트 없음
§3 모킹 경계
프로세스 경계(외부 HTTP) → wiremock-rs, DB/Repository → Fake 또는 sqlx::test (Mock 금지)
§4 Classicist TDD
상태 검증 우선, 실제 domain object 사용, Mock은 외부 경계에만
§7 Readability
AAA 구조 준수, 테스트명 <행동>_<기대결과>_when_<조건> 패턴
§8 Flaky 패턴
tokio::time::sleep · 공유 전역 상태 · SystemTime::now() 직접 사용 없음
§15 파일 구조
단위 테스트 src/#[cfg(test)], 통합/DB/API 테스트 {crate}/tests/
§16 불필요 테스트
로직 없는 getter/setter·순수 CRUD·프레임워크 배선 테스트 삭제 대상
§17.1 Blocking
Assertion 없는 테스트, 의미 없는 assertion, 이유 없는 #[ignore], 통합 테스트의 Mock DB
§2 — Behavior First 확인
테스트가 내부 구현이 아닌 observable behavior를 검증하는지 확인한다.
확인 방법:
expect_xxx().times(n) 호출이 실제 assert_eq! 보다 압도적으로 많은지 검색
테스트 내에서 pub(super) 등으로 private 구현에 직접 접근하는지 확인
// ❌ 상호작용만 검증 — 리팩토링 후 테스트 의미 없음
mock_repo.expect_save().once().returning(|_| Ok(()));
// ✅ 상태 검증 — 동작 보존 여부를 직접 확인assert_eq!(todo.status(), TodoStatus::Done);
리팩토링 시 처리: 상호작용 검증 단독 테스트는 상태 검증으로 전환한다.
§3 — 모킹 경계 준수
경계별 Test Double 선택 기준을 점검한다.
경계
올바른 처리
잘못된 처리
외부 HTTP API
wiremock-rs Fake
실제 HTTP 호출
DB/Repository
InMemoryRepo Fake 또는 sqlx::test
MockRepository
시간/난수
Clock trait 주입 → FakeClock
SystemTime::now() 직접 사용
순수 domain 객체
실제 객체
Mock 금지
리팩토링 시 처리: MockRepository 패턴 발견 시 InMemoryRepo 또는 sqlx::test로 전환을 제안한다.
§4 — Classicist TDD 원칙 확인
Red-Green-Refactor 사이클 정합성과 Mock 사용 적절성을 확인한다.
domain 로직을 Mock하지 않는가?
Repository는 Fake 또는 실제 DB를 사용하는가?
EmailSender·EventPublisher 등 side-effect가 비즈니스 요구사항인 경우에만 mockall을 사용하는가?
상태 검증(assert_eq!)이 호출 횟수 검증(expect_xxx().times())보다 많은가?
// ❌ 구현 구조 반영fntest_complete()
fntest_todo_save()
// ✅ 행동 기반fncomplete_todo_returns_error_when_already_done()
fncreate_todo_fails_when_title_is_empty()
handle_, process_, run_, do_ 접두사는 사용하지 않는다.
§8 — Flaky 패턴 제거
다음 패턴을 grep으로 탐지하여 결정론적 방식으로 교체한다.
패턴
탐지 방법
교체 방법
tokio::time::sleep
grep time::sleep in #[test] / #[tokio::test]
채널/상태 기반 대기(rx.recv().await)
SystemTime::now()
grep SystemTime::now in test modules
Clock trait 주입 → FakeClock
rand::random() 시드 없음
grep rand::random in test modules
고정 시드 또는 proptest
공유 static / OnceCell
grep static / OnceCell in test modules
테스트마다 독립 인스턴스
Quarantine 규칙: Flaky 테스트 발견 시 단순 #[ignore] 금지. 이슈 링크·담당자·기한을 포함해야 한다.
// ❌ 이유 없는 ignore#[ignore]#[tokio::test]asyncfnflaky_test() { }
// ✅ 올바른 quarantine#[ignore = "Flaky: race condition in async setup. Issue: #123, Owner: @mimul, Due: 2024-06-01"]#[tokio::test]asyncfnflaky_test_with_context() { }
§15 — 테스트 파일 구조 확인
리팩토링 후 테스트 파일이 올바른 위치에 있는지 확인한다.
테스트 종류
올바른 위치
잘못된 위치
단위 테스트 (private 함수 포함)
src/ 내부 #[cfg(test)] 모듈
tests/ 최상위
통합 테스트 (공개 API 기준)
{crate}/tests/ 하위
src/ 내부
DB 테스트 (sqlx::test)
{crate}/tests/ 하위
src/ 내부
HTTP API 테스트
{crate}/tests/ 하위
src/ 내부
이상적인 구조:
domain/
src/todo.rs # Todo 도메인 + #[cfg(test)] 단위 테스트
controller/
tests/api_test.rs # HTTP API 통합 테스트
infra/
tests/user_repository_test.rs # DB 구현체 통합 테스트
§16 — 불필요 테스트 제거
다음 카테고리에 해당하는 테스트는 삭제 대상이다.
삭제 대상
판단 기준
로직 없는 getter/setter
단순 필드 반환만 검증하는 테스트
순수 CRUD
DB 연결 없이 save → find 반복만 검증 (통합 테스트 1개로 대체)
프레임워크 배선
axum 라우팅, shaku DI 배선만 검증하는 테스트
타입 시스템 보장 항목
컴파일러가 이미 보장하는 정적 설정·상수
삭제 예정 코드 테스트
대상 코드가 dead code인 경우 함께 삭제
판단 기준: "이 테스트가 보호하는 동작을 한 문장으로 설명할 수 없으면 삭제한다"
§17.1 — PR 거절 신호 (Blocking) 즉시 수정
리팩토링 과정에서 다음 패턴을 발견하면 리팩토링과 함께 수정한다.
Blocking 신호
확인 방법
수정 방향
Assertion 없는 테스트
#[test] 함수 내 assert! 계열 없음
상태 검증 assertion 추가 또는 삭제
의미 없는 assertion
assert!(result.is_some()) 단독
구체적 값 비교(assert_eq!)로 교체
이슈 링크 없는 #[ignore]
#[ignore]에 사유·담당자·기한 없음
§8 Quarantine 규칙 적용
통합 테스트의 Mock DB
MockXxxRepository 사용
InMemoryRepo 또는 sqlx::test 전환
비결정적 출력 고정
SystemTime::now(), 시드 없는 난수
FakeClock, 고정 시드 또는 proptest
테스트명이 구현 구조 반영
test_save(), test_complete() 등
<행동>_<결과>_when_<조건> 패턴으로 변경
STEP 4 리스크 분석과 리팩토링 전략 선택
4.1 리스크 분석
Public API 변경 여부
Backward Compatibility
Migration 필요 여부
데이터 손상 가능성
성능 영향
Lock/Concurrency 영향
Security 영향
리스크 대응 분기:
리스크 수준
판단 기준
대응
🔴 High
Public API 파괴적 변경 / Migration 필요 / data 손상 가능
사용자에게 보고 후 중단. 별도 마이그레이션 계획 필요
🟡 Medium
내부 API 변경 / 성능 영향 / concurrency 주의
사용자에게 보고 후 계속. 해당 항목에 별도 테스트 추가
🟢 Low
명명 개선 / 로깅 포맷 / 가시성 조정
그대로 진행
4.2 리팩토링 전략 선택
STEP 1의 goal·level과 STEP 3의 위반 목록을 기반으로, 아래 표에서 수행할 항목과 순서를 결정한다.
goal → 우선 수행 항목 매핑:
goal
우선 수행 항목 (STEP 5에서 먼저 실행)
readability
5.2 Naming → 5.3 함수 → 5.12 pub 범위 → 5.11 Comments
maintainability
5.9 Dependency → 5.3 함수 → 5.4 Struct/Trait → 5.10 Dead Code
5.5 조건문 → 5.3 함수 → 5.10 Dead Code → 5.4 Struct/Trait
level → 허용 범위:
level
허용 범위
safe
rename / extract method / 로깅 포맷 / pub 범위 조정. Public API 변경 금지
moderate
내부 struct 분리 / dependency 정리 / 내부 API 개선. Public API 변경 금지
aggressive
architecture 개선 / domain restructuring / legacy 추상화 제거. behavior verification 필수 유지
STEP 5 리팩토링 수행
STEP 3과 STEP 4의 분석 결과를 기반으로, STEP 4.2에서 선택한 항목과 순서로 리팩토링을 진행한다.
실행 원칙:
브랜치는 feature/refactor-{모듈명 또는 작업 내용} 형태로 만든다. (예: feature/refactor-todo-domain, feature/refactor-error-handling)
각 항목마다 리팩토링 → 테스트 → 커밋을 반복한다.
커밋 메시지는 refactor(scope): 내용 형태를 따른다. (CLAUDE.md 커밋 컨벤션)
기능 추가와 리팩토링을 같은 커밋에 혼합 금지.
behavior change 발생 시 즉시 중단.
--dry-run 옵션 시: STEP 0~4 분석 결과를 출력하고, STEP 5 실행 없이 종료한다. 출력 형식: 발견된 위반 목록, 예상 수행 항목, 리스크 수준.
5.2 Naming 개선
다음을 개선한다.
의미 없는 변수명 제거
타입 기반 이름 제거
축약어 최소화
도메인 용어 사용
Boolean Flag 제거
금지 접두사 — 의미가 약해 역할을 드러내지 못한다:
// 나쁜 예handle_auth()
process_order()
run_job()
do_cleanup()
// 좋은 예: <동사>_<대상> 형태로 의도를 명시validate_access_token()
complete_order()
execute_batch_export()
remove_expired_sessions()
예:
data → order_items
flag → is_expired
5.3 함수 리팩토링
목표:
단일 책임
의도 중심
Side Effect 최소화
체크:
함수가 여러 역할 수행하는가?
조건문이 과도한가?
depth가 깊은가?
mutable state가 많은가?
보안 체크:
함수 추출 과정에서 인증·인가 검증 로직이 누락되지 않는가? (§2 Authorization)
security-sensitive 경계(validation, auth check, sanitization)가 함수 분리 후에도 유지되는가? (§3 Input Validation)
5.4 Struct / Impl / Trait 리팩토링
체크:
하나의 impl block이 여러 책임을 지는가?
struct 필드와 impl 로직이 응집되어 있는가?
도메인 규칙이 usecase에 흩어져 있는가?
trait 경계가 명확한가?
개선:
책임별 struct 분리
도메인 로직을 domain layer로 이동
trait 추출로 의존성 역전
composition 우선 (Rust는 상속이 없다)
5.5 조건문 리팩토링
다음을 우선 제거한다.
거대한 if/else
match 과다 분기
상태 기반 분기
대체:
Polymorphism (trait 활용)
Strategy Pattern
State Pattern
Lookup Table
5.6 데이터 구조 개선
다음을 제거한다.
Primitive Obsession
Magic Number
Stringly Typed 구조
대체:
Value Object
Enum
Domain Type (Id<T> Newtype, enum Status)
보안 체크:
Newtype 도입 시 생성자에 validation 로직을 포함하여 invalid input이 타입 시스템에서 차단되는가? (§3 Input Validation)
Magic Number 제거 시 보안 관련 상수(max payload size, token TTL, rate limit 등)가 명시적 상수로 정의되는가? (§11.1 DoS)
5.7 에러 처리 리팩토링
체크:
unwrap() / expect() 남용 여부
의미 없는 에러 메시지 (anyhow!("something wrong"))
레이어 간 에러 타입 누출 여부
thiserror (라이브러리) / anyhow (바이너리 main) 사용 맥락 혼용 여부