React 상태가 변경되어도 리렌더링되지 않는 이유와 해결법

React에서 상태를 바꿨는데 화면이 바뀌지 않는 이유 – 자료형과 불변성의 이해

1. 상태를 바꿔도 리렌더링되지 않는 상황

React에서 가장 기본적인 원칙 중 하나는 상태를 변경하면 컴포넌트가 다시 렌더링된다는 것이다. 그러나 상태를 분명히 바꿔서 setState를 호출했는데도 화면이 전혀 변하지 않는 경우가 있다.

const [user, setUser] = useState({ name: '길동' });

// 잘못된 방법
user.name = '지민';
setUser(user); // 리렌더링되지 않음!

사용자의 이름을 바꿔서 setUser를 호출했지만, 화면에는 기존 값이 그대로 남아 있다. 디버그를 해보면 상태는 분명 변경되었는데, React는 아무런 반응도 하지 않는다. 어떤 오류도, 로그도 없다. 하지만 이건 React가 잘못된 것이 아니라, 의도적으로 리렌더링을 생략한 경우다.

2. 리렌더링이 발생하지 않은 이유

React는 상태가 변경되었는지를 판단할 때, 이전 상태와 새로운 상태를 비교한다. 이 비교에서 두 값이 같은 참조(메모리 주소)를 가지고 있을 경우 변경되지 않은 것으로 간주한다.

// React 내부적으로 이런 식으로 비교
if (이전상태 === 새로운상태) {
  // 참조가 같음 → 상태 변경 없다고 간주 → 리렌더링 생략
}

앞서 제시한 예시처럼 객체의 내부 속성만 변경한 경우, 객체의 참조는 동일하므로 React는 "변경 없음"으로 판단한다.

3. JavaScript 자료형과 근본적 이유

이 문제는 결국 JavaScript의 자료형 특성과 메모리 구조에서 시작된다. JavaScript에서는 모든 값이 두 가지 중 하나로 나누어진다:

  • 원시값(Primitive): number, string, boolean, null, undefined, symbol, bigint 등
  • 참조값(Reference): object, array, function 등

원시값은 본래 불변성을 가진다

원시값은 그 자체로 불변(immutable) 특성을 가진다. 한 번 생성된 값은 내부 상태가 변경되지 않으며, 변수에 다른 원시값을 할당한다는 것은 새로운 값을 가리키는 것일 뿐, 기존 값을 수정하는 것이 아니다.

let a = 'hello';
let b = a;
b = 'world';
console.log(a); // 'hello'

값 자체가 복사되기 때문에 a와 b는 전혀 다른 값을 가리킨다.

참조값은 내부가 변경될 수 있는 구조다

객체, 배열, 함수 등은 참조값으로 저장되며, 변수에는 값이 아니라 메모리 주소가 들어간다. 복사하면 값을 복사하는 것이 아니라 같은 주소를 공유하게 된다.

const obj1 = { name: '길동' };
const obj2 = obj1;
obj2.name = '지민';

console.log(obj1.name); // '지민'

이처럼 참조값은 **내부 속성이 변경될 수 있는 구조(mutable)**를 가지며, 개발자가 직접 불변성을 지키지 않으면 예상과 다른 일이 생긴다.

4. 불변성과 상태 업데이트 방식

이 문제를 해결하려면 상태를 불변하게(immutable) 다룰 필요가 있다. 기존 객체를 직접 수정하지 않고, 필요한 부분만 새로 복사하여 새 객체로 교체해야 한다.

// 객체 상태 업데이트
setUser({
  ...user,
  name: '지민',
});

// 중첩된 객체 상태 업데이트
setState({
  ...state,
  user: {
    ...state.user,
    name: '지민',
  },
});

이렇게 하면 user 객체가 새로 생성되면서 참조값이 달라진다. React가 이 변화를 감지해서 리렌더링한다.

배열 상태에서도 동일한 원칙 적용

// 잘못된 방법
items.push(newItem);
setItems(items);

// 올바른 방법
setItems([...items, newItem]);

// 항목 제거
setItems(items.filter(item => item.id !== targetId));

불변성이 깊은 복사를 무조건 하라는 건 아니다. React 입장에서는 참조만 바뀌면 된다.

정리하기

  • React는 상태 변경을 감지할 때 참조 비교를 사용하며, 객체의 내부만 변경하고 참조가 같으면 변경을 감지하지 못한다.
  • JavaScript에서 원시값은 불변, 참조값은 내부 속성이 변경 가능한 구조로 구분된다.
  • 상태 업데이트 시 기존 객체를 수정하지 말고, 필요한 부분만 복사하여 새 객체로 만들어야 한다.