본문으로 건너뛰기

구조 분해로 꺼내기

자바스크립트 목차객체레슨 46 / 76

목차

구조 분해(destructuring)는 객체나 배열에서 값을 꺼내 한 번에 이름을 붙이는 꼴입니다. const { name, price } = item;⁠은 item에서 같은 이름의 속성을 꺼내고, const [first, second] = pair;⁠는 배열의 칸을 자리 차례로 꺼냅니다.

속성 여럿의 값에 이름을 붙이려면 const name = item.name;⁠처럼 속성마다 한 줄씩 써야 합니다. 이름과 키를 두 번씩 쓰고, 꺼낼 속성이 늘면 같은 꼴의 줄도 늡니다. 이 레슨에서는 객체와 배열에서 한 줄로 꺼내는 두 꼴과 두 값을 맞바꾸는 꼴을 보고, 객체에 없는 이름을 꺼내면 그 이름이 무엇에 붙는지 그림에서 먼저 맞혀 봅니다.

10단계 중 1단계. 메뉴 객체에서 속성을 꺼내 이름을 붙이는 코드입니다.

1객체에서 같은 이름의 속성 꺼내기

그림 앞 장면의 코드에 찍는 줄 둘을 더했습니다.

const item = {
  name: '커피',
  price: 4000,
};
const { name, price } = item;
const { size } = item;
console.log(name, price);
console.log(size);
실행 결과
커피 4000
undefined

5번 줄 왼쪽의 중괄호는 객체를 만들지 않습니다. 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);
실행 결과
라테
4500

2번 줄은 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
라테 4500

5~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 1

3번 줄도 =라 오른쪽을 먼저 계산합니다(규칙 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);
실행 결과
라테 4500

8번 줄이 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);
실행 결과
undefined

5번 줄은 menuName이라는 속성을 찾습니다. item에 그런 속성이 없어 menuName이 undefined에 붙었습니다.

맞는 규칙: 객체 꼴은 속성 이름으로 찾습니다. 다른 이름을 붙이려면 콜론 왼쪽에 꺼낼 속성을, 오른쪽에 붙일 이름을 써서 { name: menuName }이라 씁니다. 그러면 name 속성을 꺼내 menuName이라는 이름을 붙입니다. 생기는 이름은 menuName 하나이고, 이름 name은 생기지 않습니다.

틀린 생각: 배열 꼴도 이름이 같으면 그 값이 붙는다.

객체 꼴은 이름으로 찾으니(1절), 배열 꼴도 이름에 맞는 값을 찾아 붙일 것 같습니다.

const pair = ['커피', 4000];
const [price, name] = pair;
console.log(price);
console.log(name);
실행 결과
커피
4000

price는 '커피'에, 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
원두 5

pair는 [키, 값] 배열이라 대괄호 꼴로 풀어, 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번 줄 설명을 다시 봅니다. 저장하고 돌리는 법은 내 컴퓨터에서 돌려 보기에 있습니다.

끝까지 내려오면 저절로 표시됩니다. 이 기기에만 저장합니다.

자바스크립트 목차로