JS : 구조 분해 할당, 스프레드, 불변 업데이트
Step 1. 구조 분해 할당(Destructuring assignment)
지난 실습에서는 객체에서 값을 꺼낼 때 점을 찍어서 꺼냈다.
const student = { name: "지민", score: 85 };
const name = student.name;
const score = student.score;
그런데 위와 같은 방식은 객체의 속성이 여러 개라면 그 만큼의 여러 줄의 코드를 작성해야 한다.
이때, 객체 구조 분해를 이용하면 객체의 속성을 해체(분해)하여 그 값을 개별 변수에 한 줄로 담을 수 있다.
const { name, score } = student;
console.log(name); // 지민
console.log(score); // 85
왼쪽의 중괄호는 객체를 만드는 것이 아니라
"이 객체에서 name이라는 이름의 값과 score라는 이름의 값을 꺼내서, 같은 이름의 변수에 담아라" 라는 지시이다.
중괄호 안에 쓰는 이름은 객체 안에 있는 이름과 정확히 동일해야 한다. 동일하지 않은 경우, undefined가 나온다.
함수의 매개변수 자리에서도 바로 쓸 수 있다. React에서 많이 보게 될 형태이다.
const describe = ({ name, score }) => `${name}: ${score}점`;
console.log(describe(student)); // 지민: 85점
describe는 객체 하나를 받는데, 받자마자 그 안의 name과 score를 꺼내 쓴다.
백틱(`)으로 감싼 문자열 안에서는 ${ } 사이에 값을 끼워 넣을 수 있다. 이것을 템플릿 리터럴(Template literals)이라고 한다.
Step 2. 스프레드(Spread)
점 세 개 ...는 껍데기를 벗겨서 내용물을 쏟아낸다.
const numbers = [1, 2, 3];
const more = [...numbers, 4, 5];
console.log(more); // [1, 2, 3, 4, 5]
console.log(numbers); // [1, 2, 3] ← 원본은 그대로
[...numbers, 4, 5]는 "새 배열을 만드는데, 먼저 numbers의 내용물을 다 쏟아 넣고, 그다음 4와 5를 넣어라"는 뜻이다.
객체도 동일하다.
const student = { name: "지민", score: 85 };
const updated = { ...student, score: 90 };
console.log(updated); // { name: "지민", score: 90 }
console.log(student); // { name: "지민", score: 85 } ← 그대로
여기서 순서가 중요하다.
...student를 먼저 쏟아서 name과 score를 다 넣은 다음, score: 90이 나중에 나오면서 앞의 것을 덮어쓴다.
순서를 뒤집으면 덮어쓰기가 반대로 일어나서 원래 값이 남는다.
Step 3. 불변 업데이트(Immutable Update)
배열이나 객체 등의 기존 원본 데이터를 직접적으로 수정하지 않고(불변), 복사본을 만들어서 수정하는 방식으로 원하는 변화가 반영된 새로운 데이터를 만드는 것을 불변 업데이트라고 한다.
const scores = [85, 42, 91];
scores.push(70); // 원래 데이터를 직접 고침 (원래 데이터 변함)
const next = [...scores, 70]; // 새로운 배열을 만듦 (원래 데이터 불변)
두 줄 다 결과 배열은 똑같아 보인다.
그런데 React는 두 번째 const next = [...scores, 70]; 만 인정한다. (새로운 메모리 주소 할당)
React는 데이터가 바뀌었는지 확인할 때 내용물을 하나하나 비교하지 않는다. 그건 너무 느리기 때문이다.
대신 "이게 아까 그 배열과 같은 물건인가?" 만 확인한다. (메모리 주소가 같은지 확인한다.)
push를 쓰면 물건 자체는 그대로고 안의 내용만 바뀐다. (메모리 주소는 그대로)
React가 보기엔 여전히 같은 물건(메모리 주소가 그대로)이니 "안 바뀌었네" 하고 화면을 다시 그리지 않는다.
값은 바뀌었는데 화면은 그대로인 상황이 여기서 생긴다.
[...scores, 70]은 아예 새 물건을 만든다. (새로운 메모리 주소가 할당된다.)
React가 "다른 물건이네" 하고 화면을 다시 그린다. (메모리 주소가 다르네)
즉, React는 기존 데이터와 새로운 데이터의 메모리 주소를 비교하여 메모리 주소가 다른 경우에, 화면을 다시 그린다.
콘솔에서 확인해 보자.
const a = [1, 2, 3];
// JS에서 객체나 배열 등의 참조 타입을 다른 변수에 할당하는 경우, 복사본이 아닌 메모리 주소가 할당된다.
const b = a; // 즉, a, b 두 변수가 동일한 메모리를 가리킨다.
const c = [...a];
console.log(a === b); // true ← 같은 물건
console.log(a === c); // false ← 내용은 같지만 다른 물건
b는 a와 같은 물건을 가리키는 다른 이름일 뿐 c만 진짜 새 배열이다.
쓰면 안 되는 것과 대신 쓸 것
| 하려는 일 | 쓰면 안 되는 것 | 대신 쓸 것 |
| 배열 뒤에 추가 | push | [...arr, 새값] |
| 배열 앞에 추가 | unshift | [새값, ...arr] |
| 배열에서 제거 | splice | filter |
| 배열 요소 수정 | arr[0] = x | map |
| 정렬 | sort | [...arr].sort() |
| 객체 값 수정 | obj.key = x | { ...obj, key: x } |
sort는 특히 조심해야 한다. 원본을 직접 정렬해버리기 때문에 반드시 스프레드로 복사한 다음에 호출해야 한다.
연습 문제
const todos = [
{ id: 1, text: "React 배우기", done: false },
{ id: 2, text: "장보기", done: true },
{ id: 3, text: "운동하기", done: false },
];
모든 문제에서 todos는 절대 변하면 안 된다.
- 새 할 일 { id: 4, text: "청소하기", done: false } 를 맨 뒤에 추가한 새 배열
- id가 2인 항목을 제거한 새 배열
- id가 1인 항목의 done을 true로 바꾼 새 배열
- 완료되지 않은 할 일의 텍스트만 뽑은 배열 → ["React 배우기", "운동하기"]
답안
const todos = [
{ id: 1, text: "React 배우기", done: false },
{ id: 2, text: "장보기", done: true },
{ id: 3, text: "운동하기", done: false },
];
const todos1 = [...todos, { id: 4, text: "청소하기", done: false }];
const todos2 = todos.filter((todo) => todo.id !== 2);
const todos3 = todos.map((todo) =>
todo.id === 1 ? { id: 1, text: "React 배우기", done: true } : todo,
);
const todos4 = todos
.filter((todo) => todo.done === false)
.map((todo) => todo.text);
console.log(todos1);
console.log(todos2);
console.log(todos3);
console.log(todos4);
'개발 > React' 카테고리의 다른 글
| [React] Component와 Props (0) | 2026.09.14 |
|---|---|
| [React] React 개요 (0) | 2026.09.14 |
| [React] React를 위한 JavaScript (3) (0) | 2026.09.09 |
| [React] React를 위한 JavaScript (1) (0) | 2026.09.07 |
| [React] macOS에서 VS Code를 통한 React 환경 세팅 (0) | 2026.09.04 |