React는 클래스형 컴포넌트에서 함수형 컴포넌트 중심으로 구조가 변화하면서, Hook이라는 개념을 도입했다. 그중에서도
useState
는 가장 기초적이면서도 자주 사용되는 Hook으로, 컴포넌트 내부에 상태(state)를 선언하고 조작할 수 있게 해준다.
이 글에서는 공식 문서의 내용을 바탕으로, useState
의 기본적인 사용법과 동작 원리를 정리해보았다.
Hook은 함수형 컴포넌트에서 상태 값 관리 및 생명주기 처리 같은 React 고유 기능을 사용할 수 있도록 도와주는 함수이다.
Hook 이전에는 상태 관리나 라이프사이클 처리를 위해 클래스형 컴포넌트를 사용해야 했지만, Hook을 통해 더 간결하고 선언적인 코드 작성이 가능해졌다.
공식 문서에 따르면, Hook은 다음과 같은 두 가지 규칙을 반드시 따라야 한다.
Hook은 컴포넌트가 렌더링될 때 호출 순서를 기준으로 상태를 연결하므로, 조건문이나 반복문, 중첩 함수 내부에서는 사용할 수 없다.
// ❌ 잘못된 예시
function MyComponent() {
if (someCondition) {
const [value, setValue] = useState(0); // 조건문 내부 호출 ❌
}
}
// ✅ 올바른 예시
function MyComponent() {
const [value, setValue] = useState(0); // 항상 최상단에서 호출
}
Hook은 일반 함수나 클래스 컴포넌트에서는 사용할 수 없다.
React의 렌더링 흐름과 상태 관리 시스템은 함수형 컴포넌트 기반으로 동작하기 때문에, 다음과 같은 코드에서는 오류가 발생한다.
// ❌ 잘못된 예시 1: 일반 함수에서 Hook 사용 (에러 발생)
function notAComponent() {
const [value, setValue] = useState(0); // 오류: 일반 함수에서는 Hook 사용 불가
return value;
}
// ❌ 잘못된 예시 2: 클래스 컴포넌트에서 Hook 사용 (에러 발생)
class MyComponent extends React.Component {
render() {
const [count, setCount] = useState(0); // 오류: 클래스에서는 Hook 사용 불가
return <div>{count}</div>;
}
}
// ✅ 올바른 예시 1: 함수형 컴포넌트에서 Hook 사용
function FunctionalComponent() {
const [count, setCount] = useState(0);
return (
<div>
<p>카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
// ✅ 올바른 예시 2: 커스텀 Hook 내부에서 사용
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
return [count, setCount];
}
function CustomHookComponent() {
const [count, setCount] = useCounter(0);
return (
<div>
<p>커스텀 훅 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
useState
는 컴포넌트에 상태를 선언하고 해당 값을 변경할 수 있는 가장 기본적인 Hook이다.
호출 시에는 배열을 반환하며, 구조 분해 할당으로 상태값
과 상태를 갱신하는 함수
를 각각 받아 사용할 수 있다.
const [state, setState] = useState(initialValue);
state
: 현재 상태 값setState
: 상태를 변경하는 함수initialValue
: 상태의 초기값import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // 초기값 0
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
useState(0)
은 초기값을 0으로 설정하며, count
는 현재 상태 값을, setCount
는 그 값을 갱신하는 함수이다.setCount(count + 1)
을 호출하면, React는 컴포넌트를 다시 렌더링하며 count
의 변경된 값을 반영한다.setState
함수는 즉시 상태를 바꾸는 것이 아니라, React가 다음 렌더링에서 새로운 값을 반영하도록 예약하는 구조로 동작한다.
setCount(count + 1);
console.log(count); // 여전히 이전 값일 수 있다!
따라서 이전 상태를 기반으로 새로운 상태를 계산할 때는 다음과 같이 함수형 업데이트를 사용하는 것이 안전하다.
setCount(prevCount => prevCount + 1);
초기값으로는 숫자뿐 아니라 문자열, 배열, 객체 등 모든 JavaScript 값을 사용할 수 있다.
const [name, setName] = useState("Alice");
const [todos, setTodos] = useState(["공부하기", "운동하기"]);
const [user, setUser] = useState({ name: "민수", age: 26 });
객체나 배열은 불변성을 유지한 채로 업데이트하는 것이 좋다:
setUser(prev => ({ ...prev, age: prev.age + 1 }));
const [data, setData] = useState(() => heavyComputation());
이렇게 함수로 전달하면, heavyComputation()
은 처음 렌더링 시에만 실행된다. 매 렌더링마다 호출되는 것을 방지할 수 있다.
좋습니다! 글의 마무리 부분에 들어갈 수 있는 useState
핵심 개념 요약을 아래와 같이 정리해드릴게요. 시리즈형 블로그의 흐름을 유지하면서, 독자가 다시 한 번 중요한 내용을 짚고 넘어갈 수 있도록 구성했습니다.
useState
를 제대로 이해하기 위해 기억해야 할 핵심 포인트는 다음과 같다:
컴포넌트 내부 상태를 선언할 수 있게 해주는 Hook이다.
→ 상태 값과 상태 변경 함수를 [state, setState]
형태로 제공한다.
초기값은 숫자, 문자열, 배열, 객체 등 모든 JS 값이 가능하다.
→ 계산 비용이 클 경우 함수로 전달하여 최초 1회만 실행할 수도 있다.
상태 변경은 비동기적이다.
→ setState
이후 바로 값을 읽으면 이전 값일 수 있다.
→ 이전 상태 기반 업데이트는 함수형으로 처리하는 것이 안전하다.
setCount(prev => prev + 1);
Hook은 컴포넌트 최상단에서만 사용해야 하며, 조건문이나 반복문, 일반 함수 내부에서는 사용할 수 없다.
Hook은 함수형 컴포넌트 또는 커스텀 Hook에서만 사용할 수 있다.
→ 클래스 컴포넌트나 일반 함수에서 사용 시 오류가 발생한다.
useState
는 가장 단순하지만 가장 널리 사용되는 Hook으로, 상태 기반 UI를 구현하는 데 있어 출발점이 되는 개념이다.
Hook의 규칙과 useState
의 작동 방식을 정확히 이해하는 것이, 이후 다룰 useEffect
, useRef
등 더 복잡한 Hook들을 배우는 데 튼튼한 기반이 된다.