구조 분해(destructuring)는 객체나 배열에서 값을 꺼내 한 번에 이름을 붙이는 꼴입니다. const { name, price } = item;은 item에서 같은 이름의 속성을 꺼내고, const [first, second] = pair;는 배열의 칸을 자리 차례로 꺼냅니다.
속성 여럿의 값에 이름을 붙이려면 const name = item.name;처럼 속성마다 한 줄씩 써야 합니다. 이름과 키를 두 번씩 쓰고, 꺼낼 속성이 늘면 같은 꼴의 줄도 늡니다.
이 레슨에서는 객체와 배열에서 한 줄로 꺼내는 두 꼴과 두 값을 맞바꾸는 꼴을 보고, 객체에 없는 이름을 꺼내면 그 이름이 무엇에 붙는지 그림에서 먼저 맞혀 봅니다.
1객체에서 같은 이름의 속성 꺼내기
그림 앞 장면의 코드에 찍는 줄 둘을 더했습니다.
const item = {
name: '커피',
price: 4000,
};
const { name, price } = item;
const { size } = item;
console.log(name, price);
console.log(size);
커피 4000
undefined5번 줄 왼쪽의 중괄호는 객체를 만들지 않습니다. item에서 꺼낼 속성 이름을 늘어놓은 목록입니다. item의 name 속성과 price 속성을 꺼내, 같은 이름 name과 price를 그 값에 붙입니다. const name = item.name;과 const price = item.price; 두 줄을 한 줄로 쓴 것과 같습니다.
이름으로 찾으므로 늘어놓는 차례는 상관없습니다. const { price, name } = item;이라 써도 name은 '커피'에 붙습니다.
6번 줄의 size는 item에 없는 속성입니다. item.size가 undefined이듯(객체: 이름 붙은 칸 묶음), size도 undefined에 붙고 멈추지 않습니다.
그림에서 5번 줄은 객체 ①의 두 줄에서 값을 꺼내, 그 값에 이름 name과 price를 붙입니다. 꺼내도 ①의 두 줄은 그대로 남습니다.
꺼낼 객체가 아예 없으면 어떻게 될까요? 아래 코드는 주문이 둘뿐인 배열에서 셋째 주문을 꺼냈습니다(객체를 담은 배열). 무엇을 찍을지 답을 정한 뒤 펼쳐 보세요.
const orders = [
{ item: '커피', qty: 2 },
{ item: '라테', qty: 1 },
];
const order = orders[2];
const { item } = order;
console.log(item);
실행 결과 보기
main.js:6const { item } = order; ^ TypeError: Cannot destructure property 'item' of 'order' as it is undefined. at main.js:6:9예상과 다르면 그곳이 배울 곳입니다. 5번 줄의 orders[2]는 칸 밖이라 order가 undefined에 붙습니다. 6번 줄은 그 undefined에서 속성 item을 꺼내려다 TypeError로 멈추고, 7번 줄은 실행되지 않습니다.
오류 메시지는 'order가 undefined라서 속성 item을 꺼낼 수 없다'는 뜻입니다. 없는 속성은 undefined로 지나가지만, 꺼낼 객체가 undefined면 멈춥니다.
2배열에서 자리 차례로 꺼내기
배열에서 꺼낼 때는 왼쪽을 대괄호로 씁니다.
const pair = ['라테', 4500];
const [first, second] = pair;
console.log(first);
console.log(second);
라테
45002번 줄은 pair의 0번 칸 값에 first를, 1번 칸 값에 second를 붙입니다. 배열 칸에는 이름이 없어 자리 차례로 꺼냅니다. 그래서 배열 꼴의 이름은 마음대로 지어도 되지만, 늘어놓은 차례가 바뀌면 붙는 값도 바뀝니다.
Object.entries(menu)는 menu의 키와 값을 한 쌍씩 [키, 값] 배열로 묶고, 그 배열들을 담은 차례대로 모아 새 배열로 돌려줍니다. 칸을 하나씩 꺼내 배열 꼴로 풀면 키와 값에 한 번에 이름이 붙습니다.
const menu = {
커피: 4000,
라테: 4500,
};
const pairs =
Object.entries(menu);
console.log(pairs[0]);
for (const pair of pairs) {
const [name, price] = pair;
console.log(name, price);
}
[ '커피', 4000 ]
커피 4000
라테 45005~6번 줄은 한 문장입니다. pairs의 칸마다 [키, 값] 배열이 하나씩 있어, 첫 칸 pairs[0]이 [ '커피', 4000 ]입니다. 8번 줄이 바퀴마다 그 배열에 pair를 붙이고, 9번 줄이 0번 칸의 키에 name을, 1번 칸의 값에 price를 붙입니다.
객체 돌기의 for...in처럼 키와 값을 함께 찍지만, 값을 menu[name]으로 다시 꺼내지 않습니다.
3두 값 맞바꾸기
그림 뒤 장면의 코드입니다.
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b);
2 13번 줄도 =라 오른쪽을 먼저 계산합니다(규칙 2). [b, a]는 그때의 값으로 새 배열 [2, 1]을 만들고, 그다음 왼쪽 [a, b]가 그 배열을 자리 차례로 풀어 a는 2에, b는 1에 붙입니다. 붙이기 전에 오른쪽이 다 정해져 있어 두 이름이 맞바뀝니다.
3번 줄 앞에는 let을 쓰지 않습니다. 이름을 새로 만들지 않고 있는 a와 b를 다시 붙이기 때문입니다. 1, 2번 줄을 const로 만들면 3번 줄이 다시 붙이기에 막혀 TypeError로 멈춥니다(규칙 3).
대입 두 줄로 맞바꾸면 어떻게 될까요? 무엇을 찍을지 답을 정한 뒤 펼쳐 보세요.
let a = 1;
let b = 2;
a = b;
b = a;
console.log(a, b);
실행 결과 보기
2 2예상과 다르면 그곳이 배울 곳입니다. 3번 줄이 a를 2에 다시 붙이는 순간 a와 1의 연결은 끊깁니다(규칙 3). 4번 줄의 a는 이미 2를 가리켜 b도 2에 붙습니다. 대괄호 꼴은 오른쪽 [b, a]를 먼저 다 계산해 두므로 이렇게 되지 않습니다.
예제
매개변수 자리에서 꺼내기
함수가 메뉴 객체를 받아 이름과 가격을 찍습니다. 매개변수 자리에 구조 분해 꼴을 씁니다.
function show({ name, price }) {
console.log(name, price);
}
const item = {
name: '라테',
price: 4500,
};
show(item);
라테 45008번 줄이 item을 넘기면, show의 새 칸에서 const { name, price } = item;을 한 것처럼 name과 price가 생깁니다(규칙 5). 함수 안에서는 item.name처럼 꺼낼 필요 없이 두 이름을 바로 씁니다.
함수 머리만 보고도 넘긴 객체의 어느 속성을 쓰는지 알 수 있습니다. 넘긴 객체에 price가 없으면 1절의 size처럼 price가 undefined에 붙습니다.
자주 틀리는 곳
틀린 생각: 객체에 없는 이름을 꺼내면 오류로 멈춘다.
없는 이름을 쓰면 ReferenceError로 멈추니, 구조 분해로 없는 속성을 꺼내도 그럴 것 같습니다.
맞는 규칙: 없는 속성을 꺼내면 그 이름이 undefined에 붙고 지나갑니다(1절의 size). 멈추는 것은 꺼낼 객체가 undefined나 null일 때입니다(1절의 order).
틀린 생각: 객체 꼴의 왼쪽 이름은 마음대로 지어도 된다.
배열 꼴은 이름을 마음대로 지어도 자리 차례로 붙으니(2절), 객체 꼴도 그럴 것 같습니다.
const item = {
name: '커피',
price: 4000,
};
const { menuName } = item;
console.log(menuName);
undefined5번 줄은 menuName이라는 속성을 찾습니다. item에 그런 속성이 없어 menuName이 undefined에 붙었습니다.
맞는 규칙: 객체 꼴은 속성 이름으로 찾습니다. 다른 이름을 붙이려면 콜론 왼쪽에 꺼낼 속성을, 오른쪽에 붙일 이름을 써서 { name: menuName }이라 씁니다. 그러면 name 속성을 꺼내 menuName이라는 이름을 붙입니다. 생기는 이름은 menuName 하나이고, 이름 name은 생기지 않습니다.
틀린 생각: 배열 꼴도 이름이 같으면 그 값이 붙는다.
객체 꼴은 이름으로 찾으니(1절), 배열 꼴도 이름에 맞는 값을 찾아 붙일 것 같습니다.
const pair = ['커피', 4000];
const [price, name] = pair;
console.log(price);
console.log(name);
커피
4000price는 '커피'에, name은 4000에 붙었습니다. 컴퓨터는 이름의 뜻을 보지 않고 자리 차례로만 붙입니다(규칙 6).
맞는 규칙: 배열 꼴은 자리 차례로 꺼냅니다. 0번 칸이 이름이고 1번 칸이 가격이면 const [name, price] = pair;처럼 칸 차례대로 늘어놓습니다.
확인 문제
1. 무엇이 찍힐까요? 한 줄을 글자 그대로 쓰세요.
let a = '커피';
let b = '라테';
let c = '쿠키';
[a, b, c] = [b, c, a];
console.log(a, b, c);
힌트
4번 줄 오른쪽의 새 배열에 어떤 값이 어떤 차례로 들어가는지 먼저 적어 봅니다.
정답
라테 쿠키 커피4번 줄은 오른쪽을 먼저 계산해 새 배열 ['라테', '쿠키', '커피']를 만든 뒤, 자리 차례로 풀어 a는 '라테'에, b는 '쿠키'에, c는 '커피'에 붙입니다.
흔한 오답은 라테 쿠키 라테입니다. 왼쪽부터 한 이름씩 붙인다고 본 것입니다. 그렇게 붙이면 c를 붙일 때 a가 이미 '라테'라서 c도 '라테'가 됩니다. 붙이기 전에 오른쪽이 다 정해집니다(규칙 2).
2. 창고의 재고를 우유 3, 원두 5 두 줄로 찍으려 합니다. 빈칸을 채우세요.
const stock = {
우유: 3,
원두: 5,
};
const pairs =
Object.entries(stock);
for (const pair of pairs) {
const ________ = pair;
console.log(name, count);
}
힌트
pair가 붙는 것은 ['우유', 3] 같은 배열입니다. 9번 줄이 쓰는 두 이름을 어떤 차례로 늘어놓을지 봅니다.
정답
[name, count]입니다.
const stock = {
우유: 3,
원두: 5,
};
const pairs =
Object.entries(stock);
for (const pair of pairs) {
const [name, count] = pair;
console.log(name, count);
}우유 3
원두 5pair는 [키, 값] 배열이라 대괄호 꼴로 풀어, 0번 칸의 키에 name을, 1번 칸의 값에 count를 붙입니다.
흔한 오답은 { name, count }입니다. 배열에서 name과 count라는 속성을 찾게 되어, 줄마다 undefined undefined가 찍힙니다. [count, name]으로 썼다면 자리 차례가 바뀌어 3 우유처럼 찍힙니다.
3. 책 한 권의 정보입니다. 무엇이 찍힐까요? 보기에서 고르세요.
const book = {
title: '어린 왕자',
page: 120,
};
const { title: name } = book;
console.log(name);
console.log(title);
- ① 6번 줄에서
ReferenceError로 멈춘다 - ②
어린 왕자를 찍고 7번 줄에서ReferenceError로 멈춘다 - ③
어린 왕자를 두 번 찍는다
힌트
5번 줄 콜론의 왼쪽과 오른쪽 가운데 어느 쪽이 새로 생기는 이름인지 봅니다.
정답
어린 왕자main.js:7console.log(title); ^ ReferenceError: title is not defined at main.js:7:13②입니다. 5번 줄은 title 속성을 꺼내 name이라는 이름을 붙입니다. 생기는 이름은 name 하나라서, 6번 줄은 '어린 왕자'를 찍고 7번 줄은 없는 이름 title에서 멈춥니다.
- ① 콜론 왼쪽이 붙일 이름이고 오른쪽이 꺼낼 속성이라고 거꾸로 봤습니다. 그렇게 읽으면 이름 name이 생기지 않아 6번 줄에서 멈춥니다.
- ③ 속성 이름 title도 이름으로 생긴다고 봤습니다.
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 첫 코드를 main.js에 저장해 돌린 뒤, 5번 줄과 7번 줄의 name을 둘 다 nmae로 바꿉니다. item에 없는 이름을 꺼내는 것입니다. 첫 줄에 무엇이 찍힐지 먼저 적고, 바꿔 돌려서 맞춰 봅니다. undefined 4000이 아니면 1절의 6번 줄 설명을 다시 봅니다.
저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.