본문으로 바로가기
반응형

ES6에서는  '...'와 같이 다소 특이한 형태의 문법이 추가되었습니다. 점 3개가 연달아 붙어있는 이 표시는 Spread Opertor(스프레드 오퍼레이터, 스프레드 연산자, 전개 구문, 펼침 연산자...)를 나타내는 것으로, 배열, 함수, 객체 등을 다루는 데 있어서 매우 편리하고 재미(?) 있는 새로운 기능을 제공합니다.

이번 글에서는 스프레드 연산자에 대해서 살펴보도록 하겠습니다.

 

 

Spread Operator 기본 문법

스프레드 연산자를 사용하면 배열, 문자열, 객체 등 반복 가능한 객체 (Iterable Object)를 개별 요소로 분리할 수 있습니다. 사실 말로 표현하면 의미적으로는 크게 와 닿지  않죠? 저도 말하면서 저게 맞는 말인지 고민이 됩니다.

 

우선 기본적인 표현법을 살펴보면서 감을 잡아보는 게 좋겠네요.

간단한 예를 들면 다음과 같은 표현을 들 수 있습니다.

// Array
var arr1 = [1, 2, 3, 4, 5]; 
var arr2 = [...arr1, 6, 7, 8, 9]; 

console.log(arr2); // [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

// String
var str1 = 'paper block'; 
var str2 = [...str1]; 
console.log(str2); // [ "p", "a", "p", "e", "r", " ", "b", "l", "o", "c", "k" ]

 

잘은 모르겠지만, 일단 대략 어떤 의미인지 감이 오는것 같네요!

 

ES6에 추가된 새로운 내용 중 스프레드 연산자는 연결, 복사 등의 용도로 꽤 활용도가 높은 편입니다. 이번 글에서는 스프레드 연산자가 자주 사용될 법한 예제 위주로 설명하도록 하겠습니다. Iterable Object에 대해서는 세부적으로 파고들면 복잡하므로 일단은 패스합니다. (제가 잘 이해를 못하겠거든요 ^^;)

 

그럼 실제적인 사용 예제에 대해서 살펴보겠습니다.

 

1. 배열에서의 Spread Operator

✅ 배열 병합 (Array Concatenation)

기존에 두 개의 배열을 결합하는 데 있어서 concat 메서드를 이용하곤 했습니다. ES6에서는 spread 연산자를 이용하여 좀 더 깔끔한 배열 병합이 가능합니다.

// 기존 방식
var arr1 = [1,2,3]; 
var arr2 = [4,5,6]; 

var arr = arr1.concat(arr2); 
console.log(arr); // [ 1, 2, 3, 4, 5, 6 ] 

// ES6 spread operator
var arr1 = [1,2,3]; 
var arr2 = [4,5,6]; 

var arr = [...arr1, ...arr2]; 
console.log(arr); // [ 1, 2, 3, 4, 5, 6 ] 

 

또한 Spread 연산자를 이용하면 다양한 형태의 배열 병합을 비교적 간단하게 수행할 수 있습니다.

var arr1 = [1,2,3]; 
var arr2 = [4,5,6]; 
arr1.push(...arr2) 

console.log(arr1); // [1,2,3,4,5,6]


var arr1 = [1,2];
var arr2 = [0, ...arr1, 3, 4];

console.log(arr2); // [0, 1, 2, 3, 4]

 

✅ 배열 복사 (Copying Arrays)

JavaScript에서 배열을 새로운 변수에 할당하는 경우 새로운 배열은 기존 배열을 참조합니다. 따라서 새로운 배열을 변경하는 경우 원본 배열 역시 변경됩니다.

// 단순 변수 할당
var arr1 = ['철수','영희']; 
var arr2 = arr1; 

arr2.push('바둑이'); 
console.log(arr2); // [ "철수", "영희", "바둑이" ]
// 원본 배열도 변경됩니다.
console.log(arr1); // [ "철수", "영희", "바둑이" ]

 

배열 참조가 아닌 배열 복사를 위해서 기존에는 slice 또는 ES5의 map을 이용하여 배열을 복사했습니다.

// Javascript array 복사
var arr1 = ['철수','영희']; 
var arr2 = arr1.slice();

arr2.push('바둑이'); 
console.log(arr2); // [ "철수", "영희", "바둑이" ]
// 원본 배열은 변경되지 않습니다.
console.log(arr1); // [ "철수", "영희" ]


// ES5 map 
var arr1 = ['철수','영희']; 
var arr2 = arr1.map((item) => item);

arr2.push('바둑이'); 
console.log(arr2); // [ "철수", "영희", "바둑이" ]
// 원본 배열은 변경되지 않습니다.
console.log(arr1); // [ "철수", "영희" ]

 

ES6의 Spread 연산자를 사용하면, 다음과 같이 새로운 복사된 배열을 생성할 수 있습니다.

// ES6 spread operator
var arr1 = ['철수','영희']; 
var arr2 = [...arr1]; 

arr2.push('바둑이'); 

console.log(arr2); // [ "철수", "영희", "바둑이" ]
// 원본 배열은 변경되지 않습니다.
console.log(arr1); // [ "철수", "영희" ]

 

참고로 Spread 연산자를 이용한 복사는 얕은(shallow) 복사를 수행하며, 배열 안에 객체가 있는 경우에는 객체 자체는 복사되지 않고 원본 값을 참조합니다. 따라서 원본 배열 내의 객체를 변경하는 경우 새로운 배열 내의 객체 값도 변경됩니다.

var arr1 = [{name: '철수', age: 10}]; 
var arr2 = [...arr1]; 

arr2[0].name = '영희';

console.log(arr1); // [ {name:'영희', age: 10}]
console.log(arr2); // [ {name:'영희', age: 10}]

 

2. 함수에서의 Spread Operator

✅ Rest Parameter

함수를 호출할 때 함수의 매개변수(parameter)를 spread operator로 작성한 형태를 Rest parameter라고 부릅니다. Rest 파라미터를 사용하면 함수의 파라미터로 오는 값들을 모아서 "배열"에 집어넣습니다. 이를 통해서 깔끔한 함수 표현을 적용할 수 있습니다. 

 

아래 더하기 예제를 살펴볼까요?

function add(...rest) {
  let sum = 0;
  for (let item of rest) {
    sum += item;
  }
  return sum;
}

console.log( add(1) ); // 1
console.log( add(1, 2) ); // 3
console.log( add(1, 2, 3) ); // 6

 

위의 결과에서 보이듯이, Rest parameter를 이용하면 인자의 개수에 상관없이 모든 인자를 더하는 함수를 쉽게 구현할 수 있습니다. (기존 JavaScript에서는 "arguments"를 이용하여 처리할 수 있습니다.)

 

다음과 같이 기본 인자를 섞어서 사용하는 방법도 가능합니다.

function addMul(method, ...rest){ 
  if (method === 'add') {
    let sum = 0;
    for (let item of rest) {
      sum += item;
    }    
     return sum;    
  } else if (method === 'multiply'){
    let mul = 1;
    for (let item of rest) {
      mul *= item;
    }
     return mul;    
  }
 
} 

console.log( addMul('add', 1,2,3,4) ); // 10
console.log( addMul('multiply', 1,2,3,4) ); // 24

 

단, Rest파라미터는 항상 제일 마지막 파라미터로 있어야 합니다. function addMul (...rest, method){ }와 같은 방식으로는 사용할 수 없습니다.

 

✅ 함수 호출 인자로 사용

위의 경우와 반대로, 함수 정의 쪽이 아니라 함수를 Call 할 때 spread operator를 사용할 수 있습니다. 기존에는 배열로 되어있는 내용을 함수의 인자로 넣어주려면 손으로(?) 풀어서 넣어주던지, Apply를 이용했어야 하지만 spread operator를 이용하면 배열 형태에서 바로 함수 인자로 넣어줄 수 있습니다.

 

가장 간단한 예로 Math 함수를 예로 들어볼까요? 

var numbers = [9, 4, 7, 1]; 
Math.min(...numbers); // 1

 

Map 하고 섞으면 다음과 같이도 응용이 가능하겠네요!

var input = [{name:'철수',age:12}, {name:'영희',age:12}, {name:'바둑이',age:2}];

//가장 어린 나이 구하기.
var minAge = Math.min( ...input.map((item) => item.age) );
console.log(minAge); // 2

 

3. 객체에서의 Spread Operator

ES2018 (ES9)에서는 객체와 관련된 사항이 추가되었습니다. 배열에 대한 Spread Opeator를 지원하는 최근의 브라우저는 객체에 대한 Spread Operator 역시 지원합니다.

✅ 객체 복사 또는 업데이트

객체에서 spread operator를 이용하여 객체의 복사 또는 프로퍼티를 업데이트 할 수 있습니다. 

간단한 State Management 구현을 위해서 다음과 같은 방식으로 응용하여 사용하기도 합니다. 

var currentState = { name: '철수', species: 'human'};
currentState = { ...currentState, age: 10}; 

console.log(currentState)// {name: "철수", species: "human", age: 10}

currentState = { ...currentState, name: '영희', age: 11}; 
console.log(currentState); // {name: "영희", species: "human", age: 11}

 

위에서 두번째 예제는 객체의 프로퍼티를 오버라이드 함으로써 객체가 업데이트되는 것을 이용한 내용입니다.

 

4. Destructuring

Spread Operator는 배열이나 객체에서의 destructuring에 사용될 수 있습니다. 이 경우에 의미적으로는 spread operator라기보다는 rest parameter에 가까운 형태가 되겠네요.

var a, b, rest;
[a, b] = [10, 20];

console.log(a); // 10
console.log(b); // 20

[a, b, ...rest] = [10, 20, 30, 40, 50];

console.log(rest); // [30,40,50]

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); // {c: 30, d: 40}

 

맺음말

ES6에 새로 도입된 Spread operator는 깔끔한 맛이 있어서 그런지 은근히 많이 사용되고 있습니다. 요즘은 IE11 외에는 ES6를 지원하고, 타입스크립트 등과 같이 컴파일하여 사용하는 언어도 많이 사용하므로 큰 부담 없이 많이 사용되는 것 같습니다. 개인적으로 주로 많이 사용한 부분으로는 앵귤러에서 상태 관리를 위해서 객체 값을 update 하거나 복사하려는 경우였던 것 같습니다.

Spread Operator를 이용하면 이런저런 응용이 가능하니 최근의 프론트엔드 라이브러리를 사용하고 있다면 스프레드 연산자는 꼭 알아두는 것이 좋을 것 같습니다.

 

반응형