React 상태 관리의 최신 기술: 클래스부터 Hooks까지, 그리고 그 이상

마지막 업데이트 : 04/22/2026
  • React의 상태는 불변으로 취급해야 하며, 특히 객체와 배열의 경우 직접적인 변경보다는 setter를 통해 업데이트해야 합니다.
  • 상태 업데이트는 비동기적으로 이루어지며 일괄 처리될 수 있으므로, 함수형 업데이트 기능을 사용하면 타이머, 클로저 및 빠른 상호 작용에서 발생하는 오래된 상태 문제를 방지할 수 있습니다.
  • 훅(useState, useRef 등)을 사용하는 함수형 컴포넌트는 최신 표준이며, React.memo 및 Immer와 같은 도구는 성능 향상과 중첩 데이터 처리에 도움이 됩니다.
  • 속성과 상태를 명확하게 분리하고, 하향식 데이터 흐름 모델을 사용하면 애플리케이션 규모가 커지더라도 컴포넌트 동작을 예측 가능하게 유지할 수 있습니다.

React 상태 관리 개념

상태(State)는 겉보기에는 간단해 보이지만 앱 규모가 커질수록 금방 복잡해지는 React 개념 중 하나입니다. 처음에는 간단한 카운터로 시작하지만, 어느새 여러 개의 폼 필드, 비동기 업데이트, 중첩된 객체, 그리고 모든 것이 한꺼번에 다시 렌더링될 때 발생하는 성능 문제까지 다루게 됩니다. 상태를 깊이 이해하는 것이 바로 "React를 사용하는 사람"과 실제 환경에서 React 애플리케이션을 확장하고 디버깅할 수 있는 사람을 구분하는 기준입니다.

이 가이드에서는 클래스 컴포넌트와 라이프사이클 메서드부터 최신 Hooks 및 불변 업데이트에 이르기까지 React의 현재 상태를 살펴보겠습니다. 또한 비동기 업데이트, 오래된 클로저, useRef 대신 useState를 사용해야 하는 시점, UI를 예측 가능하게 유지하는 방법 등 미묘하지만 중요한 주제들을 자세히 살펴보겠습니다. 목표는 구성 요소가 예상대로 정확하게 동작하도록 명확한 개념을 제시하는 것입니다.

소품부터 국가에 이르기까지, 무엇이 어디에 속하는가?

React에서의 props와 state

모든 React 컴포넌트의 핵심에는 props와 state라는 두 가지 주요 데이터 소스가 있습니다. 소품 부모 컴포넌트에서 전달되어 해당 렌더링 수명 동안 고정된 상태를 유지하는 반면 상태 해당 컴포넌트 자체가 소유하고 제어하며, 시간이 지남에 따라 변경되는 데이터를 위해 만들어졌습니다.

일반적으로 다음과 같은 규칙을 따릅니다. 데이터가 외부에서 설정되고 해당 컴포넌트에서 변경되지 않으면 props이고, 컴포넌트가 해당 데이터를 추적하고 업데이트해야 하면 state입니다. 깜빡이는 텍스트 컴포넌트를 상상해 보세요. 실제 텍스트는 한 번만 제공되지만(prop), 현재 표시되는지 숨겨지는지는 지속적으로 토글됩니다(state). 이러한 구분 덕분에 React는 데이터 흐름을 예측 가능하고 단방향으로 유지할 수 있습니다.

React는 상태가 해당 상태를 제어해야 하는 가장 가까운 공통 조상에 존재하는 하향식(단방향) 데이터 흐름을 권장합니다. 부모 컴포넌트는 상태를 유지하고 해당 값을 props로 자식 컴포넌트에 전달할 수 있습니다. 자식 컴포넌트는 이러한 값을 렌더링하거나 변환할 수 있지만, 해당 값이 원래 상태에서 온 것인지, 다른 props에서 온 것인지, 아니면 하드코딩된 것인지 알 필요는 없습니다.

이것이 바로 상태를 "로컬" 또는 "캡슐화"한다는 표현을 자주 듣는 이유입니다. 상태를 소유한 컴포넌트만 해당 상태를 변경할 수 있으며, 그 상태에서 파생된 모든 UI는 props를 통해 하위 컴포넌트로 전달됩니다. 상태를 가진 컴포넌트와 상태를 가지지 않는(순수) 컴포넌트를 자유롭게 조합할 수 있으며, 어떤 컴포넌트가 상태를 가지는지 여부는 구현 세부 사항으로 간주되어 시간이 지남에 따라 변경될 수 있습니다.

클래스 구성 요소: 상태 및 생명 주기 관리 (구식 방식)

Hooks가 등장하기 전에는 React에서 상태와 생명주기 메서드를 사용하는 유일한 방법은 ES6 클래스 컴포넌트를 사용하는 것이었습니다. 대부분의 최신 앱은 함수형 컴포넌트를 기반으로 하지만, 여전히 많은 코드베이스에서 클래스 컴포넌트를 볼 수 있고 때로는 유지 관리하기도 하므로 클래스 컴포넌트의 작동 방식을 이해하는 것은 중요합니다.

간단한 함수 구성요소를 변환하려면 Clock 수업에 참여하려면 몇 가지 기계적인 절차를 따라야 합니다. 당신은 그 클래스를 확장하는 클래스를 만듭니다. React.Component, 을 추가하다 render() 이 방법은 함수 본문을 다음과 같이 이동시킵니다. render, 교체 propsthis.props그리고 원래 함수를 삭제합니다. React가 계속 렌더링하는 한 말입니다. <Clock /> 동일한 DOM 노드에 삽입할 경우, 해당 클래스의 단일 인스턴스를 재사용합니다.

클래스에 로컬 상태를 추가한다는 것은 생성자를 정의하고 초기값을 할당하는 것을 의미합니다. this.state 목적. 예를 들어, 당신은 무언가를 옮길 수도 있습니다. date props의 값을 state로 전달하기 위해, 해당 값을 호출하는 생성자를 추가합니다. super(props) 및 세트 this.state = { date: new Date() }그런 다음 모든 사용을 대체합니다. this.props.date in render()this.state.date클래스 컴포넌트에서는 직접 변수에만 값을 할당해야 한다는 점을 기억하세요. this.state 생성자 내부.

라이프사이클 메서드는 React가 컴포넌트의 수명 주기 중 특정 시점에 호출하는 특별한 클래스 메서드입니다. 컴포넌트가 DOM에 처음 삽입될 때(마운트될 때), React는 다음 함수를 호출합니다. componentDidMount()제거될 때(언마운트될 때) React는 다음을 호출합니다. componentWillUnmount()고전적인 시계 예시에서, 타이머를 설정할 때는 다음과 같이 합니다. componentDidMount 그리고 그것을 지워버리세요 componentWillUnmount타이머 ID를 저장합니다. this (예를 들면 this.timerId) 및 호출 this.setState() 매초마다 시간을 업데이트합니다.

해당 시계의 일반적인 수명 주기는 다음과 같습니다. React는 생성자를 호출하여 상태를 초기화한 다음... render() DOM을 생성하려면, componentDidMount() 타이머를 시작하는 위치입니다. 타이머가 작동할 때마다 해당 함수를 호출합니다. setState()업데이트를 대기열에 추가하고 트리거합니다. render() 새로운 상태로. 구성 요소가 제거되면, componentWillUnmount() 타이머를 초기화하여 리소스 누수를 방지합니다.

클래스에서 상태를 올바르게 관리하는 것은 세 가지 중요한 규칙을 준수하는 것을 의미합니다. setState. 돌연변이를 일으켜서는 안 됩니다 this.state 직접 업데이트할 때는 업데이트가 비동기적으로 일괄 처리될 수 있다는 점과 업데이트가 얕게 병합된다는 점(최상위 상태 키만 병합되고 깊게 중첩된 객체는 병합되지 않음)을 기억해야 합니다.

상태를 올바르게 사용하는 방법: 상태 변화, 비동기 업데이트 및 데이터 흐름

초보자들이 가장 혼란스러워하는 부분 중 하나는 바로 이것입니다. setState (그리고 Hook의 해당 기능도 마찬가지로) 상태를 즉시 업데이트하지 않으므로 상태 객체를 제자리에서 변경해서는 안 됩니다. React는 성능 향상을 위해 여러 업데이트를 일괄 처리하는 경우가 많으므로 둘 다 this.state Hooks의 클래스와 상태 변수는 업데이트를 예약한 직후의 최종 상태를 정확히 반영하지 않을 수 있습니다.

상태를 직접 변경하는 것, 예를 들어... this.state.count++ 또는 상태 객체의 속성을 수정하면 React의 변경 감지가 건너뛰어져 컴포넌트가 이전 값에 고정될 수 있습니다. React에서는 상태에 있는 모든 객체를 읽기 전용으로 취급해야 합니다. 기존 객체를 변경하는 대신, 원하는 변경 사항이 담긴 새로운 객체나 배열을 생성하여 상태 업데이트 메서드에 전달해야 합니다.

상태 업데이트는 비동기적으로 이루어질 수 있으므로 이전 상태에서 다음 상태를 계산할 때 주의해야 합니다. 수업 시간에, 예를 들면 다음과 같습니다. this.setState({ count: this.state.count + 1 }) 여러 업데이트를 일괄 처리할 경우 오류가 발생할 수 있습니다. 해결 방법은 함수형 형식을 사용하는 것입니다. this.setState((prevState, props) => ({ count: prevState.count + 1 }))이렇게 하면 최신 상태 스냅샷으로 작업하고 있음을 보장할 수 있습니다.

Hooks에서도 동일한 패턴이 적용됩니다. 값 대신 함수를 사용하여 업데이트 프로그램을 호출할 수 있습니다. 예를 들어, setCount(prev => prev + 1) 새로운 값이 이전 값에 의존하거나, 나중에 실행되는 타이머 또는 이벤트 핸들러 내에서 업데이트가 발생할 수 있는 경우, 카운터를 증가시키는 더 안전한 방법입니다.

상태는 "로컬"하지만, 상태 변화의 효과는 항상 컴포넌트 트리를 따라 전달됩니다. 상태 업데이트로 인해 부모 컴포넌트가 다시 렌더링되면 기본적으로 모든 자식 컴포넌트도 함께 다시 렌더링됩니다. 이러한 하향식 데이터 흐름은 React의 핵심 개념입니다. 즉, 최상위에 하나의 진실의 원천이 있고, 그로부터 UI가 파생되는 방식입니다.

최신 React: Hooks와 함수형 컴포넌트

React 16.8 버전 이후로 Hooks는 함수형 컴포넌트에서 상태와 부작용을 관리하는 표준적인 방법이 되었습니다. 클래스 컴포넌트가 가지고 있던 기능(그리고 그 이상의 기능)을 클래스를 작성하거나 직접 다룰 필요 없이 사용할 수 있게 해줍니다. this 명시적으로 라이프사이클 메소드를 지정하세요. 현대적인 JavaScript로 안정적인 환경 구축.

함수형 컴포넌트는 이제 React 코드베이스에서 기본 스타일입니다. 쓰는 대신 class Example extends React.Component예를 들어 다음과 같이 일반 함수를 정의합니다. function Example() { return <div />; }상태, 부작용 또는 참조가 필요할 때, 다음과 같은 함수를 통해 React에 "연결"합니다. useState, useEffect useRef훅은 클래스 내부에서 사용할 수 없으며 훅 규칙을 준수해야 합니다(항상 컴포넌트 최상위 레벨에서 호출해야 하며, 반복문이나 조건문 내에서는 절대 호출해서는 안 됩니다).

The useState Hook은 함수형 컴포넌트에 로컬 상태를 추가하는 가장 간단한 방법입니다. 이 메서드는 초기값을 인수로 받아 현재 상태값과 설정자(setter)로 이루어진 쌍을 반환합니다. 배열 구조 분해 할당 덕분에 일반적으로 다음과 같이 작성할 수 있습니다. const = useState(0)React는 리렌더링 간에 이 상태를 유지하므로 함수가 여러 번 호출되더라도 상태 값은 기억됩니다.

클래스 상태와 달리, 당신이 유지하는 값은 useState 반드시 사물일 필요는 없습니다. 숫자, 문자열, 부울, 배열 또는 객체 등 데이터에 맞는 어떤 형식이든 저장할 수 있습니다. 여러 개의 독립적인 값이 필요한 경우 다음과 같이 호출할 수 있습니다. useState 여러 번 (예를 들어, age, fruit, todos또는 단일 객체를 저장하고 그 안에 여러 속성을 관리할 수도 있지만, 업데이트할 때는 불변성 규칙을 준수해야 합니다.

반환된 setter 함수를 호출할 때 useState값을 동기적으로 변경하는 것이 아니라, 마치 `request`와 같이 업데이트를 큐에 추가하는 것입니다. setState 수업에서. 다음 렌더링 시 React는 컴포넌트에 새로운 상태 값을 부여합니다. 따라서 동일한 동기 함수 내에서 setter를 호출한 직후에 상태를 읽으면 여전히 이전 값이 표시되는 것입니다.

상태 내 객체 및 중첩 데이터 관리

React를 사용하면 객체와 배열을 포함한 모든 JavaScript 값을 상태에 저장할 수 있지만, 이러한 값은 변경할 수 없는 스냅샷으로 취급해야 합니다. 숫자나 문자열 같은 기본 값은 어차피 변경할 수 없지만, 객체와 배열은 기술적으로 변경할 수 있습니다. 하지만 이러한 값을 변경하면 React의 기본 가정이 깨지고 컴포넌트가 업데이트되지 않는 등의 미묘한 버그가 발생할 수 있습니다.

다음과 같은 상태 객체를 생각해 보세요. { x: 0, y: 0 } 포인터의 위치를 ​​나타냅니다. 당신이 쓰면 position.x = event.clientX 직접 코드를 작성하면 기존 객체가 변경됩니다. React는 setter를 호출하지 않았기 때문에 값이 변경되었는지 알 수 없으므로 다시 렌더링하지 않고 UI가 고정된 상태로 유지됩니다. 올바른 접근 방식은 다음과 같습니다. setPosition({ x: event.clientX, y: event.clientY })이는 완전히 새로운 객체를 생성하고 React에게 해당 객체를 사용하여 렌더링하도록 지시합니다.

새로 생성된 객체의 로컬 변형은 전혀 문제가 없습니다. 예를 들어, 새로운 객체를 단계별로 만들어 나갈 수 있습니다. const next = { ...prev }; next.city = 'Paris'; 만큼 next 해당 객체는 이미 해당 상태에 있지 않았습니다. 객체 변경은 이전 상태 스냅샷에서 이미 사용 중인 객체를 변경할 때만 문제가 됩니다. 앱의 다른 부분이 여전히 이전 값에 의존하고 있을 수 있기 때문입니다.

객체의 일부만 업데이트하고 나머지는 그대로 유지하려면 일반적으로 객체 스프레드 구문을 사용합니다. 폼 상태 객체의 경우 다음과 같습니다. { firstName, lastName, email }입력 변경 사항은 다음과 같은 방법으로 처리할 수 있습니다. setPerson({ ...person, : event.target.value })이 방법은 기존 속성을 복사한 다음 변경된 속성만 덮어씁니다. 스프레드 연산자는 얕은 복사이므로 중첩된 객체에는 더 많은 주의가 필요합니다.

중첩 구조가 깊은 객체는 변경하려는 경로의 모든 레벨에서 새 복사본을 만들어야 하므로 업데이트 코드가 장황해지는 결과를 초래할 수 있습니다. 예를 들어, person.artwork.city 변경 사항이 있으면, 당신은 그렇게 할 것입니다. setPerson({ ...person, artwork: { ...person.artwork, city: 'London' } })내부적으로는 "중첩된 객체"가 있는 것이 아니라, 서로를 가리키는 별개의 객체들이 존재합니다. 따라서 여러 부모 객체가 동일한 자식 객체를 가리키고 있는 상태에서 해당 자식 객체를 변경하면, 여러 곳의 데이터가 동시에 변경되는 것입니다.

중첩된 스프레드시트를 계속 작성해야 한다면, 상태 구조를 단순화하거나 Immer와 같은 도우미 라이브러리를 사용하는 것을 고려해 볼 수 있습니다. Immer를 사용하면 변경 가능한 것처럼 보이는 코드(예: draft.artwork.city = 'London'Immer는 백그라운드에서 변경 불가능한 새 복사본을 생성합니다. React에서는 Immer를 Hooks와 함께 사용할 수 있습니다. useImmer 인사말 use-immer 패키지.

실제 적용 사례: 양식, 타이머 및 사용자 입력

실제 애플리케이션에서는 단순히 카운터의 상태만 관리하는 경우는 드뭅니다. 사용자 입력, API 응답, 그리고 로딩, 오류, 성공과 같은 UI "모드"를 관리합니다. React를 사용할 때 가장 중요한 사고방식의 변화는 "DOM을 조작하는 것"(예: "이 버튼을 비활성화하는 것")이 아니라, 각 상태에서 UI가 어떻게 보여야 하는지를 설명하고 상태를 업데이트하는 것입니다.

예를 들어, 퀴즈나 양식 구성 요소는 다음을 추적할 수 있습니다. status 상태를 전환합니다. 'typing', 'submitting' 'success'. 이 JSX 코드는 제출하는 동안 제출 버튼을 조건부로 비활성화하고, 정답일 경우 성공 메시지를 표시합니다. 명령형 DOM 메서드는 전혀 호출하지 않으며, React는 새로운 상태로 화면을 다시 렌더링하고 시각적 출력을 변경합니다.

폼 필드를 다룰 때 많은 개발자들이 클래스 상태 병합과 폼 필드 병합의 차이점을 처음 접하게 됩니다. useState 행동. 수업 시간에, setState 전달한 객체를 기존 상태 객체에 병합하므로 한 필드를 업데이트해도 다른 필드는 삭제되지 않습니다. useState업데이트는 전체 값을 대체합니다. 상태가 객체이고 해당 함수를 호출하는 경우입니다. setState({ email: '...' })기타 속성(예: password) 수동으로 병합하지 않으면 사라집니다.

이러한 차이점 때문에 여러 개의 기본 상태 변수를 단일 객체로 리팩토링할 때 어려움을 겪는 경우가 많습니다. 만약 당신이 ~에서 ~로 변경한다면 const const const 그런 다음 일반적인 내용을 작성합니다. setForm({ : value })그러면 필드가 하나만 있는 상태 객체가 생성됩니다. 해결 방법은 이전 객체를 펼치는 것입니다. setForm({ ...form, : value }).

더 복잡한 앱에서는 호출을 자주 하지 않게 됩니다. setState (또는 setSomething) 어디에서든 직접. Redux나 MobX 같은 라이브러리를 사용해서 상태를 중앙 집중화하거나, 또는 useReducer 컴포넌트 수준 상태 머신을 위한 훅입니다. 이러한 설정에서도 동일한 불변성 원칙이 적용되며, 유일한 차이점은 업데이트가 수행되는 위치와 방식입니다.

리렌더링, 성능 및 useRef를 사용해야 하는 시점

React에서 상태가 업데이트될 때마다 해당 상태를 소유한 컴포넌트와 기본적으로 모든 자식 컴포넌트가 다시 렌더링됩니다. 이는 의도적인 설계입니다. 재렌더링을 통해 UI는 최신 데이터와 동기화 상태를 유지합니다. 하지만 이는 또한 상태 배치가 부적절할 경우 불필요한 작업이 발생하여 UI가 느려질 수 있음을 의미합니다. 특히 자식 컴포넌트가 비용이 많이 드는 계산을 수행하거나 대규모 목록을 렌더링할 때 이러한 문제가 두드러집니다.

입력 필드와 긴 기술 목록을 보여주는 별도의 구성 요소가 있는 앱을 상상해 보세요. 부모 컴포넌트가 사용자가 입력하는 텍스트와 목록 자체를 모두 소유하는 경우, 스킬 목록이 변경되지 않았더라도 키를 누를 때마다 스킬 목록을 포함한 전체 트리가 다시 렌더링됩니다. 이는 불필요한 작업입니다.

이를 최적화하는 간단한 방법 중 하나는 자식 컴포넌트를 래핑하는 것입니다. React.memo. React.memo `memoize`는 함수 컴포넌트의 결과를 메모이제이션하는 고차 컴포넌트입니다. 렌더링 간에 props가 동일하면 React는 해당 컴포넌트를 다시 렌더링하지 않습니다. 따라서 스킬 목록 컴포넌트는 `memoize`로 감싸면 다음과 같은 결과를 얻을 수 있습니다. React.memo키를 누를 때마다 다시 렌더링되지 않고, 특정 조건이 충족될 때만 다시 렌더링됩니다. skills 소품이 실제로 변경될 때 (예를 들어, 새로운 스킬을 추가할 때).

모든 "상태 유사" 데이터가 여기에 속하는 것은 아닙니다. useState; 때때로 useRef 더 나은 도구입니다. The useRef Hook은 변경 가능한 객체를 제공합니다. current 컴포넌트의 수명 동안 유지되는 속성이지만, 업데이트는 불가능합니다. 지원 리렌더링을 트리거합니다. 따라서 타이머 ID, DOM 요소 참조 또는 추적하고 싶지만 UI에 표시할 필요는 없는 카운터와 같은 항목을 저장하는 데 적합합니다.

간단한 예로, 카운터를 구현하는 방법이 있습니다. useRef 대신 useState. 카운트를 저장하는 경우 countRef.current 이벤트 핸들러에서 해당 값을 증가시키면 내부 값은 변경되지만 React가 다시 렌더링되지 않기 때문에 표시되는 JSX는 업데이트되지 않습니다. 이것이 바로 중요한 차이점입니다. useState UI를 구동하는 값에 사용됩니다. useRef 렌더링에 영향을 주지 않고 유지하고 싶은 값에 사용합니다.

불변성과 직접 돌연변이가 함정인 이유

React의 기본 원칙 중 하나는 상태 업데이트가 불변해야 한다는 것입니다. 그렇다고 해서 아무것도 바꿀 수 없다는 뜻은 아닙니다. 기존 값(특히 객체와 배열)을 수정하는 대신 새 값을 만들고 이전 값은 UI의 기록 스냅샷으로 남겨두는 것을 의미합니다.

상태를 직접 변경하면 사용자의 사고방식과 React가 수행하는 작업 간의 연결이 끊어집니다. 만약 당신이 다음과 같은 일을 한다면 state.count++ 상태 배열에 직접 값을 추가하거나, 업데이트 함수를 호출하지 않으면 React는 변경 사항을 인식하지 못합니다. React가 렌더링 시점을 결정하는 데 사용하는 내부 스냅샷은 그대로 유지되지만, 코드에서는 값이 변경되었다고 생각하게 됩니다. 이로 인해 페이지를 새로고침하면 "저절로 해결되는" 버그가 발생할 수 있습니다.

또한 상태 값을 다른 변수에 할당한 다음 해당 변수를 변경하는 것을 피해야 합니다. 예를 들어, 하는 것 const newCount = count; newCount++; 기본 데이터 유형과 객체 모두에 대해 동일한 기본값을 변경합니다. const copy = stateObj; 복사본을 전혀 만들지 않고, 동일한 객체에 대한 또 다른 참조만 생성합니다. 올바른 복사에는 다음과 같은 패턴이 필요합니다. { ...stateObj } 물건의 경우 또는 배열의 경우.

Redux, MobX(불변성을 위해 구성된 경우), Immer와 같은 라이브러리는 불변성 패턴을 강제하거나 단순화하기 위해 부분적으로 존재합니다. React의 내장 Hooks를 사용하든 상태 관리 라이브러리를 사용하든, 다음과 같은 황금률이 ​​적용됩니다. React가 변경 사항을 감지하고 다시 렌더링하기를 기대한다면 기존 상태를 제자리에서 변경하지 마십시오.

비동기 업데이트, 배치 처리 및 오래된 상태

React 상태에 대한 미묘하지만 중요한 특징 중 하나는 업데이트가 즉시 적용되는 것이 아니라 비동기적으로 예약된다는 점입니다. 전화 할 때 setState 또는 훅 세터처럼 setCountReact는 미래의 특정 시점에 대한 리렌더링을 "대기열"에 추가합니다. 코드를 즉시 차단하여 업데이트 및 리렌더링을 즉시 수행하지 않으므로 React는 여러 업데이트를 일괄 처리하고 성능을 원활하게 유지할 수 있습니다.

이 스케줄링 모델은 동일한 동기 블록 내에서 업데이터를 호출한 직후 상태를 즉시 읽을 수 없다는 것을 의미합니다. 일반적으로 얻게 되는 값은 이전 스냅샷입니다. 따라서 업데이터는 "다음 렌더링 시에는 이 값(또는 이 변환 함수)을 사용하세요"라는 요청으로 생각해야 합니다.

이는 특히 클로저 내부에서 현재 값을 기준으로 상태를 업데이트할 때 중요합니다. setTimeout 또는 구독 콜백. 해당 콜백 함수는 생성 당시의 상태를 캡처합니다. 그런 다음 다음과 같이 하면 됩니다. setCount(count + 1) 타임아웃 내부에서, count 말씀하신 내용은 콜백이 실제로 실행될 때쯤이면 이미 오래된 정보일 수 있습니다.

이 현상을 "오래된 상태" 또는 "오래된 폐쇄"라고 합니다. 예를 들어, 버튼을 클릭하면 타임아웃을 설정하고 1초 후에 상태를 증가시키는 함수를 호출하는 경우, 여러 번 빠르게 클릭하면 상태가 제대로 증가하지 않을 수 있습니다. 각 타임아웃 콜백은 이전 값을 사용하기 때문입니다. count 타임아웃이 예약된 시점을 포착했습니다.

확실한 해결책은 상태 설정자의 함수형 업데이트 형식을 사용하는 것입니다. 대신 setCount(count + 1) 타임아웃 내부에 다음과 같이 작성합니다. setCount(prevCount => prevCount + 1)이제 각 콜백은 타임아웃이 생성될 당시 스코프에 있던 값이 아니라 업데이트가 적용되는 시점의 최신 이전 값을 받습니다. 이렇게 하면 클로저의 다른 동작을 변경하지 않고도 오래된 상태 문제를 해결할 수 있습니다.

React 문서에서는 잘 알려지지 않은 세부 사항 하나를 지적합니다. 함수형 업데이트 메서드가 아무것도 반환하지 않는 경우(undefined), React는 다시 렌더링을 건너뜁니다. 즉, 명시적으로 업데이트를 방지하려는 경우가 아니라면, 업데이트 함수는 항상 다음 상태 값을 반환하거나 이전 값을 재사용해야 합니다. 하지만 표준 방식에서는 업데이트를 방지하는 것이 바람직한 경우는 드뭅니다. useState 용법.

비동기 스케줄링, 배치 처리 및 클로저 동작의 이러한 조합을 이해하는 것은 타임아웃, 간격, 구독 또는 빠른 사용자 상호 작용을 처리하는 앱에서 안정적인 상태 로직을 작성하는 데 매우 중요합니다. 상태 설정자가 업데이트를 즉시 수행하는 대신 예약한다는 사실을 이해하게 되면, 이전에는 무작위로 느껴졌던 버그들이 설명되기 시작할 것입니다.

props와 state, 클래스 라이프사이클과 Hooks, 불변성, 제어형 컴포넌트 등 이 모든 아이디어를 종합해 보면, useRef 시각적이지 않은 값, 메모이제이션, 비동기 업데이트 및 오래된 클로저 등을 고려하면 React UI가 시간이 지남에 따라 어떻게 진화하는지 예측할 수 있는 강력하고 효과적인 모델을 얻을 수 있습니다. 명령형 DOM 변경 방식을 생각하는 대신, 명확한 상태 모델을 설계하고 React가 리렌더링을 처리하도록 하면 애플리케이션이 성장함에 따라 구성 요소를 더 쉽게 이해하고 테스트하고 확장할 수 있습니다.

2025년 자바스크립트 안정 설정
관련 기사 :
현대 자바스크립트의 안정된 상태
관련 게시물: