本文へ移動
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(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

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으로 변환하여 오히려 가독성이 떨어짐
  • 인덱스 기반 접근이 필요한 루프를 변환함

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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

日本語の概要は準備中です。原文の説明を表示しています。

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

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

日本語の概要は準備中です。原文の説明を表示しています。

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

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

日本語の概要は準備中です。原文の説明を表示しています。

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

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月1日 更新

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

日本語の概要は準備中です。原文の説明を表示しています。

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

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

日本語の概要は準備中です。原文の説明を表示しています。

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

msbaek のスキルをすべて見る

このスキルの問題を報告する