반응형
아래는 React에서 상태(특히 참조형 데이터)를 다룰 때 불변성을 유지해야 하는 이유와, 원본 배열을 직접 변경하는 메서드들, 잘못된 예시와 올바른 예시, 그리고 얕은 복사와 깊은 복사에 관한 개념 및 방법들을 정리한 내용이다.
1. 불변성 유지 필요성
React에서 상태(state)는 컴포넌트의 UI 업데이트를 위한 핵심 데이터이다.
특히 객체나 배열과 같은 참조형 데이터(state)를 직접 수정하면,
변경된 값이 새로운 객체로 인식되지 않아 setState
호출 시 React가 변경을 감지하지 못하고 UI가 업데이트되지 않는다.
따라서, 불변성(immutability)을 유지하기 위해 원본 배열이나 객체를 직접 변경하지 않고,
복사본을 만들어 변경한 후 새로운 값으로 상태를 업데이트해야 한다.
2. 원본 배열을 직접 변경하는 메서드들
아래 메서드들은 원본 배열 자체를 직접 변경(변형)한다.
이런 메서드들을 React 상태로 사용할 경우 직접 변경하면 안 된다.
push()
: 배열의 마지막에 요소 추가pop()
: 배열의 마지막 요소 제거shift()
: 배열의 첫 요소 제거unshift()
: 배열의 첫 부분에 요소 추가splice()
: 배열의 특정 인덱스에서 요소 제거 또는 추가sort()
: 배열의 정렬을 수행 (원본 정렬)
이런 메서드들은 모두 원본 배열을 직접 변경(mutate) 하므로 React 상태 관리에 부적합하다.
3. React에서 유저 정보를 상태로 관리하는 예시
3.1 잘못된 예시 (직접 변경 → 불변성 위반)
import React, { useState } from "react"; function UserComponent() { // 초기 상태로 객체 사용 const [user, setUser] = useState({ name: "Alice", age: 25 }); const increaseAgeWrong = () => { // 직접 상태 객체를 수정 → 불변성 위반 user.age = user.age + 1; // 같은 객체를 전달하기 때문에 리렌더링되지 않을 수 있음. setUser(user); }; return ( <div> <p>{user.name} - {user.age} years old</p> <button onClick={increaseAgeWrong}>Increase Age (Wrong)</button> </div> ); } export default UserComponent;
문제점:
상태 객체를 직접 수정하면, React는 변경된 객체와 이전 객체가 동일하다고 판단하여 리렌더링하지 않는다.
3.2 올바른 예시 (복사본 생성 후 변경 → 불변성 유지)
import React, { useState } from "react"; function UserComponent() { const [user, setUser] = useState({ name: "Alice", age: 25 }); const increaseAgeCorrect = () => { // 스프레드 연산자를 사용하여 새로운 객체를 생성한 후, 상태 업데이트 setUser({ ...user, age: user.age + 1 }); }; return ( <div> <p>{user.name} - {user.age} years old</p> <button onClick={increaseAgeCorrect}>Increase Age (Correct)</button> </div> ); } export default UserComponent;
장점:
새로운 객체를 생성하기 때문에 React는 상태가 변경되었음을 감지하고 UI를 업데이트한다.
4. 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy)
4.1 얕은 복사 (Shallow Copy)
- 개념:
객체나 배열의 1차원 요소만 새로 복사하고, 중첩된 객체나 배열은 원본 참조를 공유한다. - 방법:
- 스프레드 연산자:
{ ...obj }
,[...arr]
Object.assign({}, obj)
- 배열의 경우:
arr.slice()
- 스프레드 연산자:
- 예시:
const original = { a: 1, nested: { b: 2 } }; const shallowCopy = { ...original }; shallowCopy.nested.b = 99; console.log(original.nested.b); // 99 (원본도 변경됨)
4.2 깊은 복사 (Deep Copy)
- 개념:
객체의 모든 계층을 재귀적으로 복사하여 원본과 완전히 독립적인 복사본을 생성한다. - 방법의 종류:
- JSON 방식:
JSON.parse(JSON.stringify(obj))
- 단, 함수,
undefined
,Symbol
,Map
,Set
등은 제대로 복사되지 않음.
structuredClone()
(최신 브라우저 지원):- 내장 함수로, 대부분의 자료형을 안전하게 깊은 복사 가능.
- 외부 라이브러리 사용 (예: lodash):
_.cloneDeep(obj)
- JSON 방식:
- 예시 (JSON 방식):
const original = { a: 1, nested: { b: 2 } }; const deepCopy = JSON.parse(JSON.stringify(original)); deepCopy.nested.b = 99; console.log(original.nested.b); // 2 (원본은 변경되지 않음)
5. 요약
- React 상태는 참조형(객체, 배열, 함수)일 때 직접 변경하면 안 된다.
→ 반드시 새로운 복사본을 만들어 변경해야 한다. - 원본 배열을 직접 변경하는 메서드들:
push()
,pop()
,splice()
,sort()
,shift()
,unshift()
등 - 불변성 유지가 중요한 이유:
직접 변경하면 React가 변경을 감지하지 않아 UI 업데이트가 이루어지지 않는다. - 예시:
- 잘못된 예: 직접
user.age
를 변경하고setUser(user)
호출 - 올바른 예:
setUser({ ...user, age: user.age + 1 })
- 잘못된 예: 직접
- 얕은 복사:
- 개념: 객체의 1차원 요소만 복사, 중첩 객체는 참조 공유
- 방법: 스프레드 연산자(
{ ...obj }
,[...arr]
),Object.assign()
,slice()
- 깊은 복사:
- 개념: 모든 계층의 객체를 완전히 복사하여 독립적인 복사본 생성
- 방법:
- JSON 방식:
JSON.parse(JSON.stringify(obj))
structuredClone(obj)
(최신 브라우저 지원)- 외부 라이브러리:
_.cloneDeep(obj)
(lodash)
- JSON 방식:
반응형
'Frontend > Javascript Typescript' 카테고리의 다른 글
[Javascript] 4. 변수 (모던자바스크립트딥다이브) (0) | 2025.04.06 |
---|---|
[Javascript] 렉시컬 스코프와 클로저 (0) | 2025.03.25 |
[JavaScript] Math 객체와 주요 메서드 정리 (0) | 2025.03.20 |
[Javascript] 페이지 로딩 후 스크립트 실행(defer, DOMContentloade, onload) (0) | 2025.03.20 |
[Javascript] 객체 생성자, 객체 리터럴, JSON (2) | 2025.03.20 |