Array.fill 메소드를 사용할때의 주의점

JavaScript
Array.fill 메소드를 사용할때의 주의점

안녕하세요! 오늘은 제가 며칠전에 개발을 하면서 겪은 Array.fill 메소드를 사용할때 주의점에 대해서 알아보도록 하겠습니다.

1. Array.fill 메소드

Array.fill 메소드는 배열을 특정한 값으로 채우려고 할때 사용할 수 있는 자바스크립트의 배열 메소드입니다.

간단한 사용법은 아래처럼 작성할 수 있습니다.

const numbers = [0, 1, 2, 3, 4];

numbers.fill(0); // numbers 배열의 모든 값을 0으로 변경 (원본 배열이 수정되는 특징)

console.log(numbers); // [0, 0, 0, 0, 0]

const oneNumbers = Array(5).fill(1); // 5의 길이를 갖는 배열의 모든 값을 1로 채움

console.log(oneNumbers); // [1, 1, 1, 1, 1]

만약 특정한 길이를 갖는 배열을 임의의 값으로 채우려고 할때 다양한 방법이 있는데요. 그중에서 Array.fill 메소드를 위처럼 사용하여 구현할 수 있습니다.

2. 참조타입의 값을 채울때 ⚠️

그러나 Array.fill 메소드를 사용하여 배열의 값을 채울때, 참조타입에 해당하는 값을 넣을경우 나중에 예상치못한 문제가 발생할 수 있습니다.

참조타입이란? 자바스크립트 메모리상에서 값이 저장된 힙 영역의 메모리 주소를 저장하는 변수, 대표적으로 배열, 객체, 함수 등이 포함됩니다.

자바스크립트 메모리 구조

위는 자바스크립트의 메모리 구조를 나타낸 사진입니다. Stack 영역에서 원시타입은 해당 값을 저장하는 반면, 참조타입은 Heap 영역에서 값이 저장된곳의 메모리 주소를 저장합니다.

메모리 구조를 알아두고나서 아래의 내용을 읽으면 도움이 될것 같아서 작성했습니다.

const newArray = Array(5).fill({
  title: '',
  completed: false,
});

Array.fill 메소드의 인자를 참조타입 값으로 넣으면, 해당 값에 대해서 모든 요소를 얕은복사 형태로 채우게 됩니다.

얕은복사깊은복사란? 얕은복사: 복사할 객체의 같은 메모리 주소를 가리킵니다. 깊은복사: 복사할 객체의 값만 복사하여 새로운 메모리 주소로 저장합니다.

얕은복사와 깊은복사

모든 요소가 얕은복사 형태로 채워지기 때문에, 하나의 요소만 변경하려고해도 모든 요소가 변경되는 현상이 발생합니다.

const users = Array(2).fill({
  name: '',
  age: 0,
});

users[0].age = 20;
users[0].name = '권용빈';

console.log(users);

/*
  [
    {
      age: 20,
      name: '권용빈',
    },
    {
      age: 20,
      name: '권용빈',
    }
  ]
*/

참조타입을 Array.fill 메소드에 넘길때 발생하는 문제이므로, 위 예시에서 작성한 객체뿐만이 아닌 배열(2차원 배열 구성) 등을 넣었을때도 동일한 문제가 발생합니다.

const users = Array(2).fill([1, 2]);

users[0][0] = 3;

console.log(users);

/*
  예상 출력값
  [
    [3, 2],
    [1, 2]
  ]
*/

/*
  실제 출력값
  [
    [3, 2],
    [3, 2]
  ]
*/

3. 해결 방법 🔨

이렇듯 참조타입의 값을 넣은 Array.fill 메소드는 모든 요소를 변형시키는 문제가 있습니다. 그렇다면 반복되는 값으로 배열을 구성하려면 어떤 방법을 사용하는것이 좋을까요?

바로 자바스크립트의 Array 메소드 중 하나인 Array.from을 사용하여 문제를 해결할 수 있습니다.

/*
  첫번째 인자는 배열의 길이를 나타내는 객체
  두번째 인자는 배열 요소 값을 반환하는 함수로 구성합니다.
*/
const users = Array.from({ length: 2 }, (_, index) => ({
  age: index + 1,
  name: '',
}));

users[0].age = 20;
users[0].name = '권용빈';

console.log(users);

/*
  [
    {
      age: 20,
      name: '권용빈',
    },
    {
      age: 2,
      name: '',
    }
  ]
*/

인자로 전달한 값을 얕은복사로 채우는 Array.fill 메소드 대신 깊은복사 형태로 채우는 Array.from 메소드를 사용하면 모든 요소가 변경되지 않도록 방지할 수 있습니다.

4. 마치며 📌

오늘은 간단하게 Array.fill 메소드를 사용할때의 주의점 및 해결 방법에 대해서 글을 작성해봤습니다.

제가 얼마전에 기업과제를 하다가 이 문제를 겪었는데, 얕은복사와 관련한 문제임을 알기전까지 어떤 문제인지 디버깅했던 시간이 오래 걸렸던 기억이 있네요. 😭 여러분에게도 이 문제를 해결하는데 도움이 되었다면 좋겠습니다. 🙂


이상으로 글을 마치겠습니다. 읽어주셔서 감사합니다!

zod를 사용하여 선언적으로 데이터 검증하기
이전글

zod를 사용하여 선언적으로 데이터 검증하기

선언적 데이터 검증 라이브러리 zod를 사용해봅시다.

코어 웹 바이탈(Core Web Vitals) 알아보기
다음글

코어 웹 바이탈(Core Web Vitals) 알아보기

웹의 성능을 측정할때 핵심 지표가 되는 코어 웹 바이탈이란 무엇인지 알아봅시다.