React 18 useDeferredValue 심화: 검색 자동완성에서 디바운싱을 제거하는 이유

React 18 useDeferredValue 심화: 검색 자동완성에서 디바운싱을 제거하는 이유

⚛️ React useState 훅 완벽 가이드: 상태 관리의 기초부터 실전까지

🎯 리액트 개발의 필수 산소 같은 존재인 useState를 깊이 있게 파헤칩니다.
단순한 숫자 카운팅을 넘어, 객체와 배열을 다루는 법불변성 유지의 중요성까지 확실하게 잡아드립니다.

🤔 왜 일반 변수 대신 State를 써야 할까?


자바스크립트에 익숙하신 분들은 처음에 이런 의문을 가집니다.
"그냥 let count = 0;으로 변수를 선언하고 값을 바꾸면 되지 않나?"
하지만 리액트의 세계에서는 일반 변수가 변경되어도 화면은 꼼짝하지 않습니다.
리액트는 상태(State)가 변경되었을 때만 감지하고 컴포넌트 함수를 다시 실행하여 화면을 새로 그리기(Re-rendering) 때문입니다.
즉, useState는 리액트에게 "이 값이 바뀌면 화면을 갱신해줘!"라고 알리는 통신 장치와 같습니다.

1️⃣ useState 기본 문법 해부하기


가장 기본적인 사용법부터 짚고 넘어가겠습니다.
useState는 배열 구조 분해 할당(Destructuring Assignment) 문법을 주로 사용합니다.

const [state, setState] = useState(initialValue);

📌 구성 요소 3가지

  • 1. state: 현재의 상태 값입니다. (읽기 전용이라고 생각하세요)
  • 2. setState: 상태를 변경하는 '함수'입니다. 이 함수를 통해서만 값을 바꿔야 합니다.
  • 3. initialValue: 상태의 초기값입니다. 숫자, 문자열, 객체 등 모든 타입이 가능합니다.

2️⃣ 객체와 배열 다루기 (불변성의 법칙)


초보자분들이 가장 많이 실수하는 구간이 바로 객체(Object)나 배열(Array)을 업데이트할 때입니다.
리액트는 얕은 비교(Shallow Comparison)를 통해 상태 변화를 감지하기 때문에, 원본을 직접 수정하면 변경 사실을 알아채지 못합니다.

❌ 절대 하면 안 되는 코드:
user.name = "철수"; (직접 할당 금지)
userList.push("영희"); (원본 배열 변형 금지)

따라서 기존 데이터를 복사하여 새로운 객체나 배열을 만든 후, 그곳에 변경 사항을 반영해야 합니다.
이때 가장 유용하게 쓰이는 것이 Spread Operator(전개 연산자, ...) 입니다.

✅ 올바른 상태 업데이트 비교표

데이터 타입 잘못된 방법 올바른 방법 (불변성 유지)
객체 (Object) user.name = 'Kim' setUser({ ...user, name: 'Kim' })
배열 (Array) list.push(item) setList([ ...list, item ])

3️⃣ 고급 기술: 함수형 업데이트 (Functional Update)


setState는 비동기적으로 동작하기 때문에, 연속으로 여러 번 호출할 때 예상치 못한 결과가 나올 수 있습니다.
예를 들어, 카운트를 1씩 세 번 늘리려고 setCount(count + 1)을 세 줄 연속으로 쓰면, 결과는 3이 아니라 1만 증가합니다.
이럴 때는 함수형 업데이트를 사용해야 합니다.

💡 최신 값을 보장하는 마법

setCount(prevCount => prevCount + 1);

이렇게 작성하면 리액트는 이전 상태 값(prevCount)을 인자로 받아와서 정확하게 계산합니다.
현재의 state 값에 의존해서 새로운 값을 계산해야 한다면, 무조건 이 방식을 권장합니다.

📝 요약 및 마무리


오늘 우리는 리액트 상태 관리의 핵심인 useState에 대해 깊이 알아보았습니다.
단순히 값을 바꾸는 것을 넘어, 객체와 배열의 불변성을 지키고 함수형 업데이트로 안전하게 값을 제어하는 것이 진정한 실력입니다.
처음에는 ... 문법이나 비동기 처리가 낯설 수 있지만, 계속 사용하다 보면 숨 쉬듯이 자연스러워질 것입니다.

댓글 쓰기