구조분해 할당, 전개 연산자, 나머지 연산자 정리
구조분해 할당, 전개 연산자, 나머지 연산자는 객체나 배열의 값을 더 쉽게 꺼내거나 복사할 때 자주 사용된다.
이름이 비슷하고 ... 문법도 같이 사용되다 보니 처음에는 헷갈릴 수 있다.
코드 보다가 가끔씩 헷갈려서 단순 정리용으로 남기는 글이다.
1. 예제 객체
아래는 예제에서 사용할 객체다.
const a = { aa: 1, bb: 2, cc: 3, ss: { aaa: 1, bbb: 2 } };
a 객체는 aa, bb, cc, ss 속성을 가지고 있다.
그리고 ss 속성의 값은 다시 객체다.
{
aaa: 1,
bbb: 2
}
2. 구조분해 할당
구조분해 할당을 사용하면 객체나 배열의 값을 변수로 바로 꺼낼 수 있다.
const { aa, bb, cc, ss } = a;
console.log(aa, bb, cc, ss);
// 1 2 3 { aaa: 1, bbb: 2 }
위 코드는 a 객체에서 aa, bb, cc, ss 속성을 꺼내서 각각 같은 이름의 변수로 선언한다.
즉, 아래처럼 직접 접근해서 변수에 담는 것과 비슷하다.
const aa = a.aa;
const bb = a.bb;
const cc = a.cc;
const ss = a.ss;
구조분해 할당을 사용하면 이 과정을 한 줄로 줄일 수 있다.
3. 중첩 객체의 값 가져오기
ss 속성 안에 있는 aaa 값을 가져오고 싶다면 여러 방식이 있다.
먼저 ss 자체를 구조분해 할당으로 꺼낸 뒤, ss.aaa로 접근할 수 있다.
const { aa, bb, cc, ss } = a;
console.log(ss.aaa);
// 1
이 방식은 ss라는 변수가 실제로 생성된다.
그래서 ss.aaa, ss.bbb처럼 ss 내부 속성에 접근할 수 있다.
4. 중첩 구조분해 할당
중첩 구조분해 할당을 사용하면 객체 안쪽의 값까지 한 번에 꺼낼 수 있다.
const {
aa,
bb,
cc,
ss: { aaa },
} = a;
console.log(aaa);
// 1
위 코드는 a.ss.aaa 값을 aaa라는 변수로 꺼낸 것이다.
주의할 점은 이 경우 ss 변수 자체가 만들어지는 것은 아니라는 점이다.
console.log(ss);
// ReferenceError: ss is not defined
즉, 아래 구조분해 할당은 ss라는 변수를 만드는 것이 아니라 ss 속성 안으로 들어가서 aaa를 꺼내겠다는 의미다.
const {
ss: { aaa },
} = a;
정리하면 다음과 같다.
const { ss } = a;
// ss 변수 생성
const {
ss: { aaa },
} = a;
// ss 변수는 생성되지 않고 aaa 변수만 생성
5. undefined와 ReferenceError
객체에서 존재하지 않는 속성에 접근하면 undefined가 나온다.
console.log(a.notExist);
// undefined
하지만 선언되지 않은 변수에 접근하면 참조 오류가 발생한다.
console.log(notExist);
// ReferenceError: notExist is not defined
그래서 중첩 구조분해 할당에서 ss 변수를 만들지 않았는데 ss에 접근하면 undefined가 아니라 ReferenceError가 발생한다.
const {
ss: { aaa },
} = a;
console.log(aaa);
// 1
console.log(ss);
// ReferenceError: ss is not defined
6. 콜론(:)의 의미
객체 구조분해 할당에서 콜론(:)은 상황에 따라 두 가지처럼 보일 수 있다.
첫 번째는 속성 이름을 다른 변수명으로 바꿔서 받는 경우다.
const { ss: wow } = a;
console.log(wow);
// { aaa: 1, bbb: 2 }
위 코드는 a.ss 값을 wow라는 이름의 변수로 받겠다는 의미다.
이 경우 ss라는 변수는 생성되지 않는다.
console.log(ss);
// ReferenceError: ss is not defined
실제 코드에서는 아래처럼 사용할 수 있다.
const {
formState: { errors: fsErrors },
} = useForm<Inputs>();
위 코드는 formState.errors 값을 꺼내서 fsErrors라는 변수명으로 받겠다는 의미다.
즉, errors라는 이름 대신 fsErrors라는 이름으로 사용하겠다는 뜻이다.
7. 콜론(:)과 중첩 구조분해 할당
콜론(:)은 중첩 구조분해 할당에서도 사용된다.
const {
aa,
bb,
cc,
ss: { aaa },
} = a;
console.log(aaa);
// 1
이 경우의 의미는 ss 속성 안으로 들어가서 다시 구조분해 할당을 하겠다는 뜻이다.
결과적으로 생성되는 변수는 아래와 같다.
aa
bb
cc
aaa
ss 변수는 생성되지 않는다.
즉, 아래 코드는 ss를 변수로 받는 코드가 아니다.
ss: { aaa }
a.ss 안으로 들어가서 aaa를 꺼내겠다는 의미다.
8. ... 연산자의 의미
... 문법은 위치에 따라 의미가 달라진다.
크게 두 가지로 볼 수 있다.
값을 펼치는 위치에서 사용
→ 전개 연산자(spread)
구조분해 할당에서 나머지를 모으는 위치에서 사용
→ 나머지 연산자(rest)
즉, 같은 ... 문법이지만 어디에서 사용되는지에 따라 의미가 달라진다.
9. 전개 연산자(spread)
전개 연산자는 객체나 배열의 값을 펼칠 때 사용한다.
예를 들어 ss 객체를 새로운 객체로 복사하고 싶다면 아래처럼 사용할 수 있다.
const { ss } = a;
const test = { ...ss };
console.log(test);
// { aaa: 1, bbb: 2 }
위 코드에서 { ...ss }는 ss 객체의 속성들을 펼쳐서 새로운 객체를 만든다는 의미다.
즉, 아래 객체를 새로 만드는 것과 비슷하다.
const test = {
aaa: 1,
bbb: 2,
};
그래서 아래처럼 출력하면 둘 다 같은 형태로 나온다.
console.log(test, { ...ss });
// { aaa: 1, bbb: 2 } { aaa: 1, bbb: 2 }
10. 나머지 연산자(rest)
나머지 연산자는 구조분해 할당에서 아직 꺼내지 않은 나머지 값들을 모을 때 사용한다.
const { aa, ...test } = a;
console.log(test);
// { bb: 2, cc: 3, ss: { aaa: 1, bbb: 2 } }
위 코드는 a 객체에서 aa는 따로 꺼내고, 나머지 속성인 bb, cc, ss는 test라는 객체에 모으겠다는 의미다.
결과적으로 생성되는 변수는 아래 두 개다.
aa
test
test에는 aa를 제외한 나머지 속성들이 들어간다.
{
bb: 2,
cc: 3,
ss: { aaa: 1, bbb: 2 }
}
즉, 구조분해 할당 안에서 ...test처럼 사용되면 전개가 아니라 나머지 값을 모으는 역할을 한다.
11. spread와 rest 비교
같은 ... 문법이라도 위치에 따라 의미가 달라진다.
전개 연산자는 값을 만들 때 사용한다.
const test = { ...a };
위 코드는 a 객체의 속성을 펼쳐서 새로운 객체를 만든다.
반면 나머지 연산자는 구조분해 할당에서 사용한다.
const { aa, ...test } = a;
위 코드는 aa를 제외한 나머지 속성을 test 객체에 모은다.
정리하면 다음과 같다.
const test = { ...a };
→ a를 펼쳐서 새 객체 생성
→ spread
const { aa, ...test } = a;
→ aa를 제외한 나머지를 test에 모음
→ rest
12. 정리
구조분해 할당은 객체나 배열의 값을 변수로 쉽게 꺼내는 문법이다.
const { aa, bb } = a;
중첩 구조분해 할당을 사용하면 객체 안쪽의 값까지 바로 꺼낼 수 있다.
const {
ss: { aaa },
} = a;
다만 이 경우 ss 변수 자체가 생성되는 것은 아니다.
console.log(aaa);
// 1
console.log(ss);
// ReferenceError: ss is not defined
콜론(:)은 속성을 다른 변수명으로 받을 때 사용할 수 있다.
const { ss: wow } = a;
이 경우 a.ss 값을 wow라는 변수명으로 받는다.
... 문법은 위치에 따라 의미가 달라진다.
const test = { ...a };
값을 만들 때 사용하면 전개 연산자다.
const { aa, ...test } = a;
구조분해 할당 안에서 사용하면 나머지 연산자다.
결론:
객체에서 필요한 값만 바로 꺼내고 싶다
→ 구조분해 할당
객체 안쪽 값까지 한 번에 꺼내고 싶다
→ 중첩 구조분해 할당
속성 이름과 다른 변수명으로 받고 싶다
→ 속성명: 변수명
객체나 배열을 펼쳐서 새 값을 만들고 싶다
→ spread
구조분해 할당 후 남은 값을 모으고 싶다
→ rest