본문 바로가기
  • 프론트엔드 개발자 세오세오 | Frontend dev Seo
Learn to Code

[JS] Rest 파라미터 vs. 스프레드 문법(Spread syntax)

by CEOSEO 2021. 3. 23.
728x90
반응형

Rest parameter와 스프레드 문법은 모양이 똑같아서 ( ... ) 굉장히 혼돈하기 쉽다. 하지만 둘은 엄연히 다른 것이다. 사실, 그 의미를 살펴보면 서로 반대의 개념을 갖고 있다고 볼 수 있다.

 

Rest parameters는 함수에 전달된 인수들의 목록을 하나의 배열로 전달받기 위해서 매개변수 이름 앞에 ...을 붙이는 것인 반면, 스프레드 문법은 여러개의 값이 하나로 뭉쳐있는 이터러블(iterable. e.g. 배열)을 펼쳐서 개별적인 값들의 목록을 만드는 것이다.

 

Rest parameters: 값들을 뭉쳐서 만드는 것

스프레드 문법: 뭉쳐있는 값들을 펼치는 것

 

 

Rest 파라미터

Rest 파라미터는 매개변수 이름 앞에 점 3개를 붙여서 정의한 매개변수를 의미한다. 이때 함수에 전달된 인수들은 배열로 전달된다.

yourFunction(1, 2, 3, 4, 5);

function yourFunction(...rest) {
	console.log(rest) // [1, 2, 3, 4, 5]
}

 

함수를 만들 때 그 인자로 Rest 파라미터를 집어넣으면, 함수에 전달되는 인자들의 개수를 제한하지 않아도 된다. 위 예제의 함수 yourFunction엔 rest 파라미터가 유일한 아이로 들어갔지만, 일반 다른 매개변수와 함께 사용할 수도 있다. 이때엔 함수들 중에 가장 뒤에 넣어줘야 하며, 함수를 호출할 때 전달된 인수들은 매개변수와 rest 파라미터에 순차적으로 할당된다.

 

yourFunction(1, 2, 3, 4, 5);

function yourFunction (parameter1, ...rest) {
	console.log(parameter1); // 1
    console.log(rest); // [2, 3, 4, 5]
}
yourCousinsFunction(1, 2, 3, 4, 5);

function yourCounsinsFunction (parameter1, parameter2 ...rest) {
	console.log(parameter1); // 1
    console.log(parameter2); // 2
    console.log(rest); // [3, 4, 5]
}

 

 

Rest 파라미터 vs. arguments 객체

Rest 파라미터가 사용되기 전엔, 함수를 만들 때 매개변수의 개수를 확정할 수 없는 경우에 arguments 객체를 활용했다.

arguments: 함수가 호출될 때 전달된 인수(argument)들의 정보를 가지고 있는 유사 배열 객체 (array-like object)인 이터러블(iterable). 함수 내부에서 지역 변수처럼 사용이 가능하다.

Rest 파라미터가 도입된 뒤엔 잘 사용되지 않는데, 왜냐하면 arguments 객체는 배열이 아닌 유사 배열 객체여서 배열 메소드 사용이 불가능하기 때문이다. arguments 객체에 배열 고차함수 등을 사용하기 위해선 유사 배열 객체인 아이를 배열로 변환하는 길고 긴(?) 작업을 거쳐야하는 번거로움이 있다.

 

다행히, ES6 이후엔 rest 파라미터의 도입으로 더욱 쉽게 가변 인자 함수를 사용할 수 있게 되었다.

function getOurMoney (...args) {
	return args.reduce( (myMoney, yourMoney) => myMoney + yourMoney, 0)
}

console.log(getOurMoney(100, 200, 300, 400, 500))

 

 


 

 

 

스프레드 문법 

스프레드 문법은 크게 3가지의 경우에 사용한다.

1. 함수 호출문의 인수 목록

2. 배열 리터럴의 요소 목록

3. 객체 리터럴의 프로퍼티 목록

 

 

1. 함수 호출문의 인수 목록

let arr = [1,2,3,4]
Math.max(arr)  // NaN
Math.max(...arr) // 4

 

위 예시와 같이 Math.max()에 인자로 배열인 arr를 넣게 되면, NaN가 나온다. 그러나 ...을 붙인 스프레드 문법을 사용한 ...arr를 인자로 전달하면, ...arr는 [1,2,3,4]를 1 2 3 4와 같이 목록으로 만들어주기 때문에 이 중 가장 큰 4를 올바르게 리턴하게 된다.

 

 

2. 배열 리터럴의 요소 목록

2-1. 배열과 배열을 합치고 싶을 때

스프레드 문법이 도입된 ES6 이전엔, 배열과 배열을 합치기 위해서 별도의 메소드를 사용해야 했다.

let myArr = [1, 2]
let yourArr = [3, 4]
let ourArr = myArr.concat(yourArr)

console.log(ourArr) // [1, 2, 3, 4]

스프레드 문법을 사용하면 똑같은 결과물을 메소드의 사용 없이 더 간편하게 이뤄낼 수 있다.

let myArr = [1, 2]
let yourArr = [3, 4]
let ourArr = [...myArr, ...yourArr]

console.log(ourArr) // [1, 2, 3, 4]

 

 

2-2. 배열의 중간에 다른 배열을 넣고 싶을 때

배열의 중간에 다른 무언가를 추가하고 싶다면 splice 메소드를 사용한다.

splice( start, deleteCount, insertItems )

splice에는 3개의 인자가 들어간다.

  • start: 원본 배열의 요소를 제거하기 시작할 인덱스
  • deleteCount: start부터 제거할 요소의 개수. 만약 0이라면 아무것도 제거하지 않는다.
  • insertItems: 제거한 위치에 삽입할 요소들의 목록. 비워둔다면 아무 것도 새로 넣지 않는다.

splice를 이용하여 배열의 중간에 또 다른 배열을 추가하는 예제는 다음과 같다:

let arr1 = ['L', 'E']
let arr2 = ['O', 'V']

arr1.splice(1, 0, arr2) // ['L', ['O', 'V'], 'E']

이렇게 해도, 어찌저찌 사랑(love)이(?) 완성되긴 한다. 하지만 중간에 이물질(?) 괄호들이 끼게 된다. 우리의 사랑엔 걸림돌이 없어야 하기에 아래와 같이 스프레드 문법을 사용해준다.

let arr1 = ['L', 'E']
let arr2 = ['O', 'V']

arr1.splice(1, 0, ...arr2)

console.log(arr1) // ['L', 'O', 'V', 'E']

 

 

2-3. 배열을 복사하고 싶을 때

ES6 이전엔 배열을 복사하려면 slice (슬라이스) 메소드를 사용했다. (위 2-2의 splice (스플라이스)와는 다르다. 혼동 주의)

let yourMoney = ['salary', 'house', 'Tesla Stock']
let myMoney = yourMoney.slice();

console.log(myMoney) // ['salary', 'house', 'Tesla Stock']
console.log(myMoney === yourMoney) // false

 

스프레드 문법을 사용하면, slice를 사용했을 때 가독성이 아주 쪼매나게 좋아지면서 동일한 결과물을 얻는 코드를 작성할 수 있다.

let yourMoney = ['salary', 'house', 'Tesla Stock']
let myMoney = [...yourMoney]

console.log(myMoney) // ['salary', 'house', 'Tesla Stock']
console.log(myMoney === yourMoney) // false

** 참고: slice 또는 스프레드 문법을 사용한 복사는 모두 얕은 복사(shallow copy)이다.

 

 

 

2-4. 이터러블(iterable)을 배열로 바꾸고 싶을 때

이터러블(iterable)은 for...of 반복문을 사용할 수 있는 배열 및 유사 배열들을 뜻한다. '반복하다'라는 뜻을 가진 iterate이라는 동사에 '가능하다'를 뜻하도록 + able이 붙어서 '반복가능한 놈'이 되었다고 생각하면 된다.

 

배열이 아니면서 배열과 비스무레한 유사배열들은 배열의 내장 고차함수 등을 사용하려면 배열이 되기 위해 신분 세탁을 해줘야한다. 그 신분세탁을 진행하기 위해 스프레드 문법을 사용하는 것이다.

 

function tungjang() {
	return [...arguments].reduce( (income, costOfLiving) => income - costOfLiving, 0)

 

그러나, 스프레드 문법은 이터러블에 한해서만 사용이 가능하다. 즉, 이터러블이 아닌 유사 배열 객체에는 스프레드 문법을 사용할 수 없다는 것이다.

 

따라서, 만약 유사 배열 객체를 배열로 변경하려면 스프레드 문법이 아니라 Array.from 메소드를 사용해야 한다.

 

 

 

3. 객체 리터럴(Object literals)의 프로퍼티 목록

원래 객체를 복사하려면 Object.assign 메소드를 사용해야했다. 그러나Rest/Spread Properties for ECMAScript proposal (ES2018) 를 통해 객체에도 스프레드 문법을 사용할 수 있게 되었고, 손쉽게 객체의 얕은 복사를 할 수 있게 되었다.

let myWish = {
	car: 'Tesla Model S',
    computer: 'Macbook Pro M1 16',
    coffee: 'Iced Americano'
    }

let yourWish = {...myWish}

console.log(yourWish) // {car: 'Tesla Model S', computer: 'Macbook Pro M1 16', coffee: 'Iced Americano'}

 

마찬가지로, 객체를 병합할 때도 스프레드 문법을 사용할 수 있다.

let obj1 = {a: 1, b: 2}
let obj2 = {c: 3, d: 4}

let objTogether = {...obj1, ...obj2} // {a: 1, b: 2, c: 3, d: 4}

 

 

 

 

728x90
반응형

댓글