1. 들어가며
Java Stream API의 선언적 처리 방식과 지연 평가(Lazy Evaluation)는 코드의 가독성과 유지보수성을 높여준다. 그러나 복잡한 필터 체이닝을 구성할 때 내부 동작 원리를 고려하지 않으면 예상치 못한 성능 저하를 초래할 수 있다. 본 글에서는 Stream 필터의 효율을 결정짓는 두 가지 핵심 개념인 선택도와 비용을 분석하고, 이를 통해 최적의 필터 배치 전략을 도출해 보겠다.
2. 필터 체이닝의 실생활 비유: 기업의 채용 단계
Stream 필터 체이닝의 성능 이슈를 이해하기 위해 기업의 채용 과정을 예로 들어보겠다. 100명의 지원자 중 단 1명의 최종 합격자를 선발해야 하는 상황에서 채용 담당자는 다음과 같은 단계를 구성한다.
- 서류 검토: 학력 및 경력 기준을 통해 50명을 선별한다. (수행이 매우 빠르고 쉬움)
- 온라인 테스트: 코딩 테스트 등으로 20명을 추려낸다. (중간 정도의 리소스 소요)
- 전화 인터뷰: 직무 역량을 확인하여 5명을 선별한다. (상당한 시간과 노력이 필요함)
- 대면 면접: 심층 면접을 통해 최종 1명을 선발한다. (가장 많은 비용과 자원이 소요됨)
이 과정의 핵심은 비용이 낮은 단계부터 적용하여 후보군을 빠르게 줄이고, 비용이 높은 단계는 최소한의 대상에게만 적용하는 구조에 있다. 100명 전원을 대면 면접하는 것보다 이 방식이 훨씬 효율적인 것은 자명하다. Stream API의 필터 체이닝 역시 이와 동일한 원리로 동작해야 한다.
3. 성능 최적화를 위한 두 가지 핵심 지표
3.1. 선택도 (Selectivity)

선택도는 전체 데이터 중 필터 조건을 만족하여 통과하는 데이터의 비율을 의미한다.
- 낮은 선택도: 많은 데이터를 걸러내고 적은 데이터만 통과시킨다. (효율적 거름망)
- 높은 선택도: 대부분의 데이터가 필터를 통과한다. (성긴 거름망)
3.2. 비용 (Cost)
비용은 필터 조건을 평가하는 데 필요한 연산 리소스(CPU, 메모리, 네트워크 등)를 뜻한다. 상대적인 기준으로 구분하면 다음과 같다.
- 🟢 저비용: 단순 비교 연산 (예: num > 0)
- 🟡 중비용: 문자열 연산, 정규식 처리 (예: str.contains("Java"))
- 🔴 고비용: 외부 데이터베이스 조회, 외부 API 호출, 복잡한 암호화 알고리즘 등
4. 필터 배치 전략: 왜 선택도가 낮은 것이 우선인가?
성능 최적화의 첫 번째 전략은 비용이 큰 필터를 최대한 적게 실행하는 것이다. 이를 위해서는 선택도가 낮은(많이 걸러내는) 필터를 앞단에 배치하여 뒤쪽 필터로 넘어가는 데이터의 양을 최소화해야 한다. 다음은 1,000개의 데이터가 입력되었을 때 비용이 동일한(각 10분 소요) 두 필터의 순서에 따른 연산 횟수 비교이다.
- A 필터: 선택도 0.1 (900개 제거)
- B 필터: 선택도 0.9 (100개 제거)
| 구조 | 연산 흐름 | 총 연산 횟수 |
| A → B | 1,000번(A 실행) → 100번(B 실행) | 1,100번 |
| B → A | 1,000번(B 실행) → 900번(A 실행) | 1,900번 |
동일한 결과를 도출함에도 불구하고, 선택도가 낮은 필터를 앞에 배치한 구조가 연산 횟수를 획기적으로 줄여준다. 즉, 앞쪽 필터가 얼마나 엄격하게 데이터를 걸러내느냐에 따라 전체 시스템의 연산 효율이 결정되는 것이다.
5. 정량적 판단 기준: Filter Overhead
각 필터의 효율성을 객체화하여 판단하기 위해 Filter Overhead라는 개념을 사용한다. 이는 각 필터의 비용과 선택도를 곱한 값이다.

이 수식이 시사하는 바는 명확하다.
- 비용이 낮고 선택도가 낮을수록(작은 비용으로 많이 걸러낼수록) Overhead는 작아지며, 이는 매우 효율적인 필터임을 뜻한다.
- 비용이 높고 선택도가 높을수록(큰 비용을 쓰면서도 거르지 못할수록) Overhead는 커지며, 이는 비효율적인 필터임을 뜻한다.
6. 결론: 최적의 필터 체이닝 설계
Java Stream의 성능을 극대화하기 위해서는 전체 파이프라인에서 Filter Overhead가 낮은 필터부터 순서대로 배치해야 한다.
- 단순한 논리 비교나 메모리 내 연산처럼 비용이 낮고 강력하게 걸러내는 필터를 최우선으로 배치한다.
- 외부 시스템 연동이나 복잡한 계산이 필요한 고비용 필터는 앞선 필터들에 의해 충분히 정제된 소수의 데이터에만 적용되도록 뒤쪽으로 배치한다.
이러한 전략적 배치는 불필요한 연산을 원천적으로 차단하고 전체 처리량을 최적화하는 가장 과학적인 방법이다.
'Java > Core' 카테고리의 다른 글
| [Stream-4][Optimization] 실무 적용과 성과 증명 전략 (0) | 2025.12.27 |
|---|---|
| [Stream-3][Optimization] Filter Overhead를 활용한 성능 개선 사례 (0) | 2025.12.27 |
| [Stream-1][Optimization] 선언적 프로그래밍과 지연 평가(Lazy Evaluation) (0) | 2025.12.27 |
| [Basic-2] 제네릭과 제네릭 메서드 (0) | 2025.08.28 |
| [Basic-1] Pass by Value와 Pass by Reference (0) | 2025.03.28 |
