본문으로 건너뛰기
cccskills
무료GitHub에 공개

intent-revealing-names

하나의 긴 메서드를 grouping(Reorder/Chunk)·comment로 정돈해 책임을 드러낸 뒤 Arlo Belshee 6단계 네이밍(완전정직 이름 → Extract Method → Intent Revealing → Domain Abstraction)을 하나의 연속 흐름으로 관통하는 self-contained 워크플로우. "이 긴 메서드 이름이 하는 일을 안 드러낸다", "메서드 이름부터 정직하게 만들어서 쪼개줘", "이름 주도로 이 메서드 리팩토링" 요청 시 사용. 단, 여러 파일에 흩어진 개별 이름 스멜의 배치 교체는 /naming-process, 클래스 분리·도메인 이동 같은 대규모 구조 변경은 /system-wide-refactoring이 적합. /intent-revealing-names로 호출.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md18.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Intent-Revealing Names — 이름 주도 관통 리팩토링

이름 없는 긴 메서드 하나를 대상으로, grouping → comment → 정직한 이름 → extract → 의도 드러내는 이름 → 도메인 추상화를 하나의 연속 흐름으로 관통한다.

이름을 신뢰한다면 어떨까요? 이름을 읽어도 메서드를 읽을 필요가 없는 세상을 상상해 보세요. — Arlo Belshee

핵심 명제

  • intent(의도) = why(왜). 좋은 이름은 코드가 *무엇을 하는가(what)*가 아니라 *왜 부르는가(why)*를 드러낸다.
  • 이름은 리팩터링의 결과가 아니라 진단 수단이다. 이름을 정직하게 만들려는 시도 자체가 구조적 문제(SRP 위반, 원시 집착)를 드러내고, 그 진단이 Extract Method·값 객체 도입으로 이어진다.
  • comment는 extract될 메서드 이름의 초안이다 (Kent Beck: "좋은 이름이 떠오르지 않으면 먼저 주석으로 의도를 표현하라 — Extract Method의 전 단계").
  • 전환점은 3 → 5단계: 3단계까지 "무엇을(what)"을 정직하게 누적하고, 5단계에서 "왜(why)"로 전환한다. 이 전환이 있어야 "이름만 읽으면 본문을 읽을 필요가 없는" 상태가 된다.

GOAL

  • 성공 = 대상 긴 메서드가 목차(table of contents)로 축약되고, 각 이름이 why를 드러내며, 모든 테스트가 통과하고, 되돌리기 쉬운 단위 커밋으로 남는다.
  • 상위 메서드의 이름이 what 나열이 아니라 why(호출 이유)를 드러냄
  • 추출된 각 private helper가 하나의 식별 가능한 책임만 수행 (Composed Method)
  • 남은 원시 집착(Primitive Obsession)이 식별되어 후속 스킬로 인계됨 (승격 자체는 이 스킬 밖)
  • 동작 변경 없이 구조만 개선됨

CONSTRAINTS

  • 계열: Tidy — 후보 보고 후 승인 없이 적용 (../../references/refactoring-procedure.md §0·§3-A)

Hard Rules (공유)

  • 동작 변경 금지 — 구조·이름만 개선, 기능 변경 없음
  • 테스트 수정 금지 — 변경이 테스트를 실패시키면 즉시 되돌리기
  • 작은 단계 + 매 단계 테스트 — 한 번에 하나의 변형만 적용하고 green 확인
  • 명시적 git add — git add -A 금지, 변경 파일만 명시
  • 사용자 확인 — 대상 메서드 확정과 5·6단계 진입은 사용자와 합의

이 스킬 고유 규칙

  • 대상은 하나의 긴 메서드 — 파일 전체 검토가 아니라, 지정된(또는 가장 냄새나는) 긴 메서드 하나에 집중
  • rename을 마지막에 일괄 수행하지 않는다 — 이름 진화(3단계)가 extract(4단계)를 유발하고, extract 후 다시 rename(5단계)한다. 이 인터리빙이 이 스킬의 본질이다
  • Extract는 국소(local)에 한정 — 대상 메서드를 같은 클래스 내 private helper로 분할하는 것까지. 클래스 분리·도메인 이동 등 대규모 구조 변경은 /system-wide-refactoring 전담 (이 스킬은 self-contained 연속 흐름)
  • 6단계는 가르치되 인계 — 운영상 이 스킬은 5단계(Intent Revealing)까지 실행하고, 6단계(Domain Abstraction)는 방향만 보여준 뒤 원시 집착을 지목해 값 객체·도메인 이동을 후속 스킬(/discover-value-object 등)로 인계한다. 승격을 이 스킬 흐름 안에서 실행하지 않는다

6단계 관통 프로세스 — "주문 처리" 예제

e-commerce "주문 처리" 예제로 6단계를 처음부터 끝까지 진행한다. 각 단계는 목적 + 코드 상태 + 다음 단계로 넘어가게 만드는 트리거(냄새) 로 구성된다.

출발점 — 이름 없는 긴 메서드

OrderHelper.process(cart)가 재고 확인 → 재고 차감 → 결제 → 확정 메일을 한 메서드에서 처리한다. -Helper 접미사와 process는 정보가 없으면서 정보가 있는 것처럼 보이는, 가장 위험한 종류의 이름이다.

public class OrderHelper {
    public void process(Cart cart) {
        for (CartItem item : cart.getItems()) {
            int stock = stockDao.getStock(item.getProductId());
            if (stock < item.getQuantity()) throw new RuntimeException("out of stock");
        }
        for (CartItem item : cart.getItems())
            stockDao.decrease(item.getProductId(), item.getQuantity());
        BigDecimal total = BigDecimal.ZERO;
        for (CartItem item : cart.getItems())
            total = total.add(item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
        paymentGateway.charge(cart.getUserId(), total);
        emailSender.send(cart.getUserEmail(), "주문이 완료되었습니다");
    }
}

단계 A — 본문 정돈으로 책임 가시화 (grouping + comment)

사용자 요청 1·2번. 기법 정의는 /tdd-tidy(tdd-blue의 Local Tidying)에서 인용 — 여기서는 대상 메서드에 국소 적용한다.

A-1. Reorder (Reading/Cohesion Order): 변수 선언을 사용 위치 가까이로(Reading Order), 관련 로직끼리 인접하게(Cohesion Order, Step Down Rule). A-2. Chunk: 빈 줄로 논리 블록을 분리한다. A-3. Explaining Comment: 각 블록에 "무엇을/왜"를 한 줄로 붙인다. 이 주석이 곧 추출될 메서드 이름의 초안이다.

public void process(Cart cart) {
    // 재고 확인 — 부족하면 주문 거절
    for (CartItem item : cart.getItems()) {
        int stock = stockDao.getStock(item.getProductId());
        if (stock < item.getQuantity()) throw new RuntimeException("out of stock");
    }

    // 재고 차감
    for (CartItem item : cart.getItems())
        stockDao.decrease(item.getProductId(), item.getQuantity());

    // 결제 — 합계 계산 후 청구
    BigDecimal total = BigDecimal.ZERO;
    for (CartItem item : cart.getItems())
        total = total.add(item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())));
    paymentGateway.charge(cart.getUserId(), total);

    // 확정 메일 발송
    emailSender.send(cart.getUserEmail(), "주문이 완료되었습니다");
}

트리거: 주석 붙은 책임 블록이 4개다 — 이 메서드가 여러 일을 한다는 사실이 이제 눈에 보인다.

단계 B — 상위 메서드 이름을 정직하게 진화 (6단계 1→2→3)

이름 축을 정직하게 단계적으로 바꿔, 이름 스스로가 SRP 위반을 드러내게 만든다.

1. Obvious Nonsense: process() → applesauce(). 그럴듯하지만 비어 있는 이름을, 일부러 명백히 틀린 이름으로 바꿔 "여기 이름이 필요하다"를 코드가 명시하게 한다. -er/-Utils/라이프사이클(onCreate) 이름을 제거하는 단계이기도 하다.

2. Honest: applesauce() → probably_updateStockAndCharge_andStuff(). 3부 규칙 — probably_(불확실) + 파악한 동작 + _andStuff(아직 못 파악). 완벽함이 아니라 오해를 주지 않는 것이 목표.

3. Completely Honest: checkStockAndDeductStockAndChargePaymentAndSendConfirmationEmail(). 단계 A의 comment들을 그대로 이어 붙인 이름. probably_·_andStuff를 모두 제거하고 하는 일 전부(what)를 담는다. 길고 어색해도 상관없다.

트리거: 이름에 And가 네 번 등장한다 — 책임이 네 개, 즉 SRP 위반을 이름이 드러낸다. 이것이 다음 단계(extract)의 근거다.

팀 합의 노트: applesauce·probably_..._andStuff 같은 중간 이름을 실제 커밋할지는 팀 합의 사항이다. "아직 이해하지 못했다"를 정직하게 남기는 편이 그럴듯한 거짓 이름보다 낫다는 입장(Emily Bache 데모)이 있으나, 개인 작업이면 3단계에서 한 번에 커밋해도 된다.

단계 C — 드러난 책임대로 Extract (6단계 4: Does the Right Thing, composed method)

완전정직 이름의 각 And 조각 = 단계 A의 각 comment 블록. 그 블록들을 comment를 이름으로 승격하여 private helper로 추출한다. 상위 메서드는 4줄짜리 목차가 된다. (Arlo Belshee 정본 4단계 "Does the Right Thing"을 여기서 Extract로 흡수한다 — 각 조각이 비로소 제 일만 하게 되므로.)

public void checkStockAndDeductStockAndChargePaymentAndSendConfirmationEmail(Cart cart) {
    ensureStockAvailable(cart);
    deductStock(cart);
    chargePayment(cart);
    sendConfirmationEmail(cart);
}

private void ensureStockAvailable(Cart cart) { /* 재고 확인 블록 */ }
private void deductStock(Cart cart) { /* 재고 차감 블록 */ }
private void chargePayment(Cart cart) { /* 결제 블록 */ }
private void sendConfirmationEmail(Cart cart) { /* 확정 메일 블록 */ }

지역 변수가 상호 의존해 추출이 불가능하면 먼저 /replace-temp-with-query·/extract-method-object를 적용한다(이름으로 인용).

트리거: 구조는 정직해졌지만 상위 이름은 여전히 "무엇을 하는지"의 나열이다. "왜 이 메서드를 부르는지"는 아직 드러나지 않는다.

단계 D — 의도를 드러내기 (6단계 5) ★핵심 전환점

호출하는 쪽(예: CheckoutController의 "주문하기" 버튼 처리)을 살펴보면, 이 메서드의 진짜 목적은 "고객이 주문을 낸다"임을 알 수 있다. what이 아니라 why로 이름을 바꾼다.

// 호출부: checkStockAndDeductStockAndChargePaymentAndSendConfirmationEmail(cart);
//      ↓
//         placeOrder(cart);

public class OrderService {          // OrderHelper → OrderService
    public void placeOrder(Cart cart) {
        ensureStockAvailable(cart);
        deductStock(cart);
        chargePayment(cart);
        sendConfirmationEmail(cart);
    }
}

이것이 "intent = why"의 실체다. 이름은 더 짧아지고, 호출 코드를 읽을 때 자연스럽게 이해된다.

트리거: 이름은 좋아졌지만 내부에 String productId, int quantity, BigDecimal total 같은 원시 타입이 분산되어 있다 — 원시 집착(Primitive Obsession).

단계 E — 도메인 추상화 (6단계 6)

여기서부터는 무거운 구조 변경이라 이 스킬의 범위를 벗어난다 — 이 스킬은 5단계까지 실행하고 6단계는 인계한다. 값 객체·First Class Collection·Functional Core 분리는 각각 /discover-value-object, /introduce-parameter-object, /first-class-collection, /segregate-functional-core로 인계한다.

아래 코드는 후속 스킬들로 도달하는 목표 end-state(aspirational)이며, 이 스킬에서 전부 실행하지 않는다. 이 스킬의 역할은 5단계까지 완성한 뒤 원시 집착을 가리키고, 실제 승격은 인용된 후속 스킬로 넘기는 것이다 (클래스 분리·도메인 이동은 이 스킬 범위 밖 — FAILURE CONDITIONS 참조).

public Order placeOrder(Cart cart) {
    Order order = cart.toOrder();          // Order 애그리거트
    inventory.reserve(order.lines());      // 재고 확인+차감 → Inventory 도메인
    payment.charge(order.total());         // total() → Money 값 객체
    notifier.notifyOrderPlaced(order);
    return order;
}
이전 (원시 집착)이후 (도메인 개념)
String productId + int quantity 반복OrderLine 값 객체
BigDecimal total 산재Money 값 객체 (order.total())
stockDao.getStock()/decrease() 절차 호출Inventory.reserve(lines)
Cart를 절차적으로 순회·변환Order 애그리거트 (cart.toOrder())

이제 placeOrder의 본문은 하나의 유비쿼터스 언어 안에서 비즈니스 규칙을 읽는 산문이 된다.

6단계 요약

단계이 스킬의 활동초점예제 이름
A. Tidy-prep (6단계 이전 준비)Reorder + Chunk + Comment책임 가시화// 재고 확인 등 4개 블록
B1. Obvious Nonsense나쁜 이름 제거—applesauce()
B2. Honest파악한 사실만 정직하게what(부분)probably_updateStockAndCharge_andStuff()
B3. Completely Honest하는 일 전부what(전부)checkStock...AndSendConfirmationEmail()
C. ExtractSRP 위반대로 분리Composed MethodensureStockAvailable() + 3개
D. Intent Revealing ★무엇이 아니라 왜whyplaceOrder(cart)
E. Domain Abstraction (범위 밖 — 인계)원시 집착 → 도메인 언어도메인후속 스킬로 인계 (Order, Money, Inventory)

다른 스킬과의 경계

이 스킬은 self-contained하지만, 각 기법의 정의는 재기술하지 않고 이름으로 인용한다.

  • grouping/comment 기법 상세 → /tdd-tidy(tdd-blue): Reorder, Chunk, Explaining Comment, Normalize Symmetries, Split Loop 등
  • 대규모 Extract / 클래스 분리 / 도메인 이동 → /system-wide-refactoring. 이 스킬은 국소 private helper 추출까지만
  • 개별 이름 rename 카탈로그(Naming Smells 탐지) → /naming-process. 이 스킬은 "하나의 메서드를 6단계로 관통"이 목적 (개별 이름 스캔이 아님)
  • 값 객체/컬렉션/함수형 코어 승격 상세 → /discover-value-object, /introduce-parameter-object, /first-class-collection, /segregate-functional-core

OUTPUT FORMAT

실행 절차

1. 대상 메서드 확정

  • 인자로 파일:메서드가 오면 그 메서드를, commit-ref가 오면 그 diff의 Java 파일에서 가장 긴/냄새나는 메서드를 후보로 제시
  • 인자가 없으면 git diff --name-only -- '*.java'(+--cached)의 변경 파일에서 20줄 이상 메서드를 찾아 후보 제시
  • 테스트 파일(*Test.java 등)은 제외
  • 후보를 보여주고 대상 하나를 사용자와 확정
관통 리팩토링 대상 후보:
- OrderHelper.process(Cart) — 15줄, 책임 4개 추정 (재고확인/차감/결제/메일)
이 메서드로 진행할까요?

2. 단계 A~E를 순서대로, 작은 단계로 적용

  • A: Reorder → Chunk → Comment (책임 블록 가시화)
  • B: 상위 메서드 이름을 1→2→3단계로 진화 (완전정직까지)
  • C: comment 블록을 이름으로 승격해 private helper 추출 (IDE Extract Method)
  • D: 호출부를 근거로 상위 메서드를 intent-revealing rename (IDE Rename — 참조 자동 갱신). 클래스명 -Helper/-er도 이때 함께
  • E: (실행 안 함 — 인계) 5단계 완성 후 남은 원시 집착을 지목하고, 값 객체·도메인 이동을 후속 스킬(/discover-value-object 등)로 제안한다. 승격은 이 스킬에서 수행하지 않는다

각 단계마다: 변경 → 테스트 실행(./gradlew test 또는 mvn test) → green이면 진행, red면 즉시 되돌리기.

3. 커밋 — 되돌리기 쉬운 단위로

의미 단위로 나눠 커밋하는 것을 권장한다 (각 단계가 독립적으로 되돌려짐):

  • refactor: tidy-prep OrderHelper.process — group & comment responsibilities (단계 A)
  • refactor: extract composed methods from OrderHelper.process (단계 C, B의 이름 진화 포함)
  • refactor: reveal intent — process → placeOrder (단계 D)

(단계 E의 도메인 승격은 이 스킬에서 커밋하지 않는다 — 후속 스킬로 인계.)

  • 커밋 메시지는 docs/reviewable-commits.md(없으면 ${CLAUDE_PLUGIN_ROOT}/references/reviewable-commits.md) 표준을 따른다. subject는 refactor: 접두사, body에 Why(이름이 무엇을 드러냈고 왜 이렇게 분리/명명했는지)를 담는다. 형식은 그 표준이 유일한 출처이므로 여기서 재기술하지 않는다. 길이는 ../../references/commit-style.md의 간결성 규칙(제목 + 핵심 bullet 2~4줄)을 따른다.
  • 한글 메시지는 임시 파일 + git commit -F (-m "한글" 금지 — 깨짐).
  • 최소 요건이 급하면 전체를 단일 refactor: 커밋으로 마무리해도 되나, 단계별 분리가 리뷰·롤백에 유리하다.

4. 완료 보고

  • 이름 진화 경로 요약 (process → applesauce → … → placeOrder)
  • 추출된 helper 목록과 각 책임
  • 테스트 결과
  • 남은 원시 집착과 인계할 후속 스킬 제안 (/discover-value-object 등)

FAILURE CONDITIONS

이 중 하나라도 발생하면 작업 실패로 간주:

  • 리팩토링 후 테스트가 실패함
  • rename을 맨 마지막에 일괄 수행함 (3단계 완전정직 이름이 extract를 유발하는 인터리빙을 건너뜀)
  • 3단계(Completely Honest)를 건너뛰고 곧장 intent-revealing 이름으로 건너뜀 (SRP 위반 진단 과정 생략)
  • 별도 브랜치를 만들거나 PR을 생성함 (self-contained 위반 — 대규모는 /system-wide-refactoring)
  • 클래스 분리·도메인 이동 같은 대규모 구조 변경을 이 스킬에서 수행 (경계 침범)
  • 단계 E(도메인 승격)를 후속 스킬로 인계하지 않고 이 스킬 흐름 안에서 직접 실행 (5단계까지만 실행이 원칙)
  • git add -A 사용 / 동작 변경 발생 / 사용자 확인 없이 5단계(Intent Revealing) 진입

더 읽을거리 (비-load-bearing)

이 스킬은 아래 vault 문서를 소스로 작성되었다. 런타임 의존성이 아니며, 없어도 스킬은 완전하게 동작한다.

  • vault: Intent Revealing Names.md — Arlo Belshee 6단계 프로세스를 "주문 처리" 예제로 관통한 원 노트
  • Emily Bache, Trustworthy Code with Naming as a Process — Arlo Belshee 기법 라이브 데모
  • Robert C. Martin, Clean Code Ch.4 Meaningful Names — Intention-Revealing Names 원개념

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 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의 스킬 모두 보기

이 스킬의 문제 신고