React 상태와 객체 동일성: 속성이 바뀌어도 왜 같은 상태일 수 있는가
상태 설정에서 이전 값과 다음 값을 비교하는 기준을 객체의 내부 내용과 구분하고, 같은 객체를 다시 전달해 화면 갱신 신호를 잃는 실패를 진단한다.
목차
표준·구현·측정·해석 표시는 무엇인가요?
- 표준웹 표준이나 언어 명세가 정한 동작
- 구현특정 기술이나 브라우저가 실제로 구현한 동작
- 측정명시한 환경에서 직접 실행해 관찰한 결과
- 해석앞선 근거에서 도출한 설계 판단
- 미확인아직 공식 근거나 재현 결과를 확인하지 못한 내용
30초 요약
React의 useState 상태 설정 함수는 이전 상태와 다음 상태를 Object.is로 비교한다. 객체에서는
속성 내용이 비슷한지가 아니라 같은 객체 값인지가 핵심이다.
기존 객체의 속성만 변경 → 객체 동일성은 그대로 → 같은 상태 신호가 될 수 있음
새 객체를 만들어 전달 → 객체 동일성이 달라짐 → 새 상태를 표현할 수 있음화면이 바뀌지 않을 때 강제 다시 렌더링부터 추가하지 않는다. 상태 설정 직전
Object.is(previous, next)를 확인하고, 같은 객체를 다시 전달했는지 먼저 찾는다.
이 글을 관통하는 상황: 수량 값은 2인데 화면은 1이다
주문 수량과 합계를 한 객체 상태로 관리한다고 하자.
import { useState } from 'react'
export default function OrderEditor() {
const [order, setOrder] = useState({
quantity: 1,
unitPrice: 10000,
})
function handleAdd() {
order.quantity += 1
setOrder(order)
}
return (
<section>
<p>수량: {order.quantity}</p>
<p>합계: {order.quantity * order.unitPrice}원</p>
<button type="button" onClick={handleAdd}>수량 추가</button>
</section>
)
}처리 함수 안에서 console.log(order.quantity)는 2를 보여 줄 수 있다. 하지만 상태 설정 함수에
전달한 값은 이전 렌더링이 읽던 바로 그 order 객체다. React는 “속성 로그가 달라졌다”가 아니라
“이전 상태 값과 다음 상태 값이 같은가?”를 판단한다.
잘못된 대응은 DOM을 직접 바꾸거나, 화면을 억지로 다시 계산하려고 주문과 무관한 카운터 state를 추가하는 우회다. 그러면 상태와 화면의 기준이 더 멀어진다. 먼저 전달한 값의 동일성을 확인해야 한다.
상태는 현재 렌더링의 스냅샷이다
“어차피 다음 화면에 쓸 값인데 지금 바꿔도 된다”는 생각은 이전과 다음 상태를 동시에 관찰할 수 없게 만든다. 오류 기록이나 비동기 처리 함수가 이전 렌더링의 객체를 들고 있다면 그 객체의 내용도 예상하지 못한 시점에 달라진다.
속성 변경과 새 객체는 다른 사건이다
따라서 다음 두 구현은 React에 다른 신호를 준다. 두 함수를 한 클릭에서 차례로 실행하는 코드가 아니라, 같은 수량 추가 요구를 구현하는 서로 독립된 대안이다. 코드를 읽고 먼저 예측해 보자.
- 잘못된 구현에서
Object.is(previousOrder, nextOrder)는 무엇이며 화면은 새 수량을 받을까? - 교정 구현에서는 같은 비교가 무엇이며 이전 객체의 수량은 보존될까?
먼저 잘못된 구현이다.
function handleAddWrong() {
const previousOrder = order
previousOrder.quantity += 1
const nextOrder = previousOrder
console.log(Object.is(previousOrder, nextOrder)) // true
setOrder(nextOrder)
}속성을 바꿨지만 nextOrder는 같은 객체이므로 비교 결과는 true다. React는 같은 상태 신호를
받아 새 화면 계산을 생략할 수 있다. 이제 기존 객체를 바꾸지 않고 새 객체를 명시적으로 만든다.
function handleAddCorrect() {
const previousOrder = order
const nextOrder = {
quantity: previousOrder.quantity + 1,
unitPrice: previousOrder.unitPrice,
}
console.log(Object.is(previousOrder, nextOrder)) // false
setOrder(nextOrder)
}교정 구현의 비교 결과는 false이고 previousOrder.quantity는 그대로다. React에는 새 상태 값이
전달되어 수량과 합계를 다시 계산할 수 있다. 속성이 많거나 중첩된 객체에서 변경 경로를 복사하는
방법은 4편에서 다룬다.
Object.is로 상태 신호를 먼저 확인한다
상태를 전달하기 직전에 이전 값과 다음 값을 기록한다.
const nextOrder = order
nextOrder.quantity += 1
console.log({
sameState: Object.is(order, nextOrder), // true
quantity: nextOrder.quantity, // 2
})
setOrder(nextOrder)quantity가 2라는 로그와 sameState가 true라는 로그는 모순이 아니다. 하나는 객체 속성의
현재 내용이고, 다른 하나는 두 이름이 같은 객체 값을 가리키는지 보여 준다.
같은 비교 규칙이 원시 값에도 적용된다
객체가 아닌 숫자 상태도 같은 Object.is 기준을 사용한다.
const [quantity, setQuantity] = useState(2)
setQuantity(2)
Object.is(2, 2) // trueNaN, +0, -0처럼 Object.is와 ===의 결과가 달라지는 숫자 경계는 JavaScript 동일성 선행
글에서 다뤘다. React는 그 규칙을 상태 비교에 사용하므로 필요한 경우 그 결과를 그대로 대입해
예측할 수 있다.
생략은 화면 결과의 경계로 검증한다
2편에서 본 것처럼 함수 호출과 실제 화면 반영은 다른 증거다. 테스트에서는 내부 호출 횟수보다 다음 사용자 결과를 확인한다.
- 같은 상태를 다시 전달해도 화면 결과가 불필요하게 달라지지 않는가?
- 새 상태를 전달하면 수량과 합계가 함께 바뀌는가?
- 이전 상태 객체가 변경되지 않은 채 로그와 비동기 처리에서 보존되는가?
같은 상황을 동일성 신호로 다시 진단한다
문제 처리 함수의 수량 로그는 2인데 화면은 1로 남음
잘못된 판단 객체 속성이 바뀌면 React가 내부 변경을 찾아낼 것임
관찰 Object.is(previousOrder, nextOrder)가 true
원인 이전 상태 객체를 직접 바꾸고 같은 객체를 다음 상태로 전달
행동 이전 객체를 보존하고 변경을 표현하는 새 객체를 상태 설정 함수에 전달
검증 Object.is(previousOrder, nextOrder)는 false이고 수량·합계가 함께 2로 바뀜풀 리퀘스트에서는 다음 질문을 확인한다.
- 상태 객체의 속성을 직접 대입하거나 배열을 직접 변경하지 않는가?
- 상태 설정 함수에 이전과 같은 객체를 다시 전달하지 않는가?
- 화면 갱신 실패를 조사할 때
Object.is(previous, next)를 확인했는가? - 함수 호출 횟수와 사용자에게 보이는 상태 변경을 같은 계약으로 만들지 않았는가?
기억할 축은 짧다.
속성이 달라진 것과 새 객체가 된 것은 다르다. React에는 다음 상태 값을 전달한다.
다음 글에서는 중첩 상태를 직접 바꾸지 않고 변경 경로에 새 객체를 만드는 방법과 그 이유를 살펴본다.
Active recall
기억에서 꺼내 보기
답을 쓰지 않아도 됩니다. 먼저 머릿속으로 답한 뒤 펼쳐서 정답·이유·흔한 오해를 비교하세요.
01상태 객체의 속성을 직접 바꾼 뒤 같은 객체를 상태 설정 함수에 전달하면 React는 왜 같은 상태로 판단할 수 있는가?
정답
속성 변경은 객체 자체를 새 객체로 만들지 않으므로 이전 상태와 다음 상태가 Object.is에서 같은 객체로 비교되기 때문이다.
관련 설명 다시 읽기왜 그런가
React는 상태 객체의 모든 속성을 깊게 순회해 변경을 찾지 않고 전달받은 값 자체를 비교한다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
02상태 설정이 화면 갱신으로 이어지지 않을 때 가장 먼저 어떤 비교를 기록할 수 있는가?
정답
상태 설정 직전의 이전 값과 전달할 다음 값을 Object.is로 비교해 React가 같은 값 신호를 받는지 확인할 수 있다.
관련 설명 다시 읽기왜 그런가
비교가 true라면 객체 내부 로그보다 먼저 새 값 자체를 만들었는지 조사해야 한다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
03현재 숫자 상태가 2일 때 다시 2를 전달하면 객체 상태와 다른 깊은 비교가 일어날까?
정답
아니다. 숫자도 Object.is 기준으로 이전 값과 다음 값을 비교하며 Object.is(2, 2)는 true다.
관련 설명 다시 읽기왜 그런가
비교 규칙은 값의 종류에 따라 깊은 비교로 전환되지 않는다. 객체는 같은 객체인지, 원시 값은 SameValue 규칙에 따라 비교된다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
04같은 상태를 전달하면 컴포넌트 함수가 어떤 상황에서도 절대 호출되지 않는다고 테스트해도 될까?
정답
아니다. React는 업데이트를 생략할 수 있지만 어떤 경우에는 생략 전에 컴포넌트를 호출할 수 있다고 공식 문서가 경고한다.
관련 설명 다시 읽기왜 그런가
사용자에게 보이는 결과와 자식 갱신이 생략되는지를 계약으로 보고 함수 호출 횟수 자체를 기능 요구로 만들지 않는다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
출처와 검증 범위
아래 날짜는 링크를 마지막으로 열어 본 날입니다. 문서 상단의 검증일은 글의 설명과 적용 범위를 다시 확인한 날입니다.
- useStateReact · 공식 문서 · 확인 2026-08-08
- State as a SnapshotReact · 공식 문서 · 확인 2026-08-08
- Updating Objects in StateReact · 공식 문서 · 확인 2026-08-08
- SameValueEcma International · 표준 명세 · 확인 2026-08-08
- Object.isEcma International · 표준 명세 · 확인 2026-08-08