개요
스프레드 문법(Spread Syntax)은 ES6에서 새롭게 도입된 문법이다. 이 문법은 마침표 세 개(...)를 사용하여 하나로 뭉쳐있는 여러 값들의 집합을 펼쳐서 개별적인 값들의 목록으로 변환한다. 여러 값을 하나로 뭉친 배열이나 객체와 같은 데이터를 전개해서 개별 값들로 분리할 때 유용하게 사용할 수 있다.
스프레드 문법의 사용 가능 대상
스프레드 문법은 모든 데이터 타입에 사용할 수 있는 것이 아니다. for...of 문으로 순회할 수 있는 이터러블 객체에만 사용할 수 있다는 제약이 있다. 구체적으로 다음과 같은 데이터 타입들이 스프레드 문법의 대상이 될 수 있다.
- Array: 가장 대표적인 이터러블 객체로, 배열의 요소들을 개별 값으로 펼칠 수 있다.
- String: 문자열도 이터러블이므로 각 문자를 개별적으로 분리할 수 있다.
- Map과 Set: ES6에서 도입된 이터러블 객체들로, 저장된 값들을 개별적으로 펼칠 수 있다.
- DOM 컬렉션: NodeList나 HTMLCollection과 같은 DOM 요소들의 집합도 이터러블이다.
- arguments: 함수에 전달된 인수들을 담고 있는 유사 배열 객체로, 이터러블이다.
// 배열을 개별 요소로 분리하기
const numbers = [1, 2, 3];
console.log(...numbers); // 1 2 3
// 문자열을 개별 문자로 분리하기
const greeting = 'Hello';
console.log(...greeting); // H e l l o
// Map 객체를 개별 엔트리로 분리하기
const map = new Map([['a', '1'], ['b', '2']]);
console.log(...map); // ['a', '1'] ['b', '2']
// Set 객체를 개별 값으로 분리하기
const set = new Set([1, 2, 3]);
console.log(...set); // 1 2 3
// 일반 객체는 이터러블이 아니므로 에러 발생
const obj = { a: 1, b: 2 };
console.log(...obj); // TypeError: obj is not iterable
스프레드 문법의 핵심 특징
스프레드 문법의 가장 중요한 특징은 그 결과가 값이 아닌 값들의 목록이라는 점이다. 이는 매우 중요한 개념인데, 값들의 목록은 단독으로 사용할 수 없고 특정한 문맥 내에서만 의미를 가진다. 예를 들어 다음과 같은 코드는 동작하지 않는다.
// 스프레드 문법의 결과를 직접 변수에 할당하려고 하면 에러가 발생한다
const list = ...[1, 2, 3]; // SyntaxError: Unexpected token ...
대신 스프레드 문법은 다음과 같은 세 가지 문맥에서만 사용할 수 있다.
- 함수의 인수 목록을 작성할 때
- 배열 리터럴의 요소들을 나열할 때
- 객체 리터럴의 프로퍼티들을 나열할 때
함수 호출문의 인수 목록에서 사용하는 경우
함수를 호출할 때 스프레드 문법을 활용하면 매우 유용하다. 특히 배열의 요소들을 함수의 인수로 전달해야 할 때 스프레드 문법은 코드를 매우 간결하고 직관적으로 만들어준다.
Math.max() 메서드 활용 예시
Math.max() 메서드를 통해 스프레드 문법의 활용을 살펴보자. Math.max()는 숫자 타입의 인수들 중에서 가장 큰 값을 반환하는 메서드로, 매개변수의 개수가 정해져 있지 않은 가변 인자 함수이다. 다음은 배열의 요소들 중 최대값을 구하는 예시다.
const numbers = [1, 2, 3];
// 배열을 직접 전달하면 최대값을 구할 수 없다
const wrongMax = Math.max(numbers); // NaN
// 스프레드 문법으로 배열을 펼쳐서 전달하면 최대값을 구할 수 있다
const correctMax = Math.max(...numbers); // 3
배열을 직접 Math.max()의 인수로 전달하면 NaN이 반환된다. 이는 Math.max()가 배열이 아닌 숫자들의 목록을 기대하기 때문이다. 스프레드 문법을 사용하면 배열을 개별 숫자들의 목록으로 펼쳐서 전달할 수 있어 원하는 결과를 얻을 수 있다.
Rest 파라미터와의 차이점 이해하기
스프레드 문법을 배우다 보면 Rest 파라미터와 혼동하기 쉽다. 둘 다 '...'을 사용하지만, 그 역할은 정반대이다.
// Rest 파라미터는 여러 개의 인수를 하나의 배열로 모은다
function sum(...numbers) {
// numbers는 전달받은 모든 인수를 포함하는 배열이다
return numbers.reduce((acc, cur) => acc + cur, 0);
}
// 스프레드 문법은 배열을 개별 값들로 펼친다
const numbers = [1, 2, 3, 4, 5];
console.log(sum(...numbers)); // 15
Rest 파라미터는 함수가 전달받은 여러 개의 인수들을 하나의 배열로 모으는 역할을 한다. 반면 스프레드 문법은 하나의 배열을 여러 개의 개별 값들로 펼치는 역할을 한다. 이처럼 두 문법은 서로 상반된 동작을 수행한다.
이전 방식과의 비교
스프레드 문법이 도입되기 전에는 배열의 요소들을 함수의 인수로 전달하기 위해 Function.prototype.apply 메서드를 사용해야 했다.
// 이전 방식: Function.prototype.apply 사용
var numbers = [1, 2, 3];
var maxNumber = Math.max.apply(null, numbers); // 3
// 현재 방식: 스프레드 문법 사용
const numbers = [1, 2, 3];
const maxNumber = Math.max(...numbers); // 3
apply 메서드를 사용하는 방식은 코드의 의도를 파악하기 어렵고 불필요한 this 바인딩을 위한 null 값도 전달해야 했다. 반면 스프레드 문법을 사용하면 코드가 훨씬 더 간결하고 직관적이며, 실수할 여지도 줄어든다.
배열 리터럴 내부에서 사용하는 경우
스프레드 문법은 배열 리터럴 내부에서도 사용할 수 있다. ES5에서 사용하던 기존 방식과 비교했을 때, 스프레드 문법을 활용하면 더욱 간결하고 직관적인 코드를 작성할 수 있다.
concat
ES5에서는 두 개 이상의 배열을 하나로 결합하기 위해 concat 메서드를 사용해야 했다. 이는 메서드 호출을 통해 새로운 배열을 생성하는 방식이었다.
// ES5 방식
var arr = [1, 2].concat([3, 4]);
console.log(arr); // [1, 2, 3, 4]
ES6의 스프레드 문법을 사용하면 메서드 호출 없이 배열 리터럴 만으로도 여러 배열을 손쉽게 결합할 수 있다. 이는 코드의 가독성을 높이고 작성을 더욱 간편하게 만든다.
// ES6의 스프레드 문법 사용
const arr = [...[1, 2], ...[3, 4]];
console.log(arr); // [1, 2, 3, 4]
splice
ES5에서 배열의 중간에 다른 배열의 요소들을 추가하거나 제거할 때는 splice 메서드를 사용했다. 그러나 이 방식에는 주의해야 할 점이 있다. splice 메서드의 세 번째 인수로 배열을 직접 전달하면 배열 자체가 요소로 추가되어 의도하지 않은 결과가 나온다.
// ES5에서의 문제점
var arr1 = [1, 4];
var arr2 = [2, 3];
arr1.splice(1, 0, arr2);
console.log(arr1); // [1, [2, 3], 4] - 배열이 통째로 삽입됨
이 문제를 해결하기 위해 ES5에서는 Function.prototype.apply 메서드를 사용해야 했다. 이는 코드를 복잡하게 만들고 이해하기 어렵게 만든다.
// ES5의 해결 방식
var arr1 = [1, 4];
var arr2 = [2, 3];
Array.prototype.splice.apply(arr1, [1, 0].concat(arr2));
console.log(arr1); // [1, 2, 3, 4]
ES6의 스프레드 문법을 사용하면 이러한 복잡한 과정 없이 간단하게 배열 요소를 추가할 수 있다.
// ES6의 스프레드 문법 사용
const arr1 = [1, 4];
const arr2 = [2, 3];
arr1.splice(1, 0, ...arr2);
console.log(arr1); // [1, 2, 3, 4]
배열 복사
배열을 복사할 때도 스프레드 문법은 유용하다. ES5에서는 배열을 복사하기 위해 slice 메서드를 사용해야 했다.
// ES5 방식의 배열 복사
var origin = [1, 2];
var copy = origin.slice();
console.log(copy); // [1, 2]
console.log(copy === origin); // false
ES6의 스프레드 문법을 사용하면 더욱 직관적으로 배열을 복사할 수 있다. 이때 생성되는 복사본은 얕은 복사(shallow copy)를 통해 생성된다.
// ES6의 스프레드 문법을 사용한 배열 복사
const origin = [1, 2];
const copy = [...origin];
console.log(copy); // [1, 2]
console.log(copy === origin); // false
이터러블을 배열로 변환
이터러블을 배열로 변환하는 방법도 스프레드 문법을 통해 더욱 간단하게 구현할 수 있다. ES5에서는 이러한 변환을 위해 Function.prototype.apply나 Function.prototype.call 메서드를 사용해 slice 메서드를 호출해야 했다.
// ES5 방식
function sum() {
// 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환
var args = Array.prototype.slice.call(arguments);
return args.reduce(function(pre, cur) {
return pre + cur;
}, 0);
}
console.log(sum(1, 2, 3)); // 6
ES5 방식은 이터러블뿐만 아니라 유사 배열 객체도 배열로 변환할 수 있다는 장점이 있다.
// 유사 배열 객체를 배열로 변환 - ES5
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
const arr = Array.prototype.slice.call(arrayLike); // [1, 2, 3]
console.log(Array.isArray(arr)); // true
ES6의 스프레드 문법을 사용하면 이터러블을 배열로 변환하는 과정이 훨씬 간단해진다.
// ES6의 스프레드 문법 사용
function sum() {
// 이터러블이면서 유사 배열 객체인 arguments를 배열로 변환
return [...arguments].reduce((pre, cur) => pre + cur, 0);
}
console.log(sum(1, 2, 3)); // 6
하지만 여기서 주의할 점이 있다. 스프레드 문법은 일반 유사 배열 객체에는 사용할 수 없다.
// 유사 배열 객체
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
// 스프레드 문법은 이터러블이 아닌 유사 배열 객체에는 사용할 수 없다
const arr = [...arrayLike]; // TypeError: arrayLike is not iterable
이터러블이 아닌 유사 배열 객체를 배열로 변환하려면 ES6에서 도입된 Array.from 메서드를 사용해야 한다.
// Array.from은 유사 배열 객체 또는 이터러블을 배열로 변환한다
const arr = Array.from(arrayLike); // [1, 2, 3]
또한 arguments와 같은 유사 배열 객체를 다루는 더 나은 방법은 Rest 파라미터를 사용하는 것이다.
// Rest 파라미터 사용
const sum = (...args) => args.reduce((pre, cur) => pre + cur, 0);
console.log(sum(1, 2, 3)); // 6
객체 리터럴 내부에서 사용하는 경우
객체 리터럴 내부에서도 스프레드 문법을 사용할 수 있다. 이는 현재 정식 사양에 포함된 스프레드 프로퍼티라는 기능이다. 일반적인 스프레드 문법은 이터러블 객체만을 대상으로 하지만, 스프레드 프로퍼티는 일반 객체에 대해서도 스프레드 문법의 사용을 허용한다는 특징이 있다.
객체의 얕은 복사
스프레드 프로퍼티를 사용하면 객체를 간단히 복사할 수 있다. 이때 생성되는 복사본은 원본 객체의 얕은 복사본이다.
const obj = { x: 1, y: 2 };
const copy = { ...obj };
console.log(copy); // { x: 1, y: 2 }
console.log(obj === copy); // false
위 예제에서 copy는 obj의 얕은 복사본으로, 프로퍼티 값은 같지만 서로 다른 객체를 참조하고 있다. 따라서 일치 비교(===) 연산자로 비교하면 false가 반환된다.
Object.assign 메서드 대체하기
스프레드 프로퍼티가 도입되기 이전에는 ES6의 Object.assign 메서드를 사용하여 객체를 병합하거나 프로퍼티를 변경, 추가했다. 이제는 스프레드 프로퍼티를 통해 더 간단하게 이러한 작업을 수행할 수 있다.
객체 병합
여러 객체를 하나로 병합할 때 스프레드 프로퍼티를 사용할 수 있다. 이때 중복되는 프로퍼티가 있다면 뒤에 위치한 프로퍼티가 앞선 프로퍼티를 덮어쓴다.
// Object.assign 사용 방식
const merged1 = Object.assign({}, { x: 1, y: 2 }, { y: 10, z: 3 });
console.log(merged1); // { x: 1, y: 10, z: 3 }
// 스프레드 프로퍼티 사용 방식
const merged2 = { ...{ x: 1, y: 2 }, ...{ y: 10, z: 3 } };
console.log(merged2); // { x: 1, y: 10, z: 3 }
프로퍼티 변경
특정 프로퍼티의 값을 변경할 때도 스프레드 프로퍼티를 활용할 수 있다. 객체를 펼친 후 변경하고자 하는 프로퍼티를 새로운 값으로 정의하면 된다.
// Object.assign 사용 방식
const changed1 = Object.assign({}, { x: 1, y: 2 }, { y: 100 });
console.log(changed1); // { x: 1, y: 100 }
// 스프레드 프로퍼티 사용 방식
const changed2 = { ...{ x: 1, y: 2 }, y: 100 };
console.log(changed2); // { x: 1, y: 100 }
프로퍼티 추가
새로운 프로퍼티를 추가할 때도 스프레드 프로퍼티를 사용할 수 있다. 기존 객체의 프로퍼티들을 펼친 후 새로운 프로퍼티를 정의하면 된다.
// Object.assign 사용 방식
const added1 = Object.assign({}, { x: 1, y: 2 }, { z: 0 });
console.log(added1); // { x: 1, y: 2, z: 0 }
// 스프레드 프로퍼티 사용 방식
const added2 = { ...{ x: 1, y: 2 }, z: 0 };
console.log(added2); // { x: 1, y: 2, z: 0 }
요약
스프레드 문법 개요
- ES6에서 도입된 스프레드 문법(...)은 하나로 뭉쳐있는 여러 값들의 집합을 개별적인 값들의 목록으로 펼치는 문법이다.
- for...of 문으로 순회 가능한 이터러블에만 사용할 수 있으며, 대표적으로 Array, String, Map, Set, DOM 컬렉션 등이 있다.
- 스프레드 문법의 결과는 값이 아닌 값들의 목록이므로 변수에 직접 할당할 수 없다.
함수 호출문의 인수 목록에서 사용
- 배열 등 이터러블의 요소들을 함수의 인수로 전달할 때 유용하게 사용할 수 있다.
- Math.max()와 같은 가변 인자 함수에 배열의 요소들을 개별 값으로 전달할 수 있다.
- ES5의 Function.prototype.apply 메서드 사용을 대체할 수 있어 더 간편하고 가독성이 좋다.
배열 리터럴 내부에서 사용
- concat: 여러 배열을 하나로 결합할 때 사용할 수 있다.
- splice: 배열의 중간에 다른 배열의 요소들을 추가할 때 사용할 수 있다.
- 배열 복사: 기존 배열의 얕은 복사본을 생성할 수 있다.
- 이터러블을 배열로 변환: 이터러블 객체를 배열로 변환할 수 있으나, 유사 배열 객체는 Array.from을 사용해야 한다.
객체 리터럴 내부에서 사용
- 스프레드 프로퍼티를 통해 일반 객체에도 스프레드 문법을 사용할 수 있다.
- 객체의 복사, 객체 병합, 프로퍼티 변경, 프로퍼티 추가 등에 활용할 수 있다.
- Object.assign 메서드를 대체할 수 있는 더 간편한 방법을 제공한다.
- 프로퍼티가 중복될 경우 뒤에 위치한 프로퍼티가 우선권을 가진다.
주의사항
- Rest 파라미터와 형태가 동일해서 혼동할 수 있으나, 스프레드 문법은 펼치는 것이고 Rest 파라미터는 모으는 것이다.
- 이터러블이 아닌 일반 유사 배열 객체는 스프레드 문법의 대상이 될 수 없으며, 이 경우 Array.from을 사용해야 한다.
- 객체의 스프레드 프로퍼티는 얕은 복사만을 수행한다.
예상문제 [🔥]
https://github.com/junh0328/prepare_frontend_interview?tab=readme-ov-file
스프레드 문법이 무엇인가요?
스프레드 문법은 ES6에서 도입된 문법으로, 마침표 세 개(...)를 사용해서 하나로 뭉쳐있는 값들의 집합을 개별적인 값들의 목록으로 펼치는 문법입니다. 예를 들어, [1, 2, 3] 이라는 배열이 있을 때 ...[1, 2, 3]은 1, 2, 3이라는 개별 값들의 목록으로 펼쳐지게 됩니다.
주의할 점은 스프레드 문법은 for...of 문으로 순회할 수 있는 이터러블에만 사용할 수 있다는 제약이 있습니다. 대표적으로 배열, 문자열, Map, Set 등이 있으며, 일반 객체는 이터러블이 아니지만 스프레드 프로퍼티라는 별도의 제안을 통해 객체에서도 사용할 수 있게 되었습니다.
어떤 상황에서 사용할 수 있나요?
스프레드 문법은 ES6에서 도입된 문법으로, 마침표 세 개(...)를 사용해서 하나로 뭉쳐있는 값들의 집합을 개별적인 값들의 목록으로 펼치는 문법입니다. 예를 들어, [1, 2, 3] 이라는 배열이 있을 때 ...[1, 2, 3]은 1, 2, 3이라는 개별 값들의 목록으로 펼쳐지게 됩니다.
주의할 점은 스프레드 문법은 for...of 문으로 순회할 수 있는 이터러블에만 사용할 수 있다는 제약이 있습니다. 대표적으로 배열, 문자열, Map, Set 등이 있으며, 일반 객체는 이터러블이 아니지만 스프레드 프로퍼티라는 별도의 제안을 통해 객체에서도 사용할 수 있게 되었습니다.
스프레드 문법은 크게 세 가지 상황에서 유용하게 사용될 수 있습니다.
1. 함수 호출의 인수 목록으로 사용할 때
- 예를 들어 Math.max()와 같은 가변 인자 함수에 배열의 요소들을 전달할 때 유용합니다.
const numbers = [1, 2, 3];
Math.max(...numbers); // 3
2. '배열' 리터럴 내부에서 사용할 때
- 배열의 결합, 복사, 요소 삽입 등을 할 수 있습니다.
// 배열 결합
const arr1 = [...[1, 2], ...[3, 4]]; // [1, 2, 3, 4]
// 배열 복사
const original = [1, 2];
const copy = [...original];
3. '객체' 리터럴 내부에서 사용할 때
- 객체의 복사나 프로퍼티 병합에 사용됩니다.
const obj1 = { foo: 1, bar: 2 };
const obj2 = { ...obj1, baz: 3 }; // { foo: 1, bar: 2, baz: 3 }
이러한 스프레드 문법은 ES5에서 사용하던 Function.prototype.apply나 Object.assign 등의 메서드를 대체할 수 있어 코드를 더 간결하고 가독성 있게 만들어줍니다.
'🧱 프론트엔드 주제 > JavaScript' 카테고리의 다른 글
[모던 자바스크립트 Deep Dive] 38장 - 브라우저의 렌더링 과정 (3) | 2024.10.31 |
---|---|
[모던 자바스크립트 Deep Dive] 36장 - 배열 디스트럭처링 할당 (0) | 2024.10.25 |
[모던 자바스크립트 Deep Dive] 26장 - ES6 함수의 추가 기능 (8) | 2024.10.17 |
[모던 자바스크립트 Deep Dive] 25장 - 클래스 (1) | 2024.10.15 |
[모던 자바스크립트 Deep Dive] 23장 - 실행 컨텍스트 (0) | 2024.08.11 |