reduce로 그룹핑하던 코드와 new Promise 밖으로 resolve를 꺼내던 패턴 정리
문제 발생
목록을 카테고리별로 묶는 코드는 늘 reduce로 시작했습니다. 짧지도 않고, 초기값과 누산기 갱신에서 한 번씩 실수가 납니다.
const byType = items.reduce((acc, item) => {
(acc[item.type] ||= []).push(item);
return acc;
}, {});그리고 콜백 기반 API를 프로미스로 감쌀 때는 resolve를 바깥으로 꺼내려고 이런 모양을 썼습니다.
let resolve, reject;
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
socket.on("message", (m) => resolve(m));
socket.on("error", (e) => reject(e));let 두 개가 초기화되기 전에 참조될 수 있는 것처럼 보이고, TypeScript에서는 타입 단언을 붙여야 통과했습니다.
원인 분석
두 패턴 모두 "표준에 없어서 매번 손으로 쓰는 관용구"였습니다. 사람마다 조금씩 다르게 쓰고, 다르게 쓰는 만큼 리뷰에서 읽는 비용이 듭니다. 최근 몇 해 사이 둘 다 표준 API가 생겼습니다.
Object.groupBy(items, callback)— 콜백이 돌려준 문자열을 키로 삼아 묶습니다. 2024년 3월부터 Baseline입니다.Promise.withResolvers()—promise,resolve,reject를 한 번에 돌려줍니다. 위의let두 개짜리 관용구를 그대로 대체합니다.
해결 방안
- 그룹핑은
Object.groupBy로 씁니다.
const byType = Object.groupBy(items, (item) => item.type);
// { vegetables: [...], fruit: [...] }- 반환값이
null프로토타입 객체라는 점을 압니다. 이건 실수가 아니라 의도입니다 — 데이터에서 온 키가toString이나constructor같은 이름이어도 프로토타입 오염이 일어나지 않습니다. 다만result.hasOwnProperty(...)처럼 프로토타입 메서드를 기대하는 코드는 실패하므로Object.hasOwn(result, key)를 씁니다. - 키가 문자열이 아니면
Map.groupBy를 씁니다.Object.groupBy의 키는 문자열이나 심볼이어야 하고 나머지는 문자열로 강제 변환됩니다. 날짜 객체나 다른 객체로 묶어야 한다면Map.groupBy가 임의의 값을 키로 받습니다.
const byDate = Map.groupBy(logs, (log) => startOfDay(log.at)); // Date를 키로- 콜백 API를 감쌀 때는
Promise.withResolvers()를 씁니다.
const { promise, resolve, reject } = Promise.withResolvers();
socket.on("message", resolve);
socket.on("error", reject);
await promise;- 지원 범위를 확인하고 폴리필을 정합니다. 둘 다 비교적 최근 표준이라, 구형 브라우저나 오래된 Node를 지원해야 한다면
core-js같은 폴리필이 필요합니다. 지원 대상이 최신 브라우저뿐이라면 그대로 씁니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.