Array.prototype.flat() 폴리필 구현하기

문제 상황

레거시 프로젝트에서 중첩 배열을 평탄화해야 하는 요구사항이 생겼다. ES2019 스펙에 Array.prototype.flat()이 제안되었지만, IE11을 지원해야 해서 폴리필이 필요했다.

재귀 방식 구현

가장 직관적인 방법은 재귀를 사용하는 것이다.

if (!Array.prototype.flat) {
  Array.prototype.flat = function(depth = 1) {
    const flatten = (arr, d) => {
      return arr.reduce((acc, val) => {
        if (Array.isArray(val) && d > 0) {
          return acc.concat(flatten(val, d - 1));
        }
        return acc.concat(val);
      }, []);
    };
    
    return flatten(this, depth);
  };
}

스택 방식 구현

깊이가 깊은 배열에서는 스택 오버플로우 위험이 있어 반복문 방식도 구현했다.

function flattenWithStack(arr, depth = 1) {
  const stack = arr.map(item => [item, depth]);
  const result = [];
  
  while (stack.length > 0) {
    const [item, d] = stack.pop();
    
    if (Array.isArray(item) && d > 0) {
      stack.push(...item.map(i => [i, d - 1]));
    } else {
      result.unshift(item);
    }
  }
  
  return result;
}

성능 비교

실제 데이터로 테스트한 결과 재귀 방식이 약간 빨랐지만, depth가 100 이상일 때는 스택 방식이 안전했다. 일반적인 케이스에서는 재귀 방식으로 충분했고, 가독성도 더 좋아서 재귀 방식을 채택했다.

적용 결과

Babel로 트랜스파일하는 과정에서 core-js 폴리필을 사용할 수도 있었지만, 번들 크기를 줄이기 위해 필요한 메서드만 직접 구현하는 방향을 선택했다. 10KB 정도 번들 사이즈를 줄일 수 있었다.

Array.prototype.flat() 폴리필 구현하기