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
가 넘겨지게 됩니다.
배열 리터럴의 전개
배열 리터럴은 []
로 생성한 배열을 의미합니다.
배열에 추가로 요소를 넣는다던지, 배열 복사, 배열 이어 붙이기 등 다양한 작업에서 ...
연산자를 사용할 수 있습니다.
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' }
위에서 clonedObj1
은 obj1
의 프로퍼티만 복사받은 객체입니다.
따라서 두 객체의 참조값 자체는 다릅니다.
1
console.log(obj1 === clonedObj1); // false
이러한 ...
연산자의 성질은 상태
의 불변성(immutable) 유지에도 응용됩니다.
(불변성은 그 내용이 중요해서 별도의 포스팅으로 다루겠지만, 간단히 말해서 기존에 메모리에 담겨 있던 값이 다시 변경되지 않는
것을 말하며, 이 불변성을 유지함으로써 상태 변화
를 빠르게 탐지할 수 있습니다.)
예를 들어 obj1
의 name
프로퍼티만 변경하고 싶은 경우, 아래와 같이 사용하면 됩니다.
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
값만 변경(덮어씌우는)하는 효과를 가집니다.
그리고 obj1
과 newObj
는 서로 다른 객체가 됩니다.
만약 이것을 name
상태값이 변했다는 상황으로 대입해 본다면, 이처럼 이전 상태를 가진 객체(obj1
)와 다음 상태를 가진 객체(newObj
)가 서로 다르다는(false
) 성질을 활용해 상태가 변화했음을 감지할 수 있게 됩니다.
또한 기존의 obj1
은 변경하지 않았으므로 불변성을 유지한 것입니다.
다음 포스팅은 형태(...
)는 동일하지만 사용법이 다른 Rest 파라미터
에 대해 살펴보겠습니다.