Spread operator과 Rest Parameter

조 은길·2021년 2월 26일
0

Javascript 정리

목록 보기
16/48
post-thumbnail

Spread operator(전개구문)

구조 분해 할당에 앞서서, 구조 분해 할당에 많이 응용되는 spread operator를 먼저 알아보자!!

Spread operator는 ES6에서 도입된 이후, 많이 사용되고 있다. 요즘은 곧 사라질 IE 외에는 모든 브라우져가 ES6를 지원하고, 타입스크립트 같이 컴파일하여 사용하는 언어도 많이 사용하므로 큰 부담 없이 많이 사용되는 것 같다.

spread operator를 한 마디로 표현하자면 "배열 요소를 한 번에 펼쳐준다"이다.

하지만, 백문이 불여일견이라고 직접 예문을 보는 게 100번 낳다!!

Spread operator 배열에 적용하기

// arr은 모든 예시에 다 적용된다고 가정하자!!

const arr = ["a1", "a2", "a3"];

1. 전개 구문의 기본 효과

// 배열의 형태가 사라진 것을 확인할 수 있다.
console.log(...arr); // => a1 a2 a3 

2. 배열의 추가

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

// 1. 배열의 추가
const num2 = [10, 20, 30, ...arr];
console.log(num2); // [ 10, 20, 30, "a1", "a2", "a3"]

3. 배열의 중간 추가

const num1 = [1, 2, ...arr, 3];
// push() 사용하지 않고도, 이렇게 간단하게 배열 어디에나 집어넣을 수 있다.
console.log(num1); // [ 1, 2, "a1", "a2", "a3", 3 ]

4. 배열의 복사

  • arr이 변경된다고 해서, num3 값이 변경되지는 않는다. 즉, 새로운 복사된 배열을 생성할 수 있다.
const num3 = [...arr];
console.log(num3); // ["a1", "a2", "a3"]

arr === num3; // false

const arr1 = arr;
arr1 === arr; // true

5. 배열의 연결

const num4 = [...arr, ...num1, ...num2];
console.log(num4); 
// ["a1", "a2", "a3", 1, 2, "a1", "a2", "a3", 3, 10, 20, 30, "a1", "a2", "a3"]

단, Spread 연산자를 이용한 복사는 얕은(shallow) 복사를 수행하며, 배열 안에 객체가 있는 경우에는 객체 자체는 복사되지 않고 원본 값을 참조한다.

let arr1 = [{name: '조은길', age: 20}]; 
let arr2 = [...arr1]; 

arr2[0].name = '변경된 이름';

// arr2 의 변화가 arr1에도 똑같이 미친다.
console.log(arr1); // [ { name: '변경된 이름', age: 20 } ]
console.log(arr2); // [ { name: '변경된 이름', age: 20 } ]

배열 안에 또다른 배열이 있는 경우에도 내부의 배열 자체는 복사되지 않고 원본 값을 참조한다.

let arr3 = [['조은길', 20]]; 
let arr4 = [...arr3];

arr4[0][0] = '변경된 이름';

console.log(arr3); // [ [ '변경된 이름', 20 ] ]
console.log(arr4); // [ [ '변경된 이름', 20 ] ]

Spread operator 함수에 적용하기

Rest Parameter

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

// num은 모든 예시에 다 적용된다고 가정하자!!
const num = [1, 2, 3, 4];
function sum(x, y, z) {
  return x + y + z;
}

// x === num[0], y === num[1], z === num[2]
console.log(sum(...num)); // 6

// x === 10 , y === num[0] , z === num[1]
console.log(sum(10, ...num)); // 13

매개변수가 몇 개일지 모를 때

  • 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
  • 기본 인자를 섞어서 사용하는 방법도 가능하다.

ex 1)

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

ex 2)

// 함수의 매개변수의 적용( 가변적 매개변수 )
function sum2(x, ...arr) {
  // arr은 현재 배열의 형태이기 때문에, 다시 한번 ...을 써줘서 배열을 풀어줘야 한다.
  return [x, ...arr];
}

console.log(sum2("홍길동", 1)); // ["홍길동", 1 ]
console.log(sum2("홍길동", 1, 2)); // ["홍길동", 1 , 2]
console.log(sum2("홍길동", 1, 2, 3, 4, 5, "hello")); 
// ["홍길동", 1, 2, 3, 4, 5, "hello"]

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

Spread operator 함수 호출 인자로 사용

함수 정의 쪽이 아니라 함수를 Call 할 때 spread operator를 사용할 수 있다. 기존에는 배열로 되어있는 내용을 함수의 인자로 넣어주려면 보내주기 전에 Apply나 for문을 통한 추가 작업이 필요했다. 하지만 spread operator를 이용하면 배열 형태에서 바로 함수 인자로 넣어줄 수 있다.

배열의 내장 메소드에 바로 적용하기

const numbers = [9, 4, 7, 1]; 

// 이건 진짜 신세게!! ㅇㅈ??
Math.min(...numbers); // 1

Map과 응용하기

Map은 배열을 return하므로, 다양한 응용법이 가능하다.

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

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

함수의 default 매개값 + Spread operator

// 함수의 default 매개값
// y=10  : y 값이 전달 되지 않으면, 10으로 할당하고, 전달이 되면 그 값으로 할당한다.
function sum3(x, y = 10, z = 100) {
  return x + y + z;
}

console.log(sum3(1)); // 111
console.log(sum3(1, 2)); // 103
console.log(sum3(1, 2, 3)); // 6
console.log(sum3(...[1, 2, 3, 4])); // 6

객체에서의 Spread Operator

배열에 대한 Spread Opeator를 지원하는 브라우저들은 객체에 대한 Spread Operator 역시 지원한다.

객체 복사 또는 업데이트

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

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

조금 헷갈릴 수도 있지만, 익숙해지면 코딩이 편해진다.

간단한 예시)

let 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}

간단한 예시 응용법)

const user = {
      name: "김코딩",
      company: {
        name: "Code States",
        department: "Development",
        role: {
          name: "Software Engineer",
        },
      },
      age: 35,
    };

    const changedUser = {
      ...user,
      name: "박해커",
      age: 20,
    };

console.log( changedUser ); 
/*
{
  name: '박해커',
  company: {
    name: 'Code States',
    department: 'Development',
    role: { name: 'Software Engineer' }
  },
  age: 20
}
*/

// 이렇게 하면 user라는 객체 자체가 안으로 들어가 버린다.
// user가 key 값 그리고 객체 그 자체가 value로 들어간다.

// 하지만 ... 스프레드 오퍼레이터를 사용할 경우, 
// user의 객체가 풀어져서 객체 안의 내용물들이 그대로 이식 된다.


 const overwriteChanges = {
      name: "박해커",
      age: 20,
      ...user,
    };

console.log( overwriteChanges );
/*
{
  name: '김코딩',
  age: 35,
  company: {
    name: 'Code States',
    department: 'Development',
    role: { name: 'Software Engineer' }
  }
}
*/

const changedDepartment = {
      ...user,
      company: {
        ...user.company,
        department: "Marketing",
      },
    };

console.log( changedDepartment );
/*
{
  name: '김코딩',
  company: {
    name: 'Code States',
    department: 'Marketing',
    role: { name: 'Software Engineer' }
  },
  age: 35
}
*/

글을 마치며

Spread Operator를 모르고, 요즘 코드 뜯어보면, 100% 이해하기 어렵다.
그만큼, 이제는 모르면 안 되는 구문이고, 동시에 내 것으로 만들면, 코딩이 한결 편해지는 고마운 구문이다.

다음 편은 Spread Operator의 연장선 => 구조 분해 할당입니다.

자료 출처 및 참고 자료

profile
좋은 길로만 가는 "조은길"입니다😁

0개의 댓글