跳到正文
cccskills
免费在 GitHub 公开

replace-loop-with-pipeline

명령형 for/while 루프를 Stream API/Collection Pipeline으로 변환하여 데이터 흐름 의도 명확화. "스트림으로 바꿔", "루프를 파이프라인으로", "for문 stream", "/replace-loop-with-pipeline" 요청 시 사용. 단, 루프 안 로직이 컬렉션 클래스로 모여야 하면 /first-class-collection이 적합. /replace-loop-with-pipeline [commit-ref]로 호출.

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

查看安装方法

包含的文件(1)

  • SKILL.md6.6 KB

SKILL.md(原文)

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

Replace Loop with Pipeline

GOAL

명령형 루프를 Stream API/Collection Pipeline으로 변환하여:

  • what vs how: Pipeline은 "무엇을" 하는지, 루프는 "어떻게" 하는지 표현
  • 필터링/변환/집계의 의도가 메서드 체인으로 드러남
  • 중간 변수/플래그 변수 제거

CONSTRAINTS

  • 계열: Tidy — 후보 보고 후 승인 없이 적용 (../../references/refactoring-procedure.md §0·§3-A)
  • 동작 변경 금지: 구조 개선만 수행 (기능 변경 없음)
  • 테스트 수정 금지: 구조 변경이 테스트를 실패시키면 되돌리기
  • 명시적 git add: git add -A 금지, 변경된 파일만 명시

적용 패턴

패턴 1: 필터링 + 변환

// Before
List<String> result = new ArrayList<>();
for (Order order : orders) {
    if (order.isActive()) {
        result.add(order.getCustomerName());
    }
}

// After
List<String> result = orders.stream()
    .filter(Order::isActive)
    .map(Order::getCustomerName)
    .toList();

패턴 2: 집계

// Before
int total = 0;
for (LineItem item : items) {
    if (item.getQuantity() > 0) {
        total += item.getPrice() * item.getQuantity();
    }
}

// After
int total = items.stream()
    .filter(item -> item.getQuantity() > 0)
    .mapToInt(item -> item.getPrice() * item.getQuantity())
    .sum();

패턴 2-1: 집계 — 객체 타입 (BigDecimal 등)

primitive가 아닌 타입은 sum()이 없으므로 reduce(항등원, 누적 연산)을 쓴다. 금액(BigDecimal) 합산이 대표 사례:

// Before
BigDecimal total = BigDecimal.ZERO;
for (Line line : lines) {
    total = total.add(line.amount());
}

// After
BigDecimal total = lines.stream()
    .map(Line::amount)
    .reduce(BigDecimal.ZERO, BigDecimal::add);

패턴 3: 검색 (첫 번째 매칭)

// Before
Employee found = null;
for (Employee e : employees) {
    if (e.getDepartment().equals("Engineering")) {
        found = e;
        break;
    }
}

// After
Optional<Employee> found = employees.stream()
    .filter(e -> e.getDepartment().equals("Engineering"))
    .findFirst();

패턴 4: 존재 여부 확인

// Before
boolean hasOverdue = false;
for (Invoice invoice : invoices) {
    if (invoice.isOverdue()) {
        hasOverdue = true;
        break;
    }
}

// After
boolean hasOverdue = invoices.stream()
    .anyMatch(Invoice::isOverdue);

패턴 5: 그룹핑

// Before
Map<String, List<Employee>> byDept = new HashMap<>();
for (Employee e : employees) {
    byDept.computeIfAbsent(e.getDepartment(), k -> new ArrayList<>()).add(e);
}

// After
Map<String, List<Employee>> byDept = employees.stream()
    .collect(Collectors.groupingBy(Employee::getDepartment));

적용 기준

적용 대상

  • 컬렉션 순회 + 필터링/변환/집계/검색 패턴
  • 중간 변수(result, total, found)에 결과를 누적하는 루프
  • 플래그 변수(boolean found = false)로 제어하는 루프
  • 중첩 루프에서 내부 루프가 독립적 검색/필터인 경우
  • computeIfAbsent + add 패턴의 그룹핑 루프

적용 제외

  • 부수효과가 핵심인 루프: DB 저장, 로깅 등 각 요소마다 side effect 수행 (forEach로만 바꾸는 것은 가치 없음)
  • 인덱스 기반 접근 필수: list.get(i-1) 비교, 인접 요소 참조 등
  • break/continue 조건이 복잡: Stream으로 변환하면 오히려 난해
  • 성능 크리티컬 루프: primitive 배열 대량 처리 등 Stream 오버헤드가 문제
  • Java 8 미만 프로젝트: Stream API 사용 불가
  • 단순 forEach 전환: for → stream().forEach()는 가독성 이점 없음

OUTPUT FORMAT

실행 절차

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

후보 식별 (공통 절차 2단계)

  • 루프 패턴 탐지:
    • new ArrayList<>() + for + add() → filter/map + toList
    • int/long sum = 0 + for + += → mapToInt + sum
    • T acc = 항등원 + for + 재대입 누적 (BigDecimal 등 객체 타입) → map + reduce(항등원, 누적 연산)
    • T found = null + for + break → filter + findFirst
    • boolean flag = false + for + break → anyMatch/noneMatch
    • computeIfAbsent + for → groupingBy
  • 각 후보에 대해:
    • 파일명 및 라인 번호
    • 변환 유형 (필터링/집계/검색/존재확인/그룹핑)
    • Before/After 코드 미리보기

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

발견된 후보 3개:

1. OrderService.java:30-36
   유형: 필터링 + 변환
   → orders.stream().filter(...).map(...).toList()

2. ReportService.java:50-55
   유형: 집계 (합계)
   → items.stream().mapToInt(...).sum()

3. UserService.java:20-27
   유형: 검색
   → users.stream().filter(...).findFirst()

→ 승인 없이 적용 (Tidy 계열)

리팩토링 적용 (공통 절차 4단계)

  • 루프를 해당 Stream Pipeline으로 변환
  • 중간 변수/플래그 변수 제거
  • 필요시 import 추가 (java.util.stream.Collectors 등)

커밋 메시지: refactor: replace loop with pipeline in <클래스명> (공통 절차 6단계)

출력 예시

완료: Replace Loop with Pipeline

변경 내용:
- OrderService.java:30-36
  필터링+변환: for+if+add → stream().filter().map().toList()

- ReportService.java:50-55
  집계: for+if+= → stream().filter().mapToInt().sum()

테스트: 모든 테스트 통과 (23 tests)
커밋: refactor: replace loop with pipeline in OrderService, ReportService

FAILURE CONDITIONS

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

  • 부수효과가 핵심인 루프를 단순 forEach로 변환함
  • Stream으로 변환하여 오히려 가독성이 떨어짐
  • 인덱스 기반 접근이 필요한 루프를 변환함

评价

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

评论

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

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

提交后会立即公开。请勿填写邮箱、电话等个人信息。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 的全部技能

报告此技能的问题