跳到正文
cccskills
免费在 GitHub 公开

separate-query-modifier

값 반환과 부수효과가 혼재된 메서드를 Query(순수)와 Modifier(부수효과)로 분리(CQS). "쿼리와 명령 분리", "getter가 상태를 바꿔", "CQS", "/separate-query-modifier" 요청 시 사용. 단, I/O 전체를 계산에서 떼어내는 것은 /segregate-functional-core가 적합. /separate-query-modifier [commit-ref]로 호출.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md8.6 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Separate Query from Modifier Skill

값 반환과 부수효과가 혼재된 메서드를 Query(값 반환, 순수)와 Modifier(부수효과, void)로 분리하여 CQS 원칙 적용.

GOAL

  • 성공 = Query와 Modifier가 분리되어 커밋 완료됨
  • 값을 반환하면서 동시에 상태를 변경하는 메서드가 식별됨
  • CQS(Command-Query Separation) 위반 패턴 확인됨
  • 사용자 확인 후 Query와 Modifier 분리
  • 모든 테스트 통과

CONSTRAINTS

  • 계열: System-wide — 후보 제시 후 승인 받고 적용 (../../references/refactoring-procedure.md §0·§3-B)

Hard Rules

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

CQS 원칙

Command-Query Separation (CQS):

  • Query: 값을 반환하지만 상태를 변경하지 않음 (순수 함수)
  • Command (Modifier): 상태를 변경하지만 값을 반환하지 않음 (void)

예외: 원자적 연산 (CAS, pop 등)은 분리하면 thread-safety 파괴

적용 패턴

Separate Query from Modifier 리팩토링 단계:

  1. CQS 위반 메서드 식별

    • 값을 반환하면서 동시에 상태 변경
    • 부수효과(side effect)가 숨겨져 있음
  2. Query 메서드 추출

    • 상태 변경 없이 값만 반환
    • 순수 함수로 구현 (같은 입력 → 같은 출력)
    • 여러 번 호출해도 안전
  3. Modifier 메서드 추출

    • 상태만 변경하고 void 반환
    • 부수효과가 명시적으로 드러남
    • 메서드명으로 변경 의도 표현
  4. 호출자 코드 업데이트

    • Query와 Modifier를 순차 호출
    • 필요에 따라 Query 결과를 Modifier에 전달

Before/After 예시

// Before: 값 반환 + 부수효과 혼재
public class OrderService {
    // 총액을 반환하면서 동시에 할인 적용 (부수효과)
    public double getTotalAndApplyDiscount(Order order) {
        double total = order.calculateTotal();
        
        // 숨겨진 부수효과 - 상태 변경
        if (total > 1000) {
            order.setDiscount(0.1);  // 10% 할인
        }
        
        return total;  // 값 반환
    }
    
    // 사용
    public void processOrder(Order order) {
        double total = getTotalAndApplyDiscount(order);  // 부수효과가 숨겨짐
        System.out.println("Total: " + total);
    }
}

// After: Query와 Modifier 분리
public class OrderService {
    // Query: 값만 반환, 상태 변경 없음 (순수)
    public double getTotal(Order order) {
        return order.calculateTotal();
    }
    
    // Modifier: 상태만 변경, void 반환
    public void applyDiscountIfEligible(Order order) {
        double total = order.calculateTotal();
        if (total > 1000) {
            order.setDiscount(0.1);
        }
    }
    
    // 사용: 부수효과가 명시적으로 드러남
    public void processOrder(Order order) {
        applyDiscountIfEligible(order);  // 상태 변경이 명확
        double total = getTotal(order);   // 순수한 값 조회
        System.out.println("Total: " + total);
    }
}

또 다른 예시: Collection 연산

// Before: 요소를 반환하면서 제거 (부수효과)
public class TaskQueue {
    private List<Task> tasks = new ArrayList<>();
    
    // 다음 태스크를 반환하면서 큐에서 제거
    public Task getNextAndRemove() {
        if (tasks.isEmpty()) {
            return null;
        }
        Task next = tasks.get(0);
        tasks.remove(0);  // 부수효과
        return next;
    }
}

// After: Query와 Modifier 분리
public class TaskQueue {
    private List<Task> tasks = new ArrayList<>();
    
    // Query: 다음 태스크 조회만
    public Task peekNext() {
        if (tasks.isEmpty()) {
            return null;
        }
        return tasks.get(0);
    }
    
    // Modifier: 제거만 수행
    public void removeFirst() {
        if (!tasks.isEmpty()) {
            tasks.remove(0);
        }
    }
    
    // 사용
    public void processNext() {
        Task next = peekNext();
        if (next != null) {
            removeFirst();
            next.process();
        }
    }
}

예외: 원자적 연산

// 분리하면 안 되는 경우: thread-safety 파괴
public class AtomicCounter {
    private int value;
    
    // 분리하면 race condition 발생
    public int getAndIncrement() {
        return value++;  // 원자적 연산 - CAS
    }
    
    // 이런 경우는 CQS 예외로 유지
    // 분리 시: getValue() + increment() → thread-unsafe
}

적용 기준

Separate Query from Modifier를 적용해야 하는 경우:

  1. CQS 위반: 값을 반환하면서 동시에 상태 변경
  2. 숨겨진 부수효과: 메서드명이 get/calculate인데 상태 변경
  3. 테스트 어려움: 값 확인과 상태 확인을 동시에 해야 함
  4. 캐싱 불가: 부수효과 때문에 결과를 캐싱할 수 없음
  5. 재호출 위험: 같은 메서드를 여러 번 호출하면 문제 발생

적용하지 말아야 하는 경우:

  • 원자적 연산 (CAS, pop, getAndIncrement 등)
  • Thread-safety가 중요한 연산
  • 성능상 분리가 불가능한 경우

OUTPUT FORMAT

실행 절차

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

CQS 위반 메서드 후보 식별 (공통 절차 2단계)

대상 파일에서 다음 패턴을 찾는다:

  • 반환 타입이 void가 아닌 메서드
  • 메서드 내부에서 필드 변경 또는 setter 호출
  • 메서드명이 get/calculate/find인데 상태 변경
  • 동일 메서드를 여러 번 호출하면 다른 결과

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

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

## 리팩토링 후보 1: Separate Query from Modifier

**파일**: OrderService.java
**대상**: getTotalAndApplyDiscount() 메서드

**현재 코드**:
[메서드 전체 코드]

**CQS 위반 분석**:
- 반환: total (double)
- 부수효과: order.setDiscount(0.1) - 상태 변경

**제안 변경**:
1. Query 메서드 추출:
   - getTotal(Order) → double (순수)
   
2. Modifier 메서드 추출:
   - applyDiscountIfEligible(Order) → void (부수효과)
   
3. 호출자 코드 업데이트:
   - applyDiscountIfEligible(order);
   - double total = getTotal(order);

**적용할까요?** (yes / no / 수정 요청)

주의: 원자적 연산이 필요한 경우 적용하지 마세요.
  • 사용자가 yes → 실행 목록에 추가
  • 사용자가 no → 스킵
  • 사용자가 수정 요청 → 요청 반영 후 재제시

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

Query/Modifier 분리 실행 (공통 절차 4단계)

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

  1. Query 메서드 추출 (상태 변경 로직 제거)
  2. Modifier 메서드 추출 (반환 값 제거, void로 변경)
  3. 호출자 코드 업데이트
    • Query와 Modifier 순차 호출
    • 부수효과가 명시적으로 드러나도록

커밋 메시지 형식:

refactor: separate query from modifier in [클래스명].[메서드명]

결과 보고

사용자에게 보고:

  • 적용된 Query/Modifier 분리 목록

FAILURE CONDITIONS

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

  • 원자적 연산(CAS, pop)을 분리하여 thread-safety 파괴
  • 호출자 코드 수정 누락 (Query와 Modifier 모두 호출해야 함)
  • Query 메서드에 여전히 부수효과 남음 (완전히 순수해야 함)
  • Modifier가 값을 반환 (void여야 함)

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

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

中文简介正在准备中。当前显示原文说明。

msbaek/msbaek-claude-plugins82026年10月16日 更新

查看 msbaek 的全部技能

报告此技能的问题