Posts JavaScript의 spread operator(전개 구문) 사용하기
Post
Cancel

JavaScript의 spread operator(전개 구문) 사용하기

JavaScript의 spread operator(전개 구문) 사용하기


ES2015(ES6) 에서 새로 생긴 spread operator(전개 구문, 전개 연산자)에 대해 알아보려고 합니다.

참고 링크 : https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax


spread operator는 반복 가능한(iterable) 객체에 적용할 수 있는 문법입니다. 배열이나 문자열 등을 아래처럼 풀어서 요소 하나 하나로 전개시킬 수 있습니다.

iterable 객체 : https://ko.javascript.info/iterable

1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const str = "string";

console.log(...arr); // 1 2 3 4 5
console.log(...str); // "s" "t" "r" "i" "n" "g"

...의 사용 케이스 (Spread 로써의 역할)

...만 붙이면 자유롭게 그 객체를 펼칠 수 있어서 여러 곳에서 유용하게 사용할 수 있습니다.


함수 호출 시 인자로 넘길 때

1
2
3
4
5
6
7
8
9
function mul(x, y, z){
  return x * y * z;
}
const arr = [1, 3, 5];

const result1 = mul.apply(null, arr); // (1) apply() 방법
const result2 = mul(...arr); // (2) spread operator 방법

console.log(result2); // 15

배열 각각의 엘리먼트를 인자로 넘기고 싶을 때, ...가 없었을 경우에는 위의 (1) apply()를 사용해야 했습니다.

하지만 ...을 사용하면 간편하게 [1, 3, 5]가 전개되어 함수 인자의 x, y, z에 각각 1, 3, 5가 넘겨지게 됩니다.


배열 리터럴의 전개

배열 리터럴은 [] 로 생성한 배열을 의미합니다.

리터럴이란? : https://chanhuiseok.github.io/posts/js-1/

배열에 추가로 요소를 넣는다던지, 배열 복사, 배열 이어 붙이기 등 다양한 작업에서 ... 연산자를 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

// (1) 배열 복사
const arr3 = [...arr1]; // arr3은 [0, 1, 2]

// (2) 배열에 추가 요소로 넣기
const arr4 = [1, 2, ...arr2, 9, 10]; // arr4는 [1, 2, 3, 4, 5, 9, 10]

// (3) 두 배열 이어 붙이기
const arr5 = [...arr1, ...arr2]; // [0, 1, 2, 3, 4, 5];

Array.prototype 객체의 메서드들(apply, unshift..)을 이용해야 했던 부분들을 간편하게 ... 연산자로 대체할 수 있게 되었습니다.


Object Spread Properties - ES2018에서 새롭게 추가

어떤 한 객체의 프로퍼티를 다른 새로운 객체에 복사할 때에도 ...가 유용하게 사용됩니다.

사실 Object 자체는 iterable 객체가 아니며, 아래와 같이 사용하면 에러가 발생합니다.

1
2
const obj = { name: 'John', age: '20' };
console.log(...obj); // TypeError: obj is not iterable

위와 같은 방식이 아니라, ES2018(ES9) 에서는 객체의 프로퍼티를 전개할 수 있도록 지원하고 있으며 아래와 같이 객체 내부에서 사용할 수 있습니다.

1
2
3
4
5
6
7
8
9
const obj1 = { name: 'John', age: '20' };
const obj2 = { name: 'Tom', age: '23' };

const clonedObj1 = { ...obj1 };
// Object { name: 'John', age: '20 }

const mergedObj = { ...obj1, ...obj2 };
// Object { name: 'John', age: '20', name: 'Tom', age: '23' }

위에서 clonedObj1obj1의 프로퍼티만 복사받은 객체입니다.

따라서 두 객체의 참조값 자체는 다릅니다.

1
console.log(obj1 === clonedObj1); // false

이러한 ... 연산자의 성질은 상태의 불변성(immutable) 유지에도 응용됩니다.

(불변성은 그 내용이 중요해서 별도의 포스팅으로 다루겠지만, 간단히 말해서 기존에 메모리에 담겨 있던 값이 다시 변경되지 않는 것을 말하며, 이 불변성을 유지함으로써 상태 변화를 빠르게 탐지할 수 있습니다.)

예를 들어 obj1name 프로퍼티만 변경하고 싶은 경우, 아래와 같이 사용하면 됩니다.

1
2
3
4
5
const obj1 = { name: 'John', age: '20', flag: 'true', foo: 'bar' };
const newObj = { ...obj1, name: 'Alice' };
// Object { name: 'Alice', age: '20', flag: 'true', foo: 'bar' }

console.log(obj1 === newObj); // false

newObj의 선언부를 확인해 보면, 객체 내부에서 obj1의 프로퍼티를 전달받고 그 뒤에 name: 'Alice'를 적어서 name의 값만 바꾸고 있습니다.

이렇게 하면 복사받은 값에서 이처럼 뒤에 적은 name 값만 변경(덮어씌우는)하는 효과를 가집니다.

그리고 obj1newObj는 서로 다른 객체가 됩니다.

만약 이것을 name 상태값이 변했다는 상황으로 대입해 본다면, 이처럼 이전 상태를 가진 객체(obj1)와 다음 상태를 가진 객체(newObj)가 서로 다르다는(false) 성질을 활용해 상태가 변화했음을 감지할 수 있게 됩니다.

또한 기존의 obj1은 변경하지 않았으므로 불변성을 유지한 것입니다.


다음 포스팅은 형태(...)는 동일하지만 사용법이 다른 Rest 파라미터에 대해 살펴보겠습니다.

[React] 리액트 classnames 활용하기 (classnames, !! 연산자)

Google I/O 2021 웹 세션 (1) - 쿠키 SameSite 속성, 로드 후 웹의 성능