Allra Fintech

참조값 다루기

React에서 배열과 객체 상태를 다룰 때 왜 직접 변경하면 안 되는지 이해합니다.

참조값은 다르게 다뤄야 합니다

이전 챕터에서 숫자 하나를 state로 다루는 건 해봤습니다.
그런데 실무에서는 숫자보다 배열이나 객체를 state로 다루는 경우가 훨씬 많습니다.

여기서 한 가지 주의할 게 있습니다.
배열이나 객체는 숫자처럼 다루면 화면이 안 바뀝니다.

왜 그런지, React가 상태를 비교하는 방식부터 봅시다.

React는 상태를 어떻게 비교할까

setState를 호출하면 React는 이전 값과 새 값을 비교합니다.
비교 결과가 "다르다" 면 화면을 다시 그리고, "같다" 면 무시합니다.

React는 === 로 비교합니다. 자바의 == 연산자와 같은 감각입니다.

원시값과 참조값의 차이

원시값(숫자, 문자열)은 값 자체를 비교합니다.

setState(0)  // 이전 값
setState(1)  // 새 값 → 0 !== 1 → 다르다 → 화면 갱신

참조값(배열, 객체)은 **참조(주소)**를 비교합니다.

const items = ['사과', '바나나'];
items.push('포도');
setState(items);  // 같은 배열 참조 → 이전과 같다 → 무시

배열 안에 '포도'가 추가됐지만, React 입장에서는 같은 배열 객체를 다시 받은 것입니다.
내용이 바뀌었는지는 보지 않습니다. 참조가 같으면 같은 겁니다.

자바에서 List 두 개를 == 로 비교하면 주소만 보는 것과 똑같습니다.

나쁜 예시

할 일 목록에 항목을 추가하는 코드입니다.

const [todos, setTodos] = useState(['출근', '점심']);

function addTodo() {
  todos.push('퇴근');   // 기존 배열을 직접 변경
  setTodos(todos);      // 같은 참조 → React는 무시
}

push는 기존 배열을 직접 바꿉니다.
배열의 내용은 바뀌었지만 참조는 그대로이기 때문에, React는 "달라진 게 없네"하고 넘어갑니다.
화면은 안 바뀝니다.

객체도 마찬가지입니다.

const [user, setUser] = useState({ name: '김개발', age: 30 });

function birthday() {
  user.age = 31;       // 기존 객체를 직접 변경
  setUser(user);       // 같은 참조 → React는 무시
}

좋은 예시

새 배열을 만들어서 넘기면 됩니다.
스프레드 연산자(...)를 쓰면 기존 내용을 복사하면서 새 참조를 만들 수 있습니다.

const [todos, setTodos] = useState(['출근', '점심']);

function addTodo() {
  setTodos([...todos, '퇴근']);  // 새 배열 → 다른 참조 → 화면 갱신
}

...은 스프레드 연산자입니다. 기존 배열을 펼쳐서 새 배열을 만듭니다.

객체도 같은 방식입니다.

const [user, setUser] = useState({ name: '김개발', age: 30 });

function birthday() {
  setUser({ ...user, age: 31 });  // 새 객체 → 다른 참조 → 화면 갱신
}

패턴 정리

자주 쓰는 패턴을 모아두면 이렇습니다.

동작나쁜 예시좋은 예시
배열에 추가items.push(x)[...items, x]
배열에서 제거items.splice(i, 1)items.filter((_, idx) => idx !== i)
배열 항목 수정items[i] = xitems.map((v, idx) => idx === i ? x : v)
객체 필드 수정obj.key = x{ ...obj, key: x }

왼쪽은 전부 기존 참조를 직접 건드리는 방식이고, 오른쪽은 새 참조를 만드는 방식입니다.
React에서는 오른쪽만 써야 합니다.

정리

  • React는 상태를 === 로 비교한다. 참조가 같으면 변경으로 보지 않는다.
  • 배열이나 객체를 직접 바꾸면 참조가 안 바뀌기 때문에 화면도 안 바뀐다.
  • 항상 새 배열, 새 객체를 만들어서 넘겨야 한다.
  • 스프레드 연산자(...)가 가장 기본적인 도구다.

직접 해보기

  • 배열 state에 항목을 추가하고 삭제하는 컴포넌트 만들어보기

다음 챕터 예고

상태를 다루는 감각이 잡혔으니, 다음에는 화면이 그려진 다음에 실행되는 코드를 어떻게 다루는지 알아보겠습니다.