Skip to content
cccskills
FreePublished on GitHub

system-wide-refactoring

대화형 System-wide Refactoring — 변경 파일에서 Extract Method·Domain Logic 이동·Pull Up 등 클래스 경계를 넘는 구조 변경 후보를 찾아 승인 후 기법별 커밋으로 적용. "구조 리팩토링", "도메인 로직 이동", "클래스 분리", "system-wide", "/system-wide-refactoring" 요청 시 사용. 단, 메서드 내부 정돈(guard clause·extract variable 등)은 /tdd-tidy가 적합. /system-wide-refactoring [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md15.1 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

System-wide Refactoring Skill

코드 분석 → 리팩토링 후보 제시 → 사용자 확인 → 기법별 커밋.

GOAL

  • 성공 = 사용자가 확인한 리팩토링이 기법별 커밋으로 완료됨
  • Extract Method / Extract Delegate / Domain Logic 이동 / SoC(Split Phase, Split by Abstraction Layer, Split by Unrelated Complexity) / 상속 계층(Pull Up Method, Push Down → Delegate) / 유사 기능 통합(Programming by Difference) 후보가 식별됨
  • 사용자와 질의응답으로 방향이 확정됨
  • 모든 테스트 통과

CONSTRAINTS

Hard Rules

  • 동작 변경 금지 — 구조 개선만 수행
  • 테스트 수정 금지 — 구조 변경이 테스트를 실패시키면 되돌리기
  • 사용자 확인 없이 리팩토링 금지 — 모든 후보는 사용자 승인 후 실행
  • 커밋 단위 — 1파일 x 1기법 = 1커밋 (논리적으로 연결된 파일은 함께)
  • git add -A 금지 — 변경된 파일만 명시적으로 추가

OUTPUT FORMAT

실행 절차

공통 골격(대상 파일 수집 → 후보 제시(계열별 승인 규칙) → 적용 → 테스트 → 커밋/되돌리기, 브랜치·PR이 필요한 조건)은 이 스킬 디렉터리 기준 ../../references/refactoring-procedure.md가 정본이다. 아래는 이 기법에 고유한 부분만 규정한다.

적용 순서 — 후보 제시의 기본 순서

후보를 제시할 때 아래 순서를 기본으로 정렬한다. 사용자가 순서를 바꾸면 그에 따른다.

A. TDD 직후(테스트에 로직이 모여 있는 상태) — 초기 리팩터링 7단계:

  1. 전제: 모든 로직이 테스트에 구현되어 있다 (이 전제가 없으면 2~7의 순서가 성립하지 않는다)
  2. AAA 구조의 테스트에서 Act 부분을 Extract Method한다
  3. Act가 의존성 객체를 쓰면: ① Introduce Parameter로 의존성을 파라미터로 → ② Introduce Parameter Object로 파라미터들을 객체(Application Service)로 묶는다 (Application Service에 그대로 전달될 파라미터는 제외) → ③ Move Instance Method to Application Service. 의존성이 Application Service 생성자로 전달된다. 의존성 주입(DI)이 가능해진다
  4. Act가 의존성 객체를 쓰지 않으면: Extract Delegate로 Application Service를 추출한다
  5. Application Service에서 Slide Statements로 I/O → 계산 → I/O 구조(Functional Core / Imperative Shell)를 확보한다
  6. Application Service에서 Extract Method로 도메인 로직을 메서드로 추출한다
  7. 추출된 도메인 로직을 Extract Delegate·Move Instance Method로 domain 계층에 옮긴다

B. 기존 트랜잭션 스크립트 → 도메인 모델 (Jimmy Bogard 9단계):

  1. Composed Method — 긴 메서드를 같은 추상화 수준의 작은 메서드로 분해
  2. Extract Methods — 주석 기준으로 블록 추출
  3. Introduce Parameter — 의존성 명시화
  4. Extract Class — seam 생성
  5. Extract Interface — 테스트 용이성
  6. Make Method Non-Static — Feature Envy 해소를 위한 인스턴스 메서드 전환
  7. Move Method — 데이터를 가진 객체로 이동
  8. Inline (Undo) — 이동 결과가 불균형하면 되돌리고 재추출한다. 리팩터링은 되돌릴 수 있어야 한다
  9. Reduce Setter Scope — private setter로 캡슐화 강화

A와 B는 출발 상태가 다르다(A: 테스트에 로직 집중, B: 기존 서비스 코드). 둘을 섞지 않는다.

코드 분석 — 리팩토링 후보 식별 (공통 절차 2단계)

대상 파일을 읽고 다음 패턴을 찾는다:

Extract Method 후보:

  • 긴 메서드 (20줄 이상)에서 독립적인 로직 블록
  • 여러 곳에서 반복되는 코드 패턴
  • 서로 다른 추상화 수준이 섞인 메서드

Extract Delegate 후보 (Fat Class 분리 3기준):

  • ISP 기준: 클라이언트마다 사용하는 메서드 집합이 다르면 클라이언트별 인터페이스 단위로 분리한다
  • 관련 필드·메서드 기준: 함께 읽고 쓰는 필드와 메서드가 그룹을 이루면 그 그룹을 클래스로 추출한다
  • 관련 협력자(collaborator) 기준: 테스트 setUp에서 함께 mock되는 협력자 그룹이 있으면 그 그룹을 사용하는 메서드들이 하나의 클래스 경계 후보다 (Split by Unrelated Complexity와 같은 신호)

Domain Logic 이동 후보:

  • Feature Envy — Service에서 도메인 객체의 데이터를 직접 조작
  • Tell, Don't Ask 위반 — getter 체이닝으로 로직 수행
  • Hide Delegate — getter 체이닝으로 내부 객체를 노출 (디미터 법칙(Law of Demeter) 위반)
    • 징후: obj.getA().getB().doSomething() 형태의 체이닝
    • 해결: 중간 객체를 숨기고 위임 메서드 제공, 또는 로직 자체를 obj로 이동
    • Tell Don't Ask와의 관계: 둘 다 Feature Envy의 증상. 해결 방향 동일 — 로직을 데이터가 있는 곳으로 이동
  • Domain Service, Value Object, First Class Collection 추출 가능
  • Train Wreck 제거 — a.getB().getC().doSomething() 연쇄 호출(Law of Demeter 위반)을 2단계로 해소한다: ① 연쇄 호출을 포함한 로직을 Extract Method → ② 추출한 메서드를 연쇄의 시작 객체(a 또는 B)로 Move Method. Hide Delegate가 위임 메서드를 추가하는 것과 달리 로직 자체를 옮긴다
  • Remove Middle Man (Hide Delegate의 역) — 단순 위임만 하는 메서드가 많아져 클라이언트가 위임 객체를 직접 다루는 편이 자연스러우면 위임 메서드를 제거하고 위임 객체를 노출한다. Hide Delegate와 Remove Middle Man은 같은 축의 양 끝이며, 위임 메서드 수를 기준으로 판단한다

Split by Abstraction Layer 후보:

  • High-level 비즈니스 로직과 Low-level 인프라 코드(DB, I/O)가 한 메서드에 혼재
  • App 계층과 Domain 계층이 분리되지 않은 경우
  • 낮은 수준 기능이 높은 수준 클래스에 섞였을 때의 절차 (Push Down → Delegate → 상속 제거): ① 낮은 수준 기능을 담을 새 클래스를 임시로 원본 클래스의 하위 클래스로 만든다 ② 옮길 필드·메서드를 Push Members Down으로 하위 클래스에 내린다 ③ 테스트가 통과하도록 원본 클래스가 하위 클래스 인스턴스에 위임(delegate)하게 바꾼다 ④ 상속 관계를 제거해 합성(composition)으로 확정한다. 각 단계마다 테스트를 실행한다

Split by Unrelated Complexity 후보:

  • 서로 관계없는 복잡성(예: 사용자 처리 로직과 상품 처리 로직)이 한 메서드/클래스에 혼재
  • 서로 다른 변경 이유를 가진 코드가 결합된 경우

Split Phase 후보 (Functional Core & Imperative Shell 포함):

  • 서로 다른 계산 단계가 한 메서드에 혼재 (예: 파싱 → 처리 → 포매팅)
  • 순수 로직과 부수효과(I/O)가 분리되지 않은 경우 (impure → pure → impure 구조)
    • 패턴: I/O(impure) → 비즈니스 로직(pure) → I/O(impure)
  • 중간 데이터 구조(Intermediate Data Structure)로 단계를 연결

Pull Up Method 후보 (서브클래스 간 중복):

  • 여러 서브클래스가 같은 메서드를 동일하게 override한 경우. 절차(IDE 자동 리팩터링 활용): ① superclass의 abstract 메서드에서 abstract를 제거한다 ② 각 서브클래스의 override 메서드를 같은 이름으로 rename하고 Pull Members Up한다. IDE가 다른 서브클래스의 중복도 바꾸겠느냐고 물으면 "Skip"을 선택한다 (서브클래스 간 중복은 자동으로 제거되지 않으므로 모든 서브클래스에 대해 rename → pull up을 반복한다) ③ pull up된 메서드 하나를 골라 본문을 Extract Method하며 IDE의 "All"(전체 치환)을 선택해 나머지 중복도 한 번에 치환한다 ④ superclass 구현을 추출한 메서드 호출로 바꾼다 ⑤ 사용되지 않는 서브클래스 메서드를 제거한다

유사 기능 통합 후보 (Programming by Difference):

  • 새 기능이 기존 기능과 대부분 같고 일부만 다를 때. 순서: ① 복사·붙여넣기로 동작시킨다 → ② 차이점만 남기고 공통 부분을 추출한다(Programming by Difference) → ③ Template Method 패턴으로 수렴한다(상속 또는 위임). 세부 절차는 규정하지 않는다. 후보 제시 시 차이점 목록을 사용자에게 먼저 확인한다

후보 제시 예시 (공통 절차 3단계)

후보를 하나씩 제시하고 사용자 확인:

## 리팩토링 후보 1: Extract Method

**파일**: OrderService.java
**대상**: processOrder() 메서드 (45줄)

**현재 코드**:
[해당 코드 블록]

**제안 변경**:
- calculateDiscount() 메서드 추출 (라인 23-35)
- validateInventory() 메서드 추출 (라인 37-42)

**적용할까요?** (yes / no / 수정 요청)
## 리팩토링 후보 N: Split Phase (Functional Core & Imperative Shell)

**파일**: OrderService.java
**대상**: processOrder() 메서드 (35줄)

**현재 코드**:
[해당 코드 블록 — DB 조회, 비즈니스 로직, DB 저장이 혼재]

**제안 변경**:
1. DB 조회를 메서드 상단으로 모음 (Imperative Shell — 빵)
2. 순수 비즈니스 로직을 별도 메서드로 추출 (Functional Core — 속)
3. DB 저장을 메서드 하단으로 모음 (Imperative Shell — 빵)

**적용할까요?** (yes / no / 수정 요청)
## 리팩토링 후보 N: Hide Delegate (Domain Logic 이동)

**파일**: OrderService.java
**대상**: order.getCustomer().getAddress().getCity() (3단계 체이닝)

**현재 코드**:
String city = order.getCustomer().getAddress().getCity();
if (city.equals("Seoul")) { applyLocalDiscount(); }

**제안 변경**:
1. Order에 getCustomerCity() 위임 메서드 추가
   또는
2. 판단 로직 자체를 Order로 이동: order.isLocatedIn("Seoul")

**적용할까요?** (yes / no / 수정 요청)
## 리팩토링 후보 N: Pull Up Method (서브클래스 중복 제거)

**파일**: CardPayment.java, BankTransferPayment.java, PointPayment.java
**대상**: validate() — 3개 서브클래스가 동일 구현으로 override

**현재 코드**:
[각 서브클래스의 validate() 본문 — 동일]

**제안 변경**:
1. Payment.validate()의 abstract 제거
2. 각 서브클래스 validate()를 rename → Pull Members Up (Skip 선택), 3회 반복
3. pull up된 메서드 본문을 Extract Method(All) → 나머지 중복 치환
4. 사용되지 않는 서브클래스 메서드 제거

**적용할까요?** (yes / no / 수정 요청)
  • 사용자가 yes → 실행 목록에 추가
  • 사용자가 no → 스킵
  • 사용자가 수정 요청 → 요청 반영 후 재제시

모든 후보 확인 후 최종 실행 목록을 보여주고 진행 여부 확인.

기법별 적용 실행 (공통 절차 4단계)

확정된 리팩토링을 하나씩 수행:

  1. 리팩토링 적용

대규모 범위 — Mikado Method 분기: 하나의 후보를 적용했을 때 컴파일 오류·테스트 실패가 연쇄적으로 발생할 수 있다. 30분(타임박스) 안에 수습되지 않으면 강행하지 않는다. 다음 절차로 전환한다. 사용자와 대화형으로 진행하며 자동 실행하지 않는다. M1. 변경을 되돌린다(git checkout -- <파일>). 컴파일이 깨진 상태에서는 IDE 자동 리팩터링이 동작하지 않으므로 항상 컴파일 가능한 상태로 복귀한다 M2. 실패 원인을 "이 변경의 전제 조건"으로 기록한다 (예: "OrderService가 Repository를 직접 생성함 → 생성자 주입으로 바꿔야 함") M3. 전제 조건과 목표의 의존 관계를 목록 또는 그래프로 사용자에게 제시한다 M4. 전제 조건 중 안전하게 완료할 수 있는 것부터 하나씩 적용·커밋한다 (각각이 독립 후보가 된다) M5. 전제 조건이 모두 해결되면 원래 후보로 돌아가 다시 적용한다. 다시 실패하면 M2로 돌아간다

커밋 메시지 형식:

  • refactor: extract method [메서드명] from [클래스명]
  • refactor: extract delegate [클래스명] from [원본클래스명]
  • refactor: move [설명] to [대상 클래스]
  • refactor: split phase [설명] in [클래스명]
  • refactor: split by abstraction layer [설명] in [클래스명]
  • refactor: split unrelated complexity [설명] from [클래스명]
  • refactor: pull up [메서드명] to [상위클래스명]
  • refactor: push down [설명] to [하위클래스명] and delegate
  • refactor: remove middle man [메서드명] in [클래스명]
  • refactor: extract common part of [기능A]/[기능B] into [템플릿 메서드명]

결과 보고

사용자에게 보고:

  • 적용된 리팩토링 목록

리팩토링 과정에서 발견된 추가 개선 기회를 제안:

추가로 발견된 개선 기회:
[발견 시에만 해당 항목 표시]
- /extract-method-object — [파일명]에서 지역 변수 얽힘으로 Extract Method 불가
- /replace-conditional-with-poly — [파일명]에 반복 switch/if-else [N]곳
- /introduce-parameter-object — [파일명]에 3개 이상 파라미터 그룹 반복
- /discover-value-object — [파일명]에 primitive 타입에 로직 집중
- /first-class-collection — [파일명]에 컬렉션+관련 로직 산재
- /lift-up-conditional — [파일명]에 동일 조건문 중복
- /separate-query-modifier — [파일명]에 값 반환과 부수효과 혼재
- /introduce-special-case — [파일명]에 동일 타입 null 검사가 [N]곳 반복
- /intent-revealing-names — [파일명]에 이름이 what만 나열하는 긴 메서드가 있어 이름 주도 관통 리팩토링(3단계 완전정직→extract→intent rename)이 유효합니다
적용할 기법을 선택하세요 (slash command 또는 skip)

FAILURE CONDITIONS

공통 실패 조건(계열별 승인 규칙 위반, 테스트 실패 방치, 테스트 수정, 커밋 단위, git add -A, heredoc 한글 메시지)은 ../../references/refactoring-procedure.md에 있다. 아래는 이 기법에 고유한 것만.

  • Local Tidying 기법 수행 (Guard Clauses, Reorder 등은 tdd-tidy 전담)
  • Split Phase 적용 시 중간 데이터 구조 없이 단계만 분리 (단계 간 결합 유발)
  • Push Down → Delegate 절차에서 상속을 제거하지 않고 종료 (임시 상속이 영구화됨)
  • Pull Up 시 IDE 중복 치환에 "Replace"를 선택해 서브클래스 본문이 superclass 호출로 바뀐 채 중복 확인을 건너뜀
  • 시그니처·파라미터 순서 변경을 한 커밋에 Expand와 Contract를 함께 넣어 수행 (절차는 ../../references/parallel-change.md)

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

동일한 결과를 내는 여러 조건문(OR 나열·중첩 AND)을 하나로 통합하고 의미 있는 boolean 메서드로 추출. "조건문 합쳐", "같은 결과 반환하는 if 정리", "중첩 if 평탄화", "/consolidate-conditional" 요청 시 사용. 단, 여러 메서드에 흩어진 동일 조건을 호출자 쪽으로 올리는 것은 /lift-up-conditional, 복잡한 조건식·분기를 메서드로 쪼개는 것은 /decompose-conditional이 적합. /consolidate-conditional [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

기능의 external behavior를 Cucumber 인수 테스트(주 검증층)로 구축 — .feature 실행으로 문서↔코드 드리프트를 구조적으로 차단, Four Layer(Steps→Protocol Driver→SUT), 태그 기반 가역 제외, 기존 JUnit 인수 테스트 이관. "인수 테스트 도입", "Gherkin을 실행 가능하게", "cucumber 셋업" 요청 시 사용. /cucumber-acceptance로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

복잡한 if/then/else의 조건식과 각 분기를 의미 있는 메서드로 추출하여 가독성 향상. "조건문 분해", "if 가독성", "복잡한 조건식에 이름 붙여", "/decompose-conditional" 요청 시 사용. 단, 같은 결과를 내는 조건문들을 하나로 합치는 것은 /consolidate-conditional, 타입별 분기를 클래스로 바꾸는 것은 /replace-conditional-with-poly가 적합. /decompose-conditional [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

Primitive Obsession 제거 — 검증·연산이 따라다니는 primitive 필드(금액+통화, 이메일 문자열 등)를 도메인 개념을 담은 Value Object로 치환. "값 객체 도입", "primitive obsession", "Money 클래스로", "/discover-value-object" 요청 시 사용. 단, 함께 전달되는 파라미터 묶음을 객체로 바꾸는 것은 /introduce-parameter-object, 컬렉션을 감싸는 것은 /first-class-collection이 적합. /discover-value-object [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

컬렉션 getter가 내부 List/Set을 직접 노출하는 것을 방지 — unmodifiable 반환 + add/remove 메서드 제공. "컬렉션 캡슐화", "getter가 List 그대로 노출", "unmodifiable로", "/encapsulate-collection" 요청 시 사용. 단, 컬렉션과 관련 로직을 전용 클래스로 뽑는 것은 /first-class-collection이 적합. /encapsulate-collection [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

암묵적 의존성(전역 변수·클래스 필드·싱글턴 접근)을 명시적 파라미터로 전환하여 메서드 투명성 향상. "숨은 의존성 드러내", "필드 대신 파라미터로", "전역 참조 제거", "/explicit-parameters" 요청 시 사용. 단, 파라미터가 많아져 묶어야 하면 /introduce-parameter-object, I/O와 계산 분리는 /segregate-functional-core가 적합. /explicit-parameters [commit-ref]로 호출.

The English summary is being prepared. Showing the original description.

msbaek/msbaek-claude-plugins8Updated Oct 16, 2026

See all skills by msbaek

Report a problem with this skill