IT/언어

[JavaScript] spread 구문

개발자 두더지 2023. 3. 8. 21:32
728x90

일본의 한 블로그 글을 번역한 포스트입니다. 오역 및 의역, 직역이 있을 수 있으며 틀린 내용은 지적해주시면 감사하겠습니다.

 

 

spread 구문


...foo 와 같은 형태로 기재되며, 배열이나 오브젝트의 요소를 문자 그대로 전개하는 구문이다. 단순한 정의로는 어떤 구문인지 한번에 이해하기 어려울 것 같으므로, 배열과 오브젝트 각각의 경우를 살펴보자.

 

배열

const foo = [1, 2];

// 배열의 복사
const bar = [...foo]; // => [1, 2]

// 요소를 추가하여 새로운 배열을 생성
const baz = [...foo, 3, 4]; // => [1, 2, 3, 4]

// 배열 합치기
const hoge = [...foo, ...bar]; // => [1, 2, 1, 2]

 Array.prototype.concat()과 상응하지만 더욱 간단하게 작성할 수 있다.

 

오브젝트

const foo = { a: 1, b: 2 };

// 오브젝트의 복사
const bar = { ...foo }; // => { a: 1, b: 2 }

// 속성을 추가한 새로운 오브젝트를 생성
const baz = { ...foo, c: 3 }; // => { a: 1, b: 2, c: 3 }

// 오브젝트 합치기
const hoge = { ...foo, ...{ c: 3, d: 4 } }; // => { a: 1, b: 2, c: 3, d: 4 }

// 원래의 오브젝트에 같은 이름의 속성이 있는 경우 치환
const fuga = { ...foo, b: 3 }; // => { a: 1, b: 3 }
const piyo = { ...foo, ...{ a: 3, b: 4 } }; // => { a: 3, b: 4 }

 Object.assign()으로 작성한 것과 같은 치환이 가능하다.

 

 

주의해야할 점


 배열이든 오브젝트든 spread 구문은 shallow copy이므로, 들여쓰기가 있는 경우 주의해야할 필요가 있다.

const foo = {
  a: {
    b: 1
  }
};

const bar = { ...foo };
foo.a.b = 2;
console.log(bar); // => { a: { b: 2 } }

 자식 오브젝트는 동일하게 참조하고 있다는 것을 알 수 있다. 들여쓰기되어 있는 오브젝트를 다룰 때에는 다음과 같이 개별로 분할할 필요가 있다.

const foo = {
  a: {
    b: 1,
    c: 2
  },
  d: 3
};

const bar = {
  ...foo,
  a: {
    ...foo.a,
    c: 4
  }
};
console.log(bar); // => { a: {b: 1, c: 4}, d: 3 }

 

 

분할 대입(Destructuring assignment)


 분할 대입은 배열이나 오브젝트의 요소를 일부 획득해 개별 변수에 대입할 수 있다.

const array = [1, 2, 3];
const [x, y, z] = array;
console.log(x); // => 1
console.log(y); // => 2
console.log(z); // => 3

const obj = { foo: 1, bar: 2, baz: 3 };
const { foo } = obj;
console.log(foo); // => 1

 이 구문과 spread를 합쳐서 요소를 빼내면서 변수에 대입할 수 있다.

const array = [1, 2, 3];
const [p, ...q] = array;
console.log(p); // => 1
console.log(q); // => [2, 3]

const obj = { foo: 1, bar: 2, baz: 3 };
const { foo, ...rest } = obj;
console.log(foo); // => 1
console.log(rest); // => { bar: 2, baz: 3 }

 한편 들여쓰기한 오브젝트라면 이런 느낌이다.

const nestedObj = {
  x: { a: 1, b: 2, c: 3 },
  y: [4, 5, 6]
};

const { x: { a, ...restX }, y: [y0, ...restY] } = nestedObj;
console.log(a); // => 1
console.log(restX); // => { b: 2, c: 3 }
console.log(y0); // => 4
console.log(restY); // => [5, 6]

 

 

(여담) Redux Reducer을 이용한 사용예


 spread 구문은 오브젝트를 Immutable하게 다루는 경우에도 활용할 수 있다. 그 하나의 예로 Redux의 State/Reducer가 있기 때문에 간단히 여기서 소개하고자 한다.

interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

interface TodoState {
  todos: { [id: number]: Todo };
}

const todoReducer = (state: TodoState = { todos: {} }, action: TodoAction): TodoState => {
  switch (action.type) {
    case TypeKeys.ADD_TODO:
      const nextId = Object.keys(state.todos).length + 1;
      return {
        todos: {
          ...state.todos,
          [nextId]: {
            id: nextId,
            text: action.payload.text,
            completed: false
          }
        }
      };
    case TypeKeys.COMPLETE_TODO:
      return {
        todos: {
          ...state.todos,
          [action.payload.id]: {
            ...state.todos[action.payload.id],
            completed: true
          }
        }
      };
    default:
      return state;
  }
};

참고자료

https://qiita.com/akisx/items/682a4283c13fe336c547

728x90