Menu

자바스크립트 구조 분해 할당 완벽 정리

자바스크립트 구조 분해 할당(destructuring)으로 객체와 배열에서 값을 꺼내는 법, 이름 바꾸기, 기본값, 중첩 패턴, 함수 매개변수까지 실전 예제로 정리했습니다.

구조 분해 할당은 "모양"을 기준으로 한 패턴 매칭이다

자바스크립트의 구조 분해 할당(destructuring)은 객체나 배열의 생김새를 그대로 본뜬 패턴을 작성해서, 원하는 값을 이름이나 위치 기준으로 한 줄에 꺼내오는 문법이다. 프로퍼티를 하나씩 점 찍어가며 파고들 필요 없이, 필요한 걸 적어두면 자바스크립트가 알아서 꺼내 건네준다.

크게 두 가지 형태가 있다:

index.js
Output
Click Run to see the output here.

중괄호는 객체를 프로퍼티 이름으로 매칭하고, 대괄호는 배열을 순서(인덱스)로 매칭합니다. 두 경우 모두 왼쪽에 있는 변수들은 새로 선언되는 변수 예요. 값에 직접 접근하는 게 아니라, 값에서 필요한 부분을 꺼내오는 패턴을 묘사하는 것이라고 보시면 됩니다.

구조 분해 할당이 없던 시절에는 같은 코드를 이렇게 작성했습니다.

const name = user.name;
const age = user.age;
const first = scores[0];
const second = scores[1];

세상이 무너질 일은 아니지만, 함수마다 매번 이런 식으로 쓰고 있으면 슬슬 지겨워진다.

객체 구조 분해 할당은 프로퍼티 이름을 기준으로 한다

객체를 구조 분해할 때는 중괄호 안에 적는 이름이 원본 객체의 프로퍼티 이름과 정확히 일치해야 한다. 순서는 상관없다. 자바스크립트는 키를 기준으로 값을 찾아오기 때문이다.

index.js
Output
Click Run to see the output here.

객체에 해당 프로퍼티가 없으면 변수는 그냥 undefined가 되고, 에러는 발생하지 않습니다:

index.js
Output
Click Run to see the output here.

프로퍼티 이름에 오타가 나면 에러도 없이 조용히 undefined가 들어와 있는데, 새벽 2시에 버그 잡다가 이거 때문에 머리 싸매게 되니까 꼭 기억해 두자.

변수 이름 바꾸기 (Rename)

객체의 프로퍼티 이름을 그대로 변수명으로 쓰고 싶지 않을 때가 있다. 이미 같은 이름이 스코프에 있어서 충돌이 나거나, API가 마음에 안 드는 네이밍 컨벤션을 쓰고 있을 때가 대표적이다. 이럴 때는 원래이름: 새이름 형태로 구조 분해 할당 이름 변경을 할 수 있다:

index.js
Output
Click Run to see the output here.

읽을 때는 "id 속성을 꺼내서 userId라는 이름으로 쓰겠다" 정도로 생각하면 됩니다. 콜론 앞에 원래 속성 이름이 오고, 뒤에 새로 붙일 변수 이름이 옵니다. 다른 언어의 타입 표기처럼 보이지만 전혀 상관없습니다.

구조 분해 기본값 설정하기

속성이 없을 수도 있다면, 패턴 안에서 바로 기본값을 지정할 수 있습니다:

index.js
Output
Click Run to see the output here.

기본값은 값이 undefined일 때만 적용된다는 점을 꼭 기억하세요. null, 0, ""은 그대로 전달되고 기본값으로 대체되지 않습니다.

index.js
Output
Click Run to see the output here.

이 부분에서 많이들 헷갈립니다. null일 때도 기본값이 적용되게 하려면 명시적으로 체크하거나, 구조 분해 이후에 nullish 병합 연산자 ??를 쓰면 됩니다.

이름 변경과 기본값은 함께 쓸 수도 있습니다:

index.js
Output
Click Run to see the output here.

배열 구조 분해 할당은 순서로 매칭된다

배열에는 이름이 없고 인덱스만 있기 때문에, 배열 구조 분해 할당은 값의 순서대로 매칭됩니다. 건너뛰고 싶은 항목이 있다면 쉼표만 찍어두면 됩니다.

index.js
Output
Click Run to see the output here.

앞쪽에 쉼표 두 개를 찍은 건 "인덱스 0과 1은 건너뛰어라"는 뜻입니다. 보기에는 좀 빽빽하지만, 배열 중간에 있는 값 하나만 꺼내 쓰고 싶을 때 꽤 유용합니다.

배열 구조 분해 할당이 진가를 발휘하는 순간은, 튜플 형태로 값을 돌려주는 함수를 쓸 때입니다:

index.js
Output
Click Run to see the output here.

React 훅을 써봤다면 const [count, setCount] = useState(0) 같은 패턴이 눈에 익을 겁니다. 같은 문법이 여기저기서 계속 등장하죠.

Rest 패턴: 남은 값 한꺼번에 모으기

구조 분해 할당 안에서 ...name을 쓰면, 이름이나 위치로 매칭되지 않은 나머지 값들을 새 변수 하나로 모을 수 있습니다.

배열에서는 rest가 뒤쪽 요소들을 한 번에 가져옵니다:

index.js
Output
Click Run to see the output here.

객체에서 rest를 쓰면, 앞에서 이름으로 꺼내지 않은 나머지 프로퍼티들을 전부 모아줍니다:

index.js
Output
Click Run to see the output here.

객체를 변형하지 않고 특정 필드 하나만 떼어낼 때 흔히 쓰는 관용구입니다. rest에는 id를 제외한 나머지 프로퍼티가 담긴 새로운 객체가 들어갑니다.

rest는 패턴의 마지막 자리에만 올 수 있습니다. const [...init, last]처럼 쓰면 문법 에러가 납니다.

중첩 구조 분해 할당

구조 분해 패턴은 중첩이 가능합니다. 어떤 프로퍼티 값이 또 다른 객체나 배열이라면, 한 번의 표현식 안에서 그 안쪽까지 한꺼번에 분해할 수 있습니다:

index.js
Output
Click Run to see the output here.

강력하긴 하지만, 남용은 금물입니다. 중괄호가 세 단계쯤 중첩되면 코드가 퍼즐처럼 보이기 시작하죠. 그럴 땐 중간값을 별도 변수로 빼주세요. 똑똑해 보이는 것보다 읽기 쉬운 코드가 낫습니다.

한 가지 주의할 점: 패턴에서 data: { user }라고 써도 data라는 변수가 만들어지지 않습니다. 이건 "data 안으로 들어가서 구조 분해를 계속하라"는 지시일 뿐이에요. data 자체가 필요하다면 따로 적어줘야 합니다:

index.js
Output
Click Run to see the output here.

함수 매개변수 구조 분해

구조 분해 할당을 가장 많이 쓰는 곳은 단연 함수의 매개변수 자리입니다. "옵션 객체를 넘긴다"는 막연한 패턴이, 함수 시그니처만 봐도 뭘 받는지 한눈에 보이는 형태로 바뀌죠:

index.js
Output
Click Run to see the output here.

함수를 호출하는 쪽에서 보면 평범한 옵션 객체 스타일 호출과 다를 바 없습니다. 반면 함수 안에서는 기본값까지 적용된 이름 있는 변수들을 바로 쓸 수 있죠. options.name || "default" 같은 번거로운 코드를 더는 작성할 필요가 없습니다.

다만 한 가지 함정이 있습니다. 이 함수를 인자 없이 호출하면 에러가 납니다. undefined는 구조 분해 할당의 대상이 될 수 없기 때문이죠. 이럴 땐 매개변수 전체에 기본값을 지정해 주면 됩니다:

index.js
Output
Click Run to see the output here.

패턴 뒤에 붙은 = {}는 "인자가 들어오지 않으면 빈 객체로 간주해라"라는 의미입니다. 그 다음에 안쪽의 기본값이 적용되죠.

값 교환과 재할당하기

구조 분해 할당은 이미 선언된 변수에도 사용할 수 있는데, 이때는 객체 패턴을 괄호로 감싸야 합니다. 그렇지 않으면 자바스크립트가 {를 블록의 시작으로 해석해 버리거든요:

index.js
Output
Click Run to see the output here.

배열 원소 교환(swap)은 정말 깔끔하게 처리되죠. 객체 재할당은 자주 쓸 일은 없지만, 괄호로 감싸는 트릭은 필요할 때를 대비해 기억해 두면 유용합니다.

실전 예제

지금까지 살펴본 내용을 거의 다 활용해서, API 응답을 처리하는 함수를 한번 만들어 보겠습니다.

index.js
Output
Click Run to see the output here.

구조 분해의 각 계층마다 기본값을 지정해 두면, 데이터가 일부 누락되더라도 if 문으로 방어 코드를 잔뜩 깔지 않고도 함수가 무사히 동작합니다. 이게 바로 진짜 이점이죠. 시그니처가 정확하면서도 유연해진다는 것.

다음 주제: 객체 스프레드(Spread)

구조 분해 할당이 객체에서 값을 꺼내는 문법이라면, 스프레드는 반대로 값을 넣는 문법입니다. 복사하고, 합치고, 덮어쓸 때 쓰죠. 모던 자바스크립트에서 이 둘은 늘 짝꿍처럼 함께 등장하는데요, 다음 장에서는 스프레드를 살펴보겠습니다.

자주 묻는 질문

자바스크립트에서 구조 분해 할당(destructuring)이 뭔가요?

객체나 배열에서 값을 꺼내 한 줄에 변수로 바로 묶어주는 문법입니다. const { name } = user; 이렇게 쓰면 user 객체의 name 속성을 꺼내오고, const [first, second] = list; 하면 list의 앞 두 개 요소를 받아옵니다. 한마디로 = 왼쪽에 오른쪽 값의 '모양'을 그대로 그려주는 패턴이라고 보시면 돼요.

구조 분해 할당에서 변수 이름은 어떻게 바꾸나요?

{ 원래속성명: 새변수명 } 형태로 쓰면 됩니다. const { name: username } = user; 라고 하면 user.name 값을 꺼내서 username이라는 이름의 지역 변수에 담아줘요. 처음엔 타입 표기처럼 보여서 헷갈리는데, 콜론 앞이 원래 속성명, 뒤가 새 변수명이라는 순서만 기억하면 됩니다.

구조 분해 할당에 기본값을 줄 수 있나요?

네, 됩니다. const { timeout = 5000 } = options; 이렇게 쓰면 options.timeoutundefined일 때 5000이 들어갑니다. 주의할 점은 기본값은 오직 undefined일 때만 적용된다는 거예요. null, 0, '' 같은 값은 그대로 통과합니다. 이름 바꾸기와 같이 쓸 수도 있어요: const { t: timeout = 5000 } = options;.

구조 분해 할당이랑 스프레드(spread)는 뭐가 다른가요?

문법은 비슷해 보여도 방향이 정반대입니다. 구조 분해(const { a, b } = obj)는 기존 구조에서 값을 꺼내는 거고, 스프레드(const copy = { ...obj })는 새 구조 안에 값을 펼쳐 넣는 거예요. 참고로 구조 분해 패턴 안에 들어가는 ...는 rest 패턴으로, 나머지 값들을 한 변수에 모아주는 역할을 합니다.

Coddy로 코딩 배우기

시작하기