ECMAScript 2025로 본 Lazy Evaluation — JavaScript와 Java에서 어떻게 다를까?
ECMAScript 2025에서 도입된 Iterator Helpers를 통해 자바스크립트에서도 Lazy Evaluation이 본격 지원되며, Java의 Stream과 유사한 방식으로 필요할 때만 연산이 수행되는 구조를 비교해 설명한 글입니다.
ECMAScript 2025로 본 Lazy Evaluation — JavaScript와 Java에서 어떻게 다를까?
2025년 6월, 자바스크립트의 새로운 표준 사양인 ECMAScript 2025 (ES2025)가 공식 채택되었습니다. 이번 사양에서 특히 눈에 띄는 기능 중 하나는 바로 Iterator Helpers입니다. map(), filter(), take() 같은 연산을 배열이 아닌 반복자(iterator) 수준에서 사용할 수 있도록 해주며, Lazy Evaluation(지연 평가) 개념을 언어 차원에서 도입했다는 점에서 의미가 큽니다.
이번 글에서는 자바스크립트에서 Lazy Evaluation이 무엇이고 어떻게 동작하는지 확인해보고, 이어서 Java에서는 같은 개념이 어떻게 다르게 작동하는지도 비교해보겠습니다.
🧠 JavaScript에서 Lazy Evaluation이란?
Lazy Evaluation은 말 그대로 “필요할 때까지 계산하지 않고 미뤄두는 방식”입니다. 일반적인 자바스크립트의 Array.prototype.map()이나 filter()는 즉시 모든 요소를 처리하지만, Iterator Helpers는 그렇지 않습니다.
const iter = [1, 2, 3, 4, 5, 6].values()
.map(x => {
console.log("map:", x);
return x * 10;
})
.take(3);
console.log([...iter]);
실행 결과
map: 1
map: 2
map: 3
[10, 20, 30]
map이 필요한 만큼만 실행되고 4, 5, 6은 아예 평가되지 않았습니다. 이처럼 take()나 find() 같은 최종 조건이 만족되면, 남은 요소는 더 이상 평가되지 않고 무시되는 구조가 바로 lazy evaluation입니다.
💡 참고: 이 기능은 현재 Node.js 22 이상에서
--harmony-iterator-helpers플래그를 켜야 사용할 수 있습니다.
🔎 조건을 걸면 평가 흐름은 어떻게 바뀔까?
자바스크립트의 Lazy Evaluation은 조건 필터링이 들어가면 조금 더 흥미로워집니다.
.filter()를 사용하면 값을 일단 계산한 뒤 조건을 통과하는지 확인해야 하기 때문에, 단순한 .map()만 있을 때보다 더 많은 입력이 평가될 수도 있습니다. 하지만 여전히 "필요 이상으로는 평가하지 않는다"는 Lazy Evaluation의 원칙은 그대로 유지됩니다.
const iter = [1, 2, 3, 4, 5, 6].values()
.map(x => {
console.log("map:", x);
return x * 2;
})
.filter(x => {
console.log("filter:", x);
return x > 5;
})
.take(2);
console.log([...iter]);
실행 결과
map: 1
filter: 2
map: 2
filter: 4
map: 3
filter: 6
map: 4
filter: 8
[6, 8]
map()과 filter()가 번갈아 실행되며 조건에 맞는 값을 찾고, take(2) 조건이 만족되면 나머지 요소는 아예 평가되지 않고 중단됩니다.
즉, 조건이 복잡해져도 lazy한 특성은 유지됩니다.
이러한 패턴은 Java의 filter().limit().collect() 체인에서도 동일하게 동작합니다.
🚀 그렇다면 Java에서는?
Java에서도 이와 유사한 Lazy Evaluation은 Stream API를 통해 지원됩니다. 마찬가지로 map(), filter(), limit() 등의 중간 연산은 실제로 실행되지 않고, collect(), findFirst() 같은 최종 연산이 호출되었을 때 비로소 평가가 시작됩니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> lazyResult = numbers.stream()
.peek(x -> System.out.println("map: " + x))
.map(x -> x * 10)
.limit(3)
.collect(Collectors.toList());
실행 결과
map: 1
map: 2
map: 3
Result: [10, 20, 30]
Java의 Stream 역시 Lazy Evaluation을 기반으로 하며, 중간 연산 체인이 최종 연산 시점에 비로소 평가됩니다.
⚡️ 반대로 Eager Evaluation은?
Lazy Evaluation이 아닌 일반적인 코드에서는 모든 연산이 즉시 수행됩니다. 아래는 Java에서 일반적인 for 루프를 이용한 Eager Evaluation의 예입니다.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> eagerResult = new ArrayList<>();
for (int x : numbers) {
System.out.println("map: " + x);
eagerResult.add(x * 10);
}
List<Integer> subList = eagerResult.subList(0, 3);
System.out.println("Result: " + subList);
실행 결과
map: 1
map: 2
map: 3
map: 4
map: 5
map: 6
Result: [10, 20, 30]
subList(0, 3)를 했지만 모든 요소가 처리된 후 잘라낸 것이므로, 연산량이나 메모리 측면에서는 비효율적일 수 있습니다.
✅ 마무리
이번 ES2025를 통해 자바스크립트에서도 본격적으로 Lazy Evaluation이 지원되기 시작했습니다. 기존에는 라이브러리나 사용자 정의 반복자 없이 직접 구현해야 했던 기능들을 이제는 언어 자체에서 지원하게 된 것이죠.
그리고 Java의 Stream API와도 매우 유사하게 동작하기 때문에, 두 언어를 병행 사용하는 개발자에게는 특히 직관적으로 느껴질 수 있습니다.
Lazy Evaluation은 성능 최적화와 리소스 관리에 매우 중요한 전략입니다. 앞으로 더 많은 언어와 런타임 환경에서 기본값처럼 사용될 가능성이 높기 때문에, 지금부터 익숙해져 두는 것이 좋습니다.
이 글은 Lazy Evaluation 시리즈의 첫번째 포스트입니다.
메타데이터
- post_id
- a6bce161cc91
- slug
- ecmascript-2025로-본-lazy-evaluation-javascript와-java에서-어떻게-다를까-a6bce161cc91
- url
- https://medium.com/@sunghyunroh/ecmascript-2025%EB%A1%9C-%EB%B3%B8-lazy-evaluation-javascript%EC%99%80-java%EC%97%90%EC%84%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8B%A4%EB%A5%BC%EA%B9%8C-a6bce161cc91
- canonical_url
- https://medium.com/@sunghyunroh/ecmascript-2025%EB%A1%9C-%EB%B3%B8-lazy-evaluation-javascript%EC%99%80-java%EC%97%90%EC%84%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8B%A4%EB%A5%BC%EA%B9%8C-a6bce161cc91
- author_url
- https://medium.com/@sunghyunroh
- status
- ok
- fetched_at
- 2026-08-18 16:16:47