[모던 자바스크립트 튜토리얼] 5.10 구조 분해 할당

개발견 배도르만·2023년 4월 9일
0
post-thumbnail

구조 분해 할당

구조분해할당이란?
배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 JavaScript 표현식

반복을 줄이고 코드 가독성이 높아지는 등의 이점이 있다.

배열 분해하기

// 이름과 성을 요소로 가진 배열
let arr = ["Bora", "Lee"]

// 구조 분해 할당을 이용해
// firstName엔 arr[0]을
// surname엔 arr[1]을 할당하였습니다.
let [firstName, surname] = arr;

alert(firstName); // Bora
alert(surname);  // Lee

인덱스를 이용하지 않고도 변수에 이름과 성을 할당했다.
split 등과 같은 반환 값이 배열인 메서드를 활용할 수도 있다.

let [firstName, surname] = "Bora Lee".split(' ');

쉼표와 공백을 사용하여 필요하지 않은 요소를 버릴 수 있다.

// 두 번째 요소는 필요하지 않음
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert( title ); // Consul

할당 연산자 우측엔 모든 이터러블이 올 수 있다.

let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);

할당연산자 좌측엔 할당할 수 있는 것이라면 무엇이든 올 수 있다.

let user = {};
[user.name, user.surname] = "Bora Lee".split(' ');

alert(user.name); // Bora

.entries()로 구조분해할당 반복하기(Map에서도 사용 가능)

let user = {
  name: "John",
  age: 30
};

// 객체의 키와 값 순회하기
for (let [key, value] of Object.entries(user)) {
  alert(`${key}:${value}`); // name:John, age:30이 차례대로 출력
}

변수 교환 트릭

let guest = "Jane";
let admin = "Pete";

// 변수 guest엔 Pete, 변수 admin엔 Jane이 저장되도록 값을 교환함
[guest, admin] = [admin, guest];

alert(`${guest} ${admin}`); // Pete Jane(값 교환이 성공적으로 이뤄졌습니다!)

'...'으로 나머지 요소 가져오기

배열 앞쪽에 위치한 값 몇 개만 필요하고 이후 나머지 값들은 한 군데에 모아서 저장하는 경우

let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert(name1); // Julius
alert(name2); // Caesar

// `rest`는 배열입니다.
// 변수명은 rest가 아니어도 됨
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2

변수 앞의 점 세 개(...)와 변수가 가장 마지막에 위치해야 한다

기본값

할당하고자 하는 변수의 개수가 분해 대상인 배열의 길이보다 커도 에러가 발생하지 않는다. 값이 없으면 undefined로 취급된다.

let [firstName, surname] = [];

alert(firstName); // undefined
alert(surname); // undefined

=을 이용하면 할당할 값이 없을 때 기본으로 할당해 줄 값인 '기본값(default value)'을 설정할 수 있다.

// 기본값
let [name = "Guest", surname = "Anonymous"] = ["Julius"];

alert(name);    // Julius (배열에서 받아온 값)
alert(surname); // Anonymous (기본값)

표현식이나 함수 호출도 기본값이 될 수 있다.

// name의 prompt만 실행됨
let [surname = prompt('성을 입력하세요.'), name = prompt('이름을 입력하세요.')] = ["김"];

alert(surname); // 김 (배열에서 받아온 값)
alert(name);    // prompt에서 받아온 값

객체 분해하기

배열이 아닌 객체도 분해할 수 있다.

기본 문법:

let {var1, var2} = {var1:…, var2:…}

할당 연산자 우측엔 분해 대상 객체를, 좌측엔 상응하는 객체 프로퍼티의 '패턴'을 넣는다.

키 목록을 패턴으로 사용하는 예시:

let options = {
  title: "Menu",
  width: 100,
  height: 200
};

let {title, width, height} = options;

alert(title);  // Menu
alert(width);  // 100
alert(height); // 200

순서가 바뀌어도 키-값 쌍에 따라 동작한다.

할당연산자 좌측엔 좀 더 복잡한 패턴이 올 수도 있다.

':'을 사용한 객체 프로퍼티를 프로퍼티 키와 다른 이름을 가진 변수에 저장하는 예시:

let options = {
  title: "Menu",
  width: 100,
  height: 200
};

// { 객체 프로퍼티: 목표 변수 }
let {width: w, height: h, title} = options;

// width -> w
// height -> h
// title -> title

alert(title);  // Menu
alert(w);      // 100
alert(h);      // 200

콜론은 '분해하려는 객체의 프로퍼티 : 목표 변수'의 형태로 사용한다.

객체도 프로퍼티가 없는 경우를 대비하여 기본값 설정이 가능하다. 마찬가지로 기본값은 표현식이나 함수가 될 수도 있다.
기본값 설정 예시:

let options = {
  title: "Menu"
};

let {width = 100, height = 200, title} = options;

alert(title);  // Menu
alert(width);  // 100
alert(height); // 200

콜론과 할당 연산자를 동시에 사용할 수도 있다.

let options = {
  title: "Menu"
};

let {width: w = 100, height: h = 200, title} = options;

alert(title);  // Menu
alert(w);      // 100
alert(h);      // 200

원하는 정보만 선택해서 저장하는 것도 가능하다.

let options = {
  title: "Menu",
  width: 100,
  height: 200
};

// title만 변수로 뽑아내기
let { title } = options;

alert(title); // Menu

나머지 패턴 '...'

분해 대상 객체의 프로퍼티 개수가 할당하려는 변수의 개수보다 많은 경우 배열에서의 나머지 패턴(rest pattern)을 사용여 나머지 프로퍼티를 별도로 저장할 수 있다.

let options = {
  title: "Menu",
  height: 200,
  width: 100
};

// title = 이름이 title인 프로퍼티
// rest = 나머지 프로퍼티들
let {title, ...rest} = options;

// title엔 "Menu", rest엔 {height: 200, width: 100}이 할당됩니다.
alert(rest.height);  // 200
alert(rest.width);   // 100

let 없이 사용하기

let으로 새로운 변수를 선언하지 않고 기존의 변수에 할당하는 경우
일반적으로 아래와 같이 코드를 작성하면 될 것 같지만 에러가 발생한다.

let title, width, height;

// SyntaxError: Unexpected token '=' 이라는 에러가 아랫줄에서 발생합니다.
{title, width, height} = {title: "Menu", width: 200, height: 100};

중괄호{}의 용도는 코드블럭으로 인식하여 에러가 발생한 것이다.
자바스크립트는 표현식 안에 있지 않으면서 주요 코드 흐름 상에 있는 {...}를 코드 블록으로 인식한다. 코드 블록의 본래 용도는 아래와 같이 문(statement)을 묶는 것이다.

에러를 방지하기 위해 소괄호로 감싸 중괄호를 코드블럭이 아닌 표현식으로 해석하게 하면 된다.

let title, width, height;

// 에러가 발생하지 않습니다.
({title, width, height} = {title: "Menu", width: 200, height: 100});

alert( title ); // Menu

중첩 구조 분해(nested destructuring)

객체나 배열이 다른 객체나 배열을 포함하는 경우, 좀 더 복잡한 패턴을 사용하면 중첩 배열이나 객체의 정보를 추출하는 것.

아래 예시에서 options의 size 프로퍼티 값은 또 다른 객체이다. items 프로퍼티는 배열을 값으로 가지고 있다. 대입 연산자 좌측의 패턴은 정보를 추출하려는 객체 options와 같은 구조를 갖추고 있다.

let options = {
  size: {
    width: 100,
    height: 200
  },
  items: ["Cake", "Donut"],
  extra: true
};

// 코드를 여러 줄에 걸쳐 작성해 의도하는 바를 명확히 드러냄
let {
  size: { // size는 여기,
    width,
    height
  },
  items: [item1, item2], // items는 여기에 할당함
  title = "Menu" // 분해하려는 객체에 title 프로퍼티가 없으므로 기본값을 사용함
} = options;

alert(title);  // Menu
alert(width);  // 100
alert(height); // 200
alert(item1);  // Cake
alert(item2);  // Donut

extra(할당 연산자 좌측의 패턴에는 없음)를 제외한 options 객체의 모든 프로퍼티가 상응하는 변수에 할당되었다.

위 예시에서는 size와 items 전용 변수는 없다. 대신 size와 items 안의 정보를 변수의 할당했다.
할당 연산자 좌측의 패턴에서 size:{...}으로 작성하지 않고 size만 작성했으면 분해 대상 객체의 size를 변수 size에 저장했을 것이다.

똑똑한 함수 매개변수

함수 사용 시 매개변수의 상당수는 선택적으로 사용된다.

메뉴 생성 함수 예시:

function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
  // ...
}

이렇게 함수를 작성하면 넘겨주는 인수의 순서가 틀려 문제가 발생할 수 있기 때문에 undefined 등을 인자로 넘겨 가독성이 떨어지게 되는 문제가 있다.

// 기본값을 사용해도 괜찮은 경우 아래와 같이 undefined를 여러 개 넘겨줘야 합니다.
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])

이럴 때 구조 분해를 사용할 수 있다.

매개변수 모두를 객체에 모아 함수에 전달해, 함수가 전달받은 객체를 분해하여 변수에 할당하고 원하는 작업을 수행할 수 있도록 함수를 리팩토링해 보자.

// 함수에 전달할 객체
let options = {
  title: "My menu",
  items: ["Item1", "Item2"]
};

// 똑똑한 함수는 전달받은 객체를 분해해 변수에 즉시 할당함
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
  // title, items – 객체 options에서 가져옴
  // width, height – 기본값
  alert( `${title} ${width} ${height}` ); // My Menu 200 100
  alert( items ); // Item1, Item2
}

showMenu(options);

즉, 할당 시 뿐만 아니라 함수에서 파라미터를 받을 때에도 구조 분해가 적용될 수 있는 것이다.

중첩 객체와 콜론을 조합하여 좀 더 복잡한 구조 분해도 가능하다.

let options = {
  title: "My menu",
  items: ["Item1", "Item2"]
};

function showMenu({
  title = "Untitled",
  width: w = 100,  // width는 w에,
  height: h = 200, // height는 h에,
  items: [item1, item2] // items의 첫 번째 요소는 item1에, 두 번째 요소는 item2에 할당함
}) {
  alert( `${title} ${w} ${h}` ); // My Menu 100 200
  alert( item1 ); // Item1
  alert( item2 ); // Item2
}

showMenu(options);

기본 문법은 다음과 같다.

function({
  incomingProperty: varName = defaultValue
  ...
})

이렇게 함수 매개변수를 구조 분해할 땐, 반드시 인수가 전달된다고 가정되고 사용된다. 따라서 모든 인수에 기본값 할당 시 빈 객체를 명시적으로 전달해야 한다.

✍️ 정리

  • 구조 분해 할당을 사용하면 객체나 배열을 변수로 연결할 수 있다.
  • 객체 분해하기:
let {prop : varName = default, ...rest} = object

//object의 프로퍼티 prop의 값은 변수 varName에 할당됨
//object에 prop이 없으면 default가 varName에 할당됨

//연결할 변수가 없는 나머지 프로퍼티들은 객체 rest에 복사됨
  • 배열 분해하기:
let [item1 = default, item2, ...rest] = array
//array의 첫 번째 요소는 item1에 할당됨
//두 번째 요소는 변수 item2에 할당됨

//이어지는 나머지 요소들은 배열 rest 저장됨
  • 할당 연산자 좌측의 패턴과 우측의 구조가 같으면 중첩 배열이나 객체가 있는 복잡한 구조에서도 원하는 데이터를 뽑아낼 수 있다.
profile
네 발 개발 개

0개의 댓글