본문 바로가기

React/개념

[React-Hook] useState 업데이트의 두 가지 방식

728x90

 

React State 업데이트는 두 가지 방식으로 이전 상태를 업데이트 할 수 있다.

훅 에서 보면

 

클로저로 줄 경우 클로저의 매개변수 prev는 누가, 언제, 왜 주입하는가?!

 

React를 쓰다 보면 상태 업데이트를 아래 두 방식으로 만난다.

// 방식 A
setState(nextState);

// 방식 B
setState(prev => nextState);

 

특히 방식 B를 보면 이런 질문이 자연스럽게 든다.

  • prev는 어디서 오는가?
  • 내가 호출하는 건 onChange뿐인데, 누가 prev를 넣는가?
  • setState의 로직을 재정의하는 건가?
  • 그냥 setState({ ...state })로 직접 넣으면 되는 것 아닌가?
결론: setState에 함수를 넘기면, React가 그 함수를 직접 호출하면서 인자로 “현재 시점의 최신 state”를 넣어준다. 개발자는 로직을 재정의하는 게 아니라 “다음 state를 계산하는 방법”을 전달하는 것이다.

 

 React의 상태 업데이트 모델

React에서 상태 업데이트는 즉시 반영되는 “명령”이 아니라, React가 처리 타이밍을 제어하는 “요청(request)”에 가깝다.

이 관점을 잡으면 prev가 왜 필요한지 자연스럽게 이해된다.

핵심 문장:

  • State는 즉시 변경되지 않을 수 있다.
  • React는 성능을 위해 여러 업데이트를 묶어서(batch) 처리한다.
  • 따라서 컴포넌트 내부의 state 변수는 “항상 최신”이 아니라 “렌더 시점의 스냅샷”일 수 있다.

방식 ①: 다음 state 값을 직접 전달

예시

const [input, setInput] = useState({...})

const onChange = (e) => {
    const { name, value } = e.target;
	setInput({
	  ...input,
	  name: value,
	});
};

의미

“이 값으로 상태를 바꿔 달라” — 계산을 개발자가 직접 한다.

특징

  • 단순하고 직관적
  • 이전 state에 의존하지 않는 경우에는 충분
  • 하지만 여러 업데이트가 겹치면(배치/동시성) “옛 state” 기준으로 덮어쓸 위험이 생길 수 있음

 

방식 ②: 업데이트 함수를 전달 (Functional Update)

공식문서 링크(눌러서 클릭)

예시

const [input, setInput] = useState({...})

const onChange = (e) => {
    const { name, value } = e.target;
    setInput((prev) => ({
      ...prev,
      [name]: value,
    }));
};

의미

“다음 state를 직접 주지 않고, React가 가진 최신 state(prev)를 주면 그걸로 다음 state를 계산해 반환하겠다.”

핵심 포인트

  • 내가 setInput에 데이터를 직접 주입하지 않았는데?..
  • prev는 개발자가 전달하는 값이 아니다.
  • React가 호출 시점에 자동으로 주입하는 인자다.

그럼 prev는 누가, 언제 주입하는가?

setState에 함수가 들어오면 React 내부는 개념적으로 아래처럼 동작한다.

// React 내부 개념 모델
const currentState = state;
const nextState = updaterFunction(currentState); // 여기서 currentState가 prev로 들어감
state = nextState;

 

즉 개발자는 updaterFunction만 제공하고, React가 “현재 시점의 최신 state”를 prev로 넣어 호출한다.

왜 이런 구조가 필요한가? 

React는 성능을 위해 여러 번의 state 업데이트를 즉시 적용하지 않고 한 번에 처리할 수 있다.

그 결과 “컴포넌트 안에서 보이는 state 변수”는 업데이트 직후에도 여전히 같은 값(스냅샷)일 수 있다.

 

아래를 보쟈

 

실제로 문제가 생기는 예

값을 직접 넣는 방식 ( 여러번 순차적으로 업뎃하구싶으~)

setAge(age + 1);
setAge(age + 1);
setAge(age + 1);

 

가정: age === 42

React가 배치 처리하면 큐에는 사실상 이렇게 쌓일 수 있다:

setAge(43)
setAge(43)
setAge(43)

결과: 43

함수형 업데이트

setAge(a => a + 1);
setAge(a => a + 1);
setAge(a => a + 1);

 

React는 각 updater를 순서대로 호출하며, 매번 최신 값을 인자로 준다:

  1. a = 4243
  2. a = 4344
  3. a = 4445

결과: 45

문법 오해: (prev) => ({ ... })는 왜 괄호를 쓰나?

아래 코드는 객체를 “즉시 반환(implicit return)”하는 화살표 함수다.

(prev) => ({
  ...prev,
  name: value,
})

 

이는 아래와 완전히 동일하다.

(prev) => {
  return {
    ...prev,
    name: value,
  };
}

{}는 기본적으로 “블록”으로 해석되기 때문에, 객체를 반환하려면 ({ ... })처럼 괄호로 감싸 “객체 리터럴”임을 명시해야 한다.

자주 생기는 오해 정리

오해 정답
setState 로직을 재정의한다 아니다. 동일한 setState에 “값” 대신 “업데이트 함수”를 전달하는 것뿐이다.
prev를 내가 넣는다 아니다. React가 updater 함수를 호출할 때 최신 state를 인자로 주입한다.
그냥 state를 직접 써도 항상 최신이다 아니다. 컴포넌트의 state 변수는 렌더 시점 스냅샷이며, 배치 처리로 인해 최신이 아닐 수 있다.

언제 어떤 방식을 쓰면 좋나?

상황 추천
이전 state와 무관하게 “그 값”으로 설정 setState(nextState)
객체/배열 일부만 갱신(merge)하거나 이전 값에 의존 setState(prev => ...)
연속 업데이트(증가/누적/토글) 또는 동시 업데이트 안전성 필요 setState(prev => ...)
상태 전이 규칙이 복잡해짐 useReducer

최종 요약

  • setState(nextState)는 “값을 직접 지정”하는 방식
  • setState(prev => nextState)는 “React가 가진 최신 state로 계산”하는 방식
  • prev는 React가 updater 함수를 호출할 때 자동으로 주입
  • 배치/동시성 때문에 “컴포넌트의 state 변수”는 항상 최신이 아닐 수 있음

한 문장 요약:

React에서 함수형 업데이트는 상태를 “명령형으로 변경”하는 것이 아니라, React가 보장하는 최신 상태를 기준으로 “다음 상태를 계산”하게 만드는 안정장치다.

 

 

References :

https://react.dev/reference/react/useState?utm_source=chatgpt.com#updating-state-based-on-the-previous-state

 

useState – React

The library for web and native user interfaces

react.dev

728x90