주요 포인트 한눈에 보기
JavaScript의 reduce() 함수는 배열을 하나의 값으로 축약하는 기능을 넘어서, 초심자에게는 복잡하게 느껴질 수 있는 데이터 변환을 단계별로 누적값과 반환값으로 처리하는 도구입니다. 이 글에서는 완전 기초 개념부터 “왜 이렇게 동작하는지”, “어떤 상황에서 reduce를 써야 하는지”, “map/filter와 어떻게 다른지” 등을 초심자도 이해할 수 있도록 쉬운 비유와 상세 예시를 통해 설명합니다.
- reduce란 무엇인가?
- reduce 기본 구조와 동작 원리
- 실무와 초심자를 위한 대표 활용 패턴
- 고급 활용: 조건 분기 & 비동기 흐름 제어
- map / filter / forEach / reduce 비교 테이블
- 정리
- FAQ
reduce란 무엇인가?
reduce는 “배열의 존재하는 요소를 인덱스 순서로 처리해 누적 결과를 반환하는 메서드”입니다. 초심자는 “하나의 값으로 만든다”는 표현 때문에 어렵게 느끼지만, 실제로는 아래와 같이 생각하면 훨씬 쉽습니다.
비유: 쇼핑할 때 장바구니에 물건을 하나씩 넣으며 총합을 계산하는 것과 같습니다. 물건(배열 요소)을 하나씩 꺼내어 현재 총합(accumulator)과 합쳐가는 과정이 바로 reduce입니다.

reduce 기본 구조와 동작 원리
reduce 구조 전체를 한 눈에 보기
array.reduce((accumulator, currentValue, currentIndex, array) => {
// 누적 방식 정의
return 새로운_누적값;
}, 초기값);
위 코드의 각 파라미터가 무엇을 의미하는지 초심자도 이해할 수 있도록 매우 상세하게 설명하면 다음과 같습니다.
| 파라미터 | 설명 | 예시 값 |
|---|---|---|
| accumulator | 지금까지 누적된 값입니다. 이전 콜백의 return 값이 그대로 전달됩니다. | 총합 계산 시: 현재까지의 합계(예: 5 → 12 → 20) |
| currentValue | 배열에서 현재 처리 중인 요소입니다. | [1,2,3] 처리 시: 1 → 2 → 3 |
| currentIndex | 현재 요소의 인덱스입니다. 초기값을 생략하면 첫 번째 존재하는 요소는 누적 시작값이 되어 콜백이 다음 요소부터 실행됩니다. | 첫 번째 요소: 0 / 두 번째: 1 / 세 번째: 2 |
| array | reduce를 호출한 원본 배열 전체입니다. | [1, 2, 3] |
| initialValue (초기값) | accumulator의 시작값으로, 반드시 명시하는 것이 권장됩니다. | 합계를 0부터 시작 → 초기값: 0 |
정리: forEach는 단순 반복, map은 변환, filter는 조건 추출, reduce는 새로운 값 또는 구조를 만드는 데 사용됩니다. reduce는 이전 결과를 다음 계산에 넘기는 누적 규칙을 표현합니다. 다른 메서드나 반복문으로도 구현할 수 있으며, 누적 관계가 읽기 쉬울 때 선택합니다.
reduce는 어렵게 생겼지만 사실 함수형 프로그래밍에서 매우 단순한 규칙만 따라갑니다.
reduce를 쉽게 이해하는 예시 1: 쇼핑할 때 장바구니 합계
const cart = [1000, 3000, 2500];
const total = cart.reduce((acc, price) => acc + price, 0);
console.log(total); // 6500
초심자 기준으로 reduce를 이해하려면 “acc는 지금까지의 누적 결과”라고 기억하면 됩니다.
왜 초기값이 중요한가? (많이 발생하는 실수)
초기값이 없으면 배열의 첫 번째 요소가 자동으로 누적값이 되며, 빈 배열에서 초기값 없이 reduce를 호출하면 TypeError가 발생합니다. 특히 객체 배열을 누적할 때 초기 구조를 지정하지 않으면 타입 구조가 불안정해집니다.
// 올바른 방식: 초기값을 명시
const items = [{price: 1000}, {price: 2000}];
const total = items.reduce((acc, item) => acc + item.price, 0);
console.log(total); // 3000
자주 발생하는 실수: 초기값 생략 → 예외 발생 확률 증가

실무와 초심자를 위한 대표 활용 패턴
reduce는 map + filter의 동작을 모두 구현할 수 있기 때문에 실무에서는 데이터 변환, 집계, 그룹화 등에 널리 활용됩니다. 다음은 초심자들이 활용하기 좋은 사례입니다.
| 패턴 | 설명 | 예시 |
|---|---|---|
| 합/평균 | 숫자 배열 집계 | [10, 20, 30] |
| 객체 속성 합산 | 특정 속성 합계 | [{price:10},{price:20}] |
| 배열 평탄화 | 중첩 배열을 단일 배열로 | [[1,2],[3,4]] → [1,2,3,4] |
| 데이터 그룹화 | 특정 조건으로 묶기 | 도시별 사용자 그룹화 |
실제 예시: 그룹화 (초심자가 reduce를 어려워하는 대표 사례)
const users = [
{name: "A", city: "Seoul"},
{name: "B", city: "Busan"},
{name: "C", city: "Seoul"}
];
const grouped = users.reduce((acc, user) => {
if (!acc[user.city]) acc[user.city] = [];
acc[user.city].push(user);
return acc;
}, Object.create(null));
console.log(grouped);
// {
// Seoul: [{...}, {...}],
// Busan: [{...}]
// }
이 패턴은 실무에서 매우 자주 쓰이며, 도시별로 데이터를 묶어야 할 때 활용할 수 있는 형태입니다.
reduce가 users 배열을 순회하면서 도시 이름을 기준으로 객체를 구성해 나가는 과정입니다. 각 사용자는 해당 도시 이름을 key로 하는 배열에 차례대로 추가됩니다.
1) 최초 상태
초기값이 Object.create(null)이므로 상속된 속성 없이 시작합니다. 도시 이름이 constructor나 __proto__여도 기존 속성과 충돌하지 않습니다.
2) 첫 번째 사용자 A (Seoul)
– acc["Seoul"]이 없으므로 빈 배열 생성
– A를 push하여 Seoul: [{name:"A", city:"Seoul"}] 형태가 됩니다.
3) 두 번째 사용자 B (Busan)
– Busan 키가 없으므로 새로운 배열 생성
– B를 push하여 Busan: [{name:"B", city:"Busan"}]이 됩니다.
4) 세 번째 사용자 C (Seoul)
– Seoul 배열이 이미 있으므로 그대로 push하여 Seoul: [{A}, {C}] 형태가 됩니다.
최종 결과
모든 순회가 끝나면 도시별로 사용자가 묶인 객체가 완성됩니다.
{
Seoul: [ {name: "A", city: "Seoul"}, {name: "C", city: "Seoul"} ],
Busan: [ {name: "B", city: "Busan"} ]
}
이처럼 reduce는 단순한 누적뿐 아니라 배열을 “새로운 구조”로 재조립할 때 사용할 수 있습니다.
고급 활용: 조건 분기 & 비동기 흐름 제어
reduce는 단순 계산을 넘어, 복잡한 조건문을 활용한 데이터 흐름 제어나 비동기 작업 순차 실행에도 활용됩니다.
조건부 누적 처리
const nums = [1,2,3,4,5];
const evens = nums.reduce((acc, num) => {
if (num % 2 === 0) acc.push(num);
return acc;
}, []);
console.log(evens); // [2,4]
이 예시는 filter와 비슷해 보이지만, reduce를 사용하면 조건을 만족하는 값만 누적 배열에 직접 담을 수 있습니다. 조건이 더 복잡하거나 여러 누적값을 동시에 다뤄야 할 때 filter보다 유연하게 확장할 수 있다는 점이 큰 장점입니다.
선택 심화: Promise 체인으로 순차 실행
const task1 = () => Promise.resolve("첫 번째 결과");
const task2 = () => Promise.resolve("두 번째 결과");
const task3 = () => Promise.resolve("세 번째 결과");
const tasks = [task1, task2, task3];
tasks.reduce((prevPromise, task) => {
return prevPromise.then(result => task().then(r => [...result, r]));
}, Promise.resolve([]))
.then(console.log)
.catch(console.error);
이 예제는 Promise를 배운 뒤 읽는 선택 심화입니다. reduce 자체는 동기적으로 체인을 구성하고, 각 task는 이전 Promise가 이행된 뒤 then 안에서 호출됩니다. 이미 실행한 Promise 배열을 모은다고 시작 순서가 제어되는 것은 아닙니다. 중간 작업이 거부되면 뒤의 성공 콜백은 실행되지 않으며 마지막 catch로 전파됩니다. 순차 처리 의도가 더 잘 보이면 for…of와 await을 사용하세요.
map / filter / forEach / reduce 비교 테이블
아래 비교 표는 초심자가 네 가지 배열 메서드를 정확히 구분할 수 있도록 구성한 정리표입니다. 어떤 상황에서 어떤 메서드를 써야 하는지 직관적으로 이해할 수 있습니다.
reduce를 활용할 때는 먼저 기본 배열 메서드의 동작 흐름을 잡아두는 것이 중요합니다. JavaScript 배열 메서드 정리를 함께 복습하면 메서드 선택 기준이 더 빨라집니다.
| 메서드 | 주요 목적 | 반환값 | 원본 배열 변경 여부 | 대표 사용 예 |
|---|---|---|---|---|
| forEach | 단순 반복 수행 | undefined | 변경하지 않음 (직접 변경 시 변함) | 로그 출력, DOM 조작 등 부수 효과 작업 |
| map | 요소 변환 | 새로운 배열 | 메서드 자체는 변경하지 않음. 콜백의 변경은 가능 | 값 변환, 새로운 배열 생성 |
| filter | 조건에 맞는 요소만 추출 | 새로운 배열 | 메서드 자체는 변경하지 않음. 콜백의 변경은 가능 | 검색 또는 특정 조건 데이터만 모으기 |
| reduce | 하나의 값 또는 구조로 축약 | 숫자, 배열, 객체 등 무엇이든 가능 | 메서드 자체는 변경하지 않음. 콜백의 변경은 가능 | 합계, 그룹화, 중첩 구조 변환 등 고급 로직 |
정리
reduce는 초심자에게 어렵게 느껴지지만, 누적(accumulate) 개념과 초기값의 중요성을 이해하면 map·filter와 목적이 다른 누적 계산과 구조 변환 로직을 구성할 수 있습니다. 실무에서 데이터 그룹화, 변환, 비동기 흐름 제어 등 다양한 문제를 해결하는 데 reduce는 매우 중요한 역할을 합니다.
공식 문서와 다음 학습 경로
콜백 인수, 초기값 생략 시 첫 순회 규칙, 빈 배열 오류는 MDN의 Array.prototype.reduce() 문서를 기준으로 확인하세요. 배열 메서드 선택 기준은 앞서 연결한 JavaScript 배열 메서드 정리를 복습하고, 함수가 값과 스코프를 평가하는 흐름까지 이어서 보려면 JavaScript 실행 컨텍스트·스코프·클로저 가이드를 다음 단계로 읽으면 좋습니다.
FAQ
Q. reduce가 너무 복잡해 보이는데 쉽게 이해하는 방법이 있나요?
accumulator를 “지금까지의 결과”, currentValue를 “지금 처리 중인 값”이라고 생각하면 매우 간단합니다. 쇼핑할 때 장바구니 합계를 떠올리면 바로 이해됩니다.
Q. map과 filter 대신 reduce만 써도 되나요?
기술적으로는 가능하지만 가독성을 위해 map/filter를 우선 사용하는 것이 좋습니다. 여러 변환을 한 번에 처리해야 할 때만 reduce를 선택하는 것이 실무적입니다.
Q. 빈 배열에서 reduce를 호출하면 오류가 나는 이유는?
초기값이 없으면 reduce는 첫 요소를 누적값으로 사용합니다. 그러나 배열이 비어 있으면 첫 요소가 없어 오류가 발생합니다. 초기값을 생략할 이유가 분명하지 않다면 명시하는 편이 빈 배열과 누적 타입을 다루기 안전합니다.
면접 질문으로 자주 등장하는 reduce 관련 Q&A
Q. reduce와 다른 배열 메서드(map, filter)의 가장 큰 차이점은 무엇인가요?
map과 filter는 “항상 새로운 배열을 반환”하지만, reduce는 숫자·문자열·배열·객체 등 어떤 형태든 만들어낼 수 있습니다. 즉, reduce는 구조 변환 및 누적 로직에 최적화된 메서드입니다.
Q. reduce 콜백에서 accumulator를 항상 불변하게 유지해야 하나요?
새로 만든 로컬 배열이나 객체를 누적값으로 쓰는 경우에는 push나 속성 대입으로 채워도 됩니다. 위 그룹화와 조건부 누적 예제가 이 방식입니다. 기존 React 상태나 다른 코드가 공유하는 객체를 초기값으로 전달했다면 직접 변경하지 말고 새 결과를 만들어야 합니다. 매 순회마다 누적 객체 전체를 복사하면 비용이 커질 수 있으므로 소유 범위를 먼저 구분하세요.
Q. reduce에서 초기값(initialValue)을 설정하는 것이 중요한 이유는 무엇인가요?
초기값이 존재하면 비어 있는 배열에서도 오류가 발생하지 않으며, 누적값의 타입을 명확하게 유지할 수 있습니다. 이는 타입 안정성과 예측 가능한 로직 구현에 매우 중요합니다.
Q. reduce를 사용할 때 흔히 발생하는 실수는 무엇인가요?
누적값의 타입을 일관되게 유지하지 않아 예상치 못한 결과가 발생하는 경우가 많습니다. 또한 초기값을 생략할 때 빈 배열 처리에서 오류가 발생하기 쉬운 점도 대표적인 실수입니다.
Q. reduce를 실무에서 가장 많이 사용하는 예는 무엇인가요?
데이터 그룹화, 합계 계산, 통계 처리, 중첩 배열 평탄화, API 응답 재구조화 등입니다. 특히 백엔드에서 넘어온 데이터를 프론트엔드에서 화면에 적합한 형태로 변환할 때 자주 사용됩니다.
Q. async/await과 reduce를 함께 사용할 때 주의해야 하는 점은?
reduce는 순차 실행 구조를 만들 수 있지만, 콜백을 async로 만들면 누적된 Promise 객체가 의도대로 동작하지 않을 수 있습니다. 이 경우 명시적으로 Promise 체이닝을 구성하거나 for...of 안에서 await하는 방식을 고려해야 합니다.
직접 실습: 취소 주문을 제외한 집계
단가·수량·분류·취소 여부를 가진 주문 배열을 받아 total(금액), count(수량), byCategory(분류별 금액)를 만드세요. 입력의 price는 숫자, quantity는 0 이상의 정수, category는 문자열, cancelled는 boolean으로 가정합니다. 단순 짝수 추출에는 filter가 더 명확하지만, 여기서는 세 누적값을 같은 규칙으로 갱신합니다.
export function summarizeOrders(orders) {
return orders.reduce((summary, order) => {
if (order.cancelled) return summary;
const amount = order.price * order.quantity;
summary.total += amount;
summary.count += order.quantity;
summary.byCategory[order.category] = (summary.byCategory[order.category] ?? 0) + amount;
return summary;
}, { total: 0, count: 0, byCategory: Object.create(null) });
}
ZIP 시작본에서 취소 판정과 total·count 갱신을 완성하세요. node starter/verify.mjs로 구현을 확인하고, node complete/app.mjs 및 node complete/verify.mjs로 정답을 비교합니다. 기본 입력에서 1000원 2개와 5000원 1개는 총 7000원·3개이고 취소된 3000원 주문은 제외됩니다.
?? 0은 왼쪽 값이 null 또는 undefined일 때 0을 씁니다. 아직 없는 분류의 첫 금액을 0부터 더하기 위한 표현입니다. 누적값이 이미 0이면 그 0을 유지합니다.
초기 summary는 {total: 0, count: 0, byCategory: 빈 객체}입니다. 첫 주문 뒤에는 2000원·2개, 두 번째 뒤에는 7000원·3개, 취소 주문 뒤에는 그대로 7000원·3개입니다. 모든 분기에서 summary를 반환해야 다음 콜백이 같은 누적 구조를 받습니다. return을 빼면 다음 acc가 undefined가 됩니다.
일반적인 촘촘한 배열 [1000, 3000, 2500]에서 초기값 0을 주면 콜백이 세 번 실행되어 1000 → 4000 → 6500이 됩니다. 초기값을 생략하면 1000을 시작값으로 삼아 두 번만 실행됩니다. [1000]에 초기값이 없으면 콜백 호출 없이 1000을 반환하고, []에 초기값 0을 주면 호출 없이 0을 반환합니다. 빈 슬롯은 건너뛰므로 “length만큼 항상 호출”한다고 외우지 마세요.
누적용 객체는 이 함수 안에서 새로 만들어지므로 속성 대입으로 채워도 입력 주문을 바꾸지 않습니다. 반면 그룹에 원래 주문 객체를 넣으면 그 객체는 공유됩니다. 매 회차 전체 누적 배열을 spread로 복사하면 누적 크기에 따라 총 복사량이 크게 증가할 수 있으므로, 소유 범위와 읽기 쉬운 반복문도 함께 고려하세요.
실습 파일
파일 경로를 확인하고 같은 프로젝트 안에 저장하세요. 이미지 등 소스 목록에 없는 파일과 실행 안내는 실습 ZIP에 포함되어 있습니다.
누적값의 초깃값과 반환값을 정해 배열에서 합계를 계산합니다. 수정 전은 시작 상태, 수정 후는 비교할 완성 상태입니다.
수정 전
app.mjs
import { summarizeOrders } from './summary.mjs';
const orders = [
{ category: '문구', price: 1000, quantity: 2, cancelled: false },
{ category: '도서', price: 5000, quantity: 1, cancelled: false },
{ category: '문구', price: 3000, quantity: 1, cancelled: true },
];
console.log(JSON.stringify(summarizeOrders(orders), null, 2));
summary.mjs
export function summarizeOrders(orders) {
return orders.reduce(
(summary, order) => {
if (false) return summary;
const amount = order.price * order.quantity;
summary.total += 0;
summary.count += 0;
summary.byCategory[order.category] =
(summary.byCategory[order.category] ?? 0) + amount;
return summary;
},
{ total: 0, count: 0, byCategory: Object.create(null) },
);
}
export function runSequential(tasks) {
return tasks.reduce(
(pending, task) =>
pending.then(async (results) => {
const result = await task();
results.push(result);
return results;
}),
Promise.resolve([]),
);
}
verify.mjs
import assert from 'node:assert/strict';
import { summarizeOrders, runSequential } from './summary.mjs';
const orders = Object.freeze([
Object.freeze({ category: '문구', price: 1000, quantity: 2, cancelled: false }),
Object.freeze({ category: '도서', price: 5000, quantity: 1, cancelled: false }),
Object.freeze({ category: '문구', price: 3000, quantity: 1, cancelled: true }),
]);
const result = summarizeOrders(orders);
assert.equal(result.total, 7000);
assert.equal(result.count, 3);
assert.deepEqual({ ...result.byCategory }, { 문구: 2000, 도서: 5000 });
assert.deepEqual({ ...summarizeOrders([]).byCategory }, {});
assert.equal(summarizeOrders([]).total, 0);
assert.equal(
summarizeOrders([{ category: 'a', price: 10, quantity: 1, cancelled: true }]).count,
0,
);
const unusual = summarizeOrders([
{ category: '__proto__', price: 2, quantity: 2, cancelled: false },
{ category: 'constructor', price: 3, quantity: 1, cancelled: false },
{ category: '__proto__', price: 1, quantity: 1, cancelled: false },
]);
assert.equal(unusual.byCategory.__proto__, 5);
assert.equal(unusual.byCategory.constructor, 3);
assert.equal(Object.getPrototypeOf(unusual.byCategory), null);
assert.throws(() => [].reduce((a, b) => a + b), TypeError);
let calls = 0;
assert.equal(
[9].reduce(() => {
calls += 1;
}),
9,
);
assert.equal(calls, 0);
const trace = [];
const tasks = [1, 2, 3].map((id) => async () => {
trace.push(`start${id}`);
await Promise.resolve();
trace.push(`end${id}`);
return id;
});
assert.deepEqual(await runSequential(tasks), [1, 2, 3]);
assert.deepEqual(trace, ['start1', 'end1', 'start2', 'end2', 'start3', 'end3']);
assert.deepEqual(await runSequential([]), []);
let reached = false;
await assert.rejects(
runSequential([
() => Promise.reject(new Error('실패')),
() => {
reached = true;
return 2;
},
]),
/실패/,
);
assert.equal(reached, false);
console.log('합계·취소·빈 입력·키 충돌·초기값·비동기 순서와 실패 전파 검증 통과');
수정 방법
app.mjs
이 파일은 변경하지 않습니다. 다른 파일을 수정하는 동안 기존 내용을 유지하세요.
summary.mjs
summary.mjs에서 아래 차이가 있는 위치를 수정하세요. -는 삭제할 줄, +는 추가할 줄이며 표시는 실제 코드에 넣지 않습니다.
누적값의 초깃값과 반환값을 정해 배열에서 합계를 계산합니다.
--- 수정 전/summary.mjs
+++ 수정 후/summary.mjs
@@ -1,10 +1,10 @@
export function summarizeOrders(orders) {
return orders.reduce(
(summary, order) => {
- if (false) return summary;
+ if (order.cancelled) return summary;
const amount = order.price * order.quantity;
- summary.total += 0;
- summary.count += 0;
+ summary.total += amount;
+ summary.count += order.quantity;
summary.byCategory[order.category] =
(summary.byCategory[order.category] ?? 0) + amount;
return summary;
수정 후 탭의 전체 파일과 비교한 뒤 본문의 확인 절차를 실행하세요.
verify.mjs
이 파일은 변경하지 않습니다. 다른 파일을 수정하는 동안 기존 내용을 유지하세요.
수정 후
app.mjs
import { summarizeOrders } from './summary.mjs';
const orders = [
{ category: '문구', price: 1000, quantity: 2, cancelled: false },
{ category: '도서', price: 5000, quantity: 1, cancelled: false },
{ category: '문구', price: 3000, quantity: 1, cancelled: true },
];
console.log(JSON.stringify(summarizeOrders(orders), null, 2));
summary.mjs
export function summarizeOrders(orders) {
return orders.reduce(
(summary, order) => {
if (order.cancelled) return summary;
const amount = order.price * order.quantity;
summary.total += amount;
summary.count += order.quantity;
summary.byCategory[order.category] =
(summary.byCategory[order.category] ?? 0) + amount;
return summary;
},
{ total: 0, count: 0, byCategory: Object.create(null) },
);
}
export function runSequential(tasks) {
return tasks.reduce(
(pending, task) =>
pending.then(async (results) => {
const result = await task();
results.push(result);
return results;
}),
Promise.resolve([]),
);
}
verify.mjs
import assert from 'node:assert/strict';
import { summarizeOrders, runSequential } from './summary.mjs';
const orders = Object.freeze([
Object.freeze({ category: '문구', price: 1000, quantity: 2, cancelled: false }),
Object.freeze({ category: '도서', price: 5000, quantity: 1, cancelled: false }),
Object.freeze({ category: '문구', price: 3000, quantity: 1, cancelled: true }),
]);
const result = summarizeOrders(orders);
assert.equal(result.total, 7000);
assert.equal(result.count, 3);
assert.deepEqual({ ...result.byCategory }, { 문구: 2000, 도서: 5000 });
assert.deepEqual({ ...summarizeOrders([]).byCategory }, {});
assert.equal(summarizeOrders([]).total, 0);
assert.equal(
summarizeOrders([{ category: 'a', price: 10, quantity: 1, cancelled: true }]).count,
0,
);
const unusual = summarizeOrders([
{ category: '__proto__', price: 2, quantity: 2, cancelled: false },
{ category: 'constructor', price: 3, quantity: 1, cancelled: false },
{ category: '__proto__', price: 1, quantity: 1, cancelled: false },
]);
assert.equal(unusual.byCategory.__proto__, 5);
assert.equal(unusual.byCategory.constructor, 3);
assert.equal(Object.getPrototypeOf(unusual.byCategory), null);
assert.throws(() => [].reduce((a, b) => a + b), TypeError);
let calls = 0;
assert.equal(
[9].reduce(() => {
calls += 1;
}),
9,
);
assert.equal(calls, 0);
const trace = [];
const tasks = [1, 2, 3].map((id) => async () => {
trace.push(`start${id}`);
await Promise.resolve();
trace.push(`end${id}`);
return id;
});
assert.deepEqual(await runSequential(tasks), [1, 2, 3]);
assert.deepEqual(trace, ['start1', 'end1', 'start2', 'end2', 'start3', 'end3']);
assert.deepEqual(await runSequential([]), []);
let reached = false;
await assert.rejects(
runSequential([
() => Promise.reject(new Error('실패')),
() => {
reached = true;
return 2;
},
]),
/실패/,
);
assert.equal(reached, false);
console.log('합계·취소·빈 입력·키 충돌·초기값·비동기 순서와 실패 전파 검증 통과');
이 글이 도움이 되었나요?
JavaScript 학습 순서
필수 13개 · 전체 14개
읽음 기록 관리
전체 과정 목차 (14개)
- 필수 학습 · JavaScript 조건문과 반복문: 변수 값의 흐름부터 추적하기
- 필수 학습 · JavaScript 함수와 객체, import export로 모듈 나누기
- 필수 학습 · JavaScript 배열 메서드: map filter forEach reduce 차이
- 필수 학습 · JavaScript 객체 참조와 불변 갱신: 중첩 객체를 안전하게 바꾸기
- 필수 학습 · JavaScript reduce 사용법: 배열 누적 계산을 이해하는 기준 현재 글
- 필수 학습 · JavaScript DOM 폼 만들기: 입력 검증과 접근성 처리
- 필수 학습 · JavaScript 투두리스트 만들기: 상태와 이벤트 위임으로 완성하기
- 필수 학습 · JavaScript URLSearchParams 사용법: URL 파라미터 읽고 수정하기
- 필수 학습 · JavaScript Date UTC KST 차이: 시간대 변환 기준 잡기
- 필수 학습 · JavaScript 실행 컨텍스트 기준: 스코프 호이스팅 클로저 연결하기
- 필수 학습 · JavaScript fetch 오류 처리: Promise부터 404까지
- 필수 학습 · JavaScript 이벤트 루프: Promise와 await 실행 순서 추적하기
- 필수 학습 · JavaScript 고급 비동기: AbortController와 최신 요청 경쟁 제어
- 선택 참고 · GSAP이 처음일 때 기초 사용법: 설치부터 기본 애니메이션까지
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.
BlogFlow AI
이 글과 함께 공부하기
무엇을 도와드릴까요?
읽다가 막힌 부분을 물어보세요. 이 글을 바탕으로 설명하고, 이어서 읽을 본문을 연결합니다.