본문 바로가기

JavaScript

[ES6 JavaScript] 구조 분해 할당(Destructuring assignment)

 오늘 공부해볼 ES6 JavaScript는 구조 분해 할당입니다. ES6에서 정말 많이 쓰는 문법 중 하나입니다. 구조 분해 할당은 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 합니다.

 

Pixabay로부터 입수된 PIRO4D님의 이미지 입니다.  

배열 구조 분해

var foo = ["one", "two", "three"];

var [one, two, three] = foo;
console.log(one); 	// "one"
console.log(two);	// "two"
console.log(three); 	// "three"

 가장 기본적인 구조 분해 할당입니다. foo 배열은 "one", "two", "three" 세개의 문자열 요소를 가지고 있습니다. 각각의 문자열 요소를 one, two, three라는 이름의 변수에 구조 분해할당하고 있습니다. 배열의 순서대로 one, two, three 변수에 할당되고 이후부터 해당 변수들을 사용할 수 있습니다.

선언에서 분리한 할당

var a, b;

[a, b] = [1, 2];
console.log(a); // 1
console.log(b); // 2

 위와 같이 선언부와 구조 분해 할당 구문을 분리할 수도 있습니다.

기본 값

var a, b;

[a, b] = [1];
console.log(a); 	// 1
console.log(b); 	// undefined

[a=5, b=7] = [1];
console.log(a); 	// 1
console.log(b); 	// 7

 분해한 값이 undefined일 경우, 기본 값을 대신 사용하도록 할 수 있습니다. 위 코드에서 b의 분해된 값은 undefined입니다. 기본값을 세팅하여 7이 대신 세팅된 것을 확인할 수 있습니다.

변수 값 교환하기

 일반적으로 두 변수 값을 교환하려면 아래와 같이 임시 변수가 필요합니다.

var a = 1;
var b = 3;

var temp = a;
a = b;
b = temp;

console.log(a); // 3
console.log(b); // 1

 하지만, 구조 분해 할당을 이용하면 구조 분해 할당 표현식 하나만으로 두 변수 값을 교환할 수 있습니다.

일부 반환 값 무시하기

function f() {
  return [1, 2, 3];
}

var [a, , b] = f();
console.log(a); // 1
console.log(b); // 3

[,,] = f();	// 모든 반환 값 무시하기

 위와 같이 일부 반환 값을 무시할 수 있습니다. 반환값이 있는 자리에는 빈칸으로 놔두면 됩니다.

변수에 배열의 나머지 배열 할당하기

 나머지 구문(...)을 이용해서 분해하고 남은 부분을 하나의 변수에 할당할 수 있습니다.

var [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

 위 코드를 보면 1이 a에 할당되고 나머지 요소들은 b에 할당되는 것을 확인할 수 있습니다. 할당되는 형태는 배열입니다. 만약에 코드 하단 부분과 같이 나머지 구문 뒤에 쉼표가 있다면, 문법 오류를 발생시킵니다.

정규 표현식과 일치하는 값 해체하기

function parseProtocol(url) { 
  var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
  if (!parsedURL) {
    return false;
  }
  console.log(parsedURL); // ["https://developer.mozilla.org/en-US/Web/JavaScript", "https", "developer.mozilla.org", "en-US/Web/JavaScript"]

  var [, protocol, fullhost, fullpath] = parsedURL;
  return protocol;
}

console.log(parseProtocol('https://developer.mozilla.org/en-US/Web/JavaScript')); // "https"

 정규 표현식의 exec() 메서드는 일치하는 부분을 배열로 반환합니다. 이를 이용하여 구조 분해 할당하여 필요한 값을 변수에 담을 수 있습니다.

 

객체 구조 분해 할당

 배열은 순서가 있기 때문에 해당 요소를 순서대로 구조 분해할 수 있었습니다. 하지만, 객체는 순서가 없고 대신 각 요소의 이름을 가지고 있습니다. 이를 통해서 객체 구조 분해 할당이 가능합니다.

var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true

 가장 기본적인 객체 구조 분해 할당 예제입니다. o라는 이름의 객체를 만들었는데, 속성을 두 개 가지고 있습니다. 하나는 p라는 이름과 값 42를 가지고 있고, 하나는 q라는 이름과 값 true를 가지고 있습니다. 이를 객체 구조 분해 할당할 때는 속성의 이름을 이용합니다. p는 p라는 이름에 q는 q라는 이름의 변수에 할당됩니다. 구조 분해 할당을 사용하기 전이라면 반드시 o.p, o.q로 사용했을 텐데, 단순하게 p, q라는 이름의 변수로 사용 가능합니다.

선언 없는 할당

var a, b;

({a, b} = {a: 1, b: 2});

 객체의 선언 없는 할당은 조금 유의해야할 점이 있습니다. 반드시 처음과 끝에 괄호()를 넣어줘야합니다. 그렇지 않으면 좌변이 객체 리터럴이 아닌 블록 범위로 간주되기 때문입니다.

새로운 변수 이름 할당하기

 객체 구조 할당을 공부하다보니 그런 생각이 듭니다. 그렇다면 객체 구조 할당은 속성의 이름과 동일한 이름의 변수로만 할당이 가능할까? 다행히도 그렇지는 않습니다.

var o = {p: 42, q: true};
var {p: foo, q: bar} = o;

console.log(foo); 	// 42
console.log(bar); 	// true
console.log(p); 	// not defined
console.log(q); 	// not defined

 위 예제는 객체 o를 구조 분해합니다. 객체에는 p라는 이름의 속성과 q라는 이름의 속성이 있는데, 이를 해제하며 p는 foo라는 이름을 q는 bar라는 이름을 주었습니다. 당연히 p와 q변수는 아예 정의되지 않는 상태입니다.

기본값

 객체 구조 분해 할당도 아래와 같이 undefined일 경우, 기본값을 할당할 수 있습니다.

var {a = 10, b = 5} = {a: 3};

console.log(a); // 3
console.log(b); // 5

기본값을 갖는 새로운 이름의 변수에 할당하기

var {a: aa = 10, b: bb = 5} = {a: 3};

console.log(aa); // 3
console.log(bb); // 5

 기본값도 갖으면서 새로운 이름에 할당하길 원한다면 위와 같이 사용하면 됩니다. 먼저 :과 새로운 변수이름을 정의하고 그 다음에 기본값을 세팅해주면 됩니다.

함수 매개변수의 기본값 설정하기

 객체 구조 분해 할당을 이용하여 객체 형태의 매개변수의 기본값을 설정할 수 있습니다.

function drawES2015Chart({size = 'big', cords = { x: 0, y: 0 }, radius = 25} = {}) {
  console.log(size, cords, radius);
  // 차트 그리기 수행
}

drawES2015Chart({
  cords: { x: 18, y: 30 },
  radius: 30
});

 조금 특이한 구문을 찾아보자면 매개변수 부분 가장 오른쪽에 빈 오브젝트를 할당하는 부분이 왜 들어갈까 생각할 수 있습니다. 그 이유는 함수 호출시 하나의 인자도 제공되지 않을 경우에 사용하기 위해서 넣은 것입니다. 만약 적어도 하나 이상의 인자를 제공한다면 굳이 넣을 필요가 없습니다.

중첩된 객체 및 배열의 구조 분해

var metadata = {
    title: "Scratchpad",
    translations: [
       {
        locale: "de",
        localization_tags: [ ],
        last_edit: "2014-04-14T08:43:37",
        url: "/de/docs/Tools/Scratchpad",
        title: "JavaScript-Umgebung"
       }
    ],
    url: "/en-US/docs/Tools/Scratchpad"
};

var { title: englishTitle, translations: [{ title: localeTitle }] } = metadata;

console.log(englishTitle); // "Scratchpad"
console.log(localeTitle);  // "JavaScript-Umgebung"

 matadata 객체를 구조 분해합니다. 먼저 title을 분해하여 englishTitle이라는 변수로 할당했습니다. 그리고 translations 배열의 첫번째 요소의 객체의 title을 구조 분해하여 localeTitle이란 변수로 할당했습니다.

for of 반복문과 구조 분해

 만약에 배열의 요소들을 반복하면서 분해하려면 어떻게 해야할까요?

var people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith"
    },
    age: 35
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones"
    },
    age: 25
  }
];

for (var {name: n, family: { father: f } } of people) {
  console.log("Name: " + n + ", Father: " + f);
}

// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"

 for of 구문을 이용합니다. people을 반복하며 name과 family의 father 요소를 구조 분해합니다.

함수 매개변수로 전달된 객체에서 필드 해체하기

 함수 매개변수로 전달된 객체에서 필드를 해체합니다.

function userId({id}) {
  return id;
}

function whois({displayName: displayName, fullName: {firstName: name}}){
  console.log(displayName + " is " + name);
}

var user = {
  id: 42,
  displayName: "jdoe",
  fullName: {
      firstName: "John",
      lastName: "Doe"
  }
};

console.log("userId: " + userId(user)); // "userId: 42"
whois(user); // "jdoe is John"

 인자가 객체나 배열이라면 당연히 매개변수를 구조 분해 할 수 있습니다. 위 예제에서는 user객체로부터 id, displayName그리고 fullName의 firstName을 구조 분해했습니다.

계산된 속성 이름과 구조 분해

let key = "z";
let { [key]: foo } = { z: "bar" };

console.log(foo); // "bar

 변수를 이용하여 구조 분해도 가능합니다. 위 예제에서는 key라는 변수의 값인 "z"를 이용하여 구조 분해했습니다.

객체 구조 분해에서 나머지 구문

let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}

console.log(a);		// 10
console.log(b);		// 20
console.log(rest);	// { c: 30, d: 40 }

 객체 구조 분해에서 나머지 구문은 할당되고 남은 나머지 항목들을 모아 제공합니다. 위 예제에서는 a, b는 구조 분해 할당되었고 나머지인 c, d가 rest 객체에 담긴것을 확인할 수 있습니다.

속성 이름이 유효한 JavaScript 식별자명이 아닌 경우

const foo = { 'fizz-buzz': true };
const { 'fizz-buzz': fizzBuzz } = foo;

console.log(fizzBuzz); // "true"

 위 예제에서 fizz-buzz는 변수명으로 사용할 수 없습니다. 이럴 때에는 구조 분해 할당할 때 반드시 유효한 변수명을 제공해서 대체해줘야합니다.

 

 

 

참고:

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

 

구조 분해 할당

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

developer.mozilla.org