React
React 상태 업데이트와 스냅샷
setCount로 다음 숫자를 요청해도, 이미 실행 중인 버튼 함수의 count는 그대로입니다.
이럴 때 필요해요
장바구니 수량을 세 번 늘렸는데 하나만 늘어났다면, 각 줄이 어떤 숫자를 읽었는지 살펴보면 이유를 알 수 있습니다.
어떻게 동작하나요?
세 줄이 모두 같은 숫자를 읽어요
화면의 수량이 0일 때 버튼 함수도 count를 0으로 읽습니다. setCount(count + 1)은 React에 다음 수량을 1로 바꿔 달라는 요청입니다. 그 요청이 실행 중인 함수 안의 count를 바꾸지는 않습니다. 같은 함수에서 이 줄을 세 번 실행하면 모두 1로 바꿔 달라고 요청합니다. 이렇게 한 번의 화면 계산에서 받은 상태 값을 상태 스냅샷이라고 부릅니다.
이전 결과에 계속 더하려면
앞에서 계산한 결과에 계속 더하고 싶다면 setCount(n => n + 1)처럼 함수를 전달합니다. React는 앞선 계산의 결과를 다음 함수에 넘겨줍니다. 0에서 세 번 요청하면 0 → 1 → 2 → 3으로 계산합니다. 이 함수를 업데이터 함수라고 합니다. 숫자를 직접 전달하는 것과 이전 결과로 계산하는 것은 서로 다른 요청입니다.
코드 예제
동작을 설명하는 읽기 전용 예제입니다.
// 버튼을 누를 때 count가 0이라면
setCount(count + 1); // 1로 바꾸기
setCount(count + 1); // 1로 바꾸기
setCount(count + 1); // 1로 바꾸기세 줄의 count는 모두 0이므로 다음 수량은 1입니다. 각 줄을 setCount(n => n + 1)로 바꾸면 이전 결과에 더해서 3이 됩니다. 아래 실습에서 두 방법을 직접 비교할 수 있습니다.
이 개념으로 실습하기
배칭과 객체 상태 업데이트
요청을 모아 처리하기
이 버튼의 요청들은 함수가 끝난 뒤 모아서 처리됩니다. 이를 배칭이라고 합니다. 다음 클릭은 새 화면의 수량에서 시작하므로 같은 버튼을 다시 누르면 1 또는 3에서 이어집니다. 업데이터는 입력으로 다음 값만 계산해야 합니다. 통신이나 다른 상태 변경을 넣으면 재실행 시 문제가 생길 수 있습니다.
객체와 배열 상태 바꾸기
상태가 객체나 배열이라면 기존 내용을 직접 고치지 않고 바뀐 부분을 새 객체로 만들어 전달합니다. 예를 들어 주소의 도시를 바꿀 때는 주소 객체와 이를 담은 객체를 각각 새로 만듭니다. 전개 문법은 한 단계만 복사합니다. 이 원칙을 불변성이라고 하며 이전 화면에서 사용한 값을 보존하는 데 도움이 됩니다. 또한 await setCount()는 화면 반영 완료를 기다리는 방법이 아닙니다.
자주 헷갈리는 포인트
- state 객체 내부 프로퍼티를 직접 수정한 뒤 같은 객체 참조를 setter에 전달하지 않습니다.
- await setState()가 다음 렌더와 DOM 커밋 완료를 기다려 준다고 생각하지 않습니다.
내 말로 설명해 보기
setCount(count + 1)을 세 번 호출한 경우와 업데이터 함수를 세 번 전달한 경우는 어떻게 다른가요?
생각할 단서 첫 클릭에서 각 줄이 읽는 count와 업데이터가 받는 이전 결과를 구분해 보세요.
예시 답변 확인
설명에 들어갈 핵심
- 값 전달 세 번은 같은 스냅샷에서 같은 값을 요청합니다.
- 업데이터 함수는 이전 대기열 결과에 이어서 계산합니다.
- 다음 클릭은 새로 렌더링된 상태 스냅샷에서 시작합니다.
예시 답변
count가 0이면 값 전달 세 번은 모두 1로 바꾸는 요청이므로 결과는 1입니다. 업데이터 세 번은 0→1→2→3으로 누적됩니다. 다시 클릭하면 각각 현재 값 1과 3에서 시작해 2와 6이 됩니다.
문장을 그대로 외우기보다 예제의 값과 실행 흐름을 떠올리며 설명해 보세요.
실전 시나리오에 적용해 보기
함께 읽으면 좋은 개념
관련 면접 질문 5개
React state는 일반 변수와 어떻게 다른가요?
React state는 컴포넌트가 다시 호출되는 렌더 사이에도 React가 값을 보존해 주고, setter를 호출하면 다음 화면 계산(재렌더)을 요청하는 컴포넌트의 기억 저장소입니다. 반면 함수 안의 일반 지역 변수는 렌더마다 새로 초기화되며 값을 바꿔도 React가 화면을 다시 그리지 않습니다. 또한 각 렌더에서의 state는 그 시점의 스냅샷이므로 setter를 호출한 직후에도 현재 실행 중인 함수 안의 변수 값은 즉시 바뀌지 않습니다.
꼬리질문과 참고자료 →state를 직접 변경하지 않고 setter를 사용하는 이유는 무엇인가요?
state 객체를 직접 수정(mutation)하면 메모리의 값은 바뀌더라도 React에 업데이트 요청이 전달되지 않아 화면이 다시 그려지지 않기 때문입니다. setter를 통해 새로운 객체 참조나 값을 넘겨주어야 React가 이전 스냅샷과 비교(Object.is)해 변경을 감지하고 다음 렌더를 예약할 수 있습니다. 또한 기존 상태를 훼손하지 않고 새 복사본을 만드는 불변성을 지켜야 이전 렌더 스냅샷과 메모이제이션 비교, 디버깅 이력이 안전하게 유지됩니다.
꼬리질문과 참고자료 →React는 상태 변경을 어떤 방식으로 처리하나요?
useState나 useReducer의 setter가 호출되면 React는 해당 업데이트 요청을 큐에 넣고 다음 렌더를 예약합니다. 이때 새 상태값이 이전 상태값과 Object.is 기준으로 동일하다면 불필요한 재렌더를 건너뛰며, 값이 달라졌다면 컴포넌트를 다시 호출해 새 UI 트리를 계산한 뒤 변경된 부분만 커밋 단계에서 DOM에 반영합니다. 즉 React가 모든 일반 객체 내부를 실시간으로 감시하는 것이 아니라, 명시적인 setter 호출과 참조 비교를 통해 상태 전이를 처리합니다.
꼬리질문과 참고자료 →