[React.js] useState 동작 원리 쉽게 이해하기: React 상태 관리의 핵심
useState
동작 원리 쉽게 이해하기: React 상태 관리의 핵심
React에서 상태 관리는 사용자 인터페이스를 동적으로 만들고 상호작용을 구현하는 데 필수적인 개념이다. 그중에서도 useState
Hook은 컴포넌트 내에서 상태를 관리하는 가장 기본적인 도구이다. 이 글에서는 useState
의 동작 원리를 쉽게 이해하고, 효과적으로 활용하는 방법을 알아본다.
1. useState
의 기본 구조: 상태 변수와 업데이트 함수
useState
Hook을 컴포넌트 내에서 호출하면 배열 형태의 값이 반환된다. 첫 번째 요소는 현재 상태 값을 담고 있는 변수이고, 두 번째 요소는 이 상태 값을 업데이트(마치 setter 함수와 닮았다!)하는 함수이다.
import React, { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0); // count: 현재 상태, setCount: 업데이트 함수
// ...
}
useState()
함수의 인자로 전달하는 값은 상태의 초기값으로 설정된다. 위 예시에서는 count
의 초기값이 0
이다.
2. 상태 업데이트의 핵심: 업데이트 함수 사용
상태를 변경하고자 할 때는 직접 상태 변수에 값을 할당하는 것이 아니라, useState
로부터 반환받은 업데이트 함수(setCount
등)를 사용해야 한다. 이 함수에 새로운 상태 값을 전달하면, React는 내부적으로 상태가 변경되었음을 인지하고 해당 컴포넌트를 다시 렌더링한다.
function handleClick() {
setCount(count + 1); // count 상태를 1 증가시키고 컴포넌트 리렌더링 요청
}
3. 불변성 유지의 중요성: React의 효율적인 업데이트
React는 상태 변화를 감지하고 효율적으로 UI를 업데이트하기 위해 얕은 비교(shallow comparison) 방식을 사용한다. 객체나 배열과 같은 참조 타입의 상태를 직접 수정하면, 이전 상태와 다음 상태의 참조 값이 동일하게 유지되어 React가 변화를 감지하지 못할 수 있다. 따라서 상태를 업데이트할 때는 불변성을 유지하며 새로운 상태 값을 생성해야 한다.
불변성 유지 방법
배열 복사 후 수정: 스프레드 연산자(
...
)나slice()
메서드를 사용하여 배열의 복사본을 만들고, 복사본을 수정한 후 업데이트한다.const [items, setItems] = useState(['사과', '바나나']); function addItem() { setItems([...items, '딸기']); // 스프레드 연산자로 새로운 배열 생성 }
객체 복사 후 수정: 스프레드 연산자를 사용하여 객체의 복사본을 만들고, 복사본의 특정 속성을 수정한 후 업데이트한다.
const [user, setUser] = useState({ name: '홍길동', age: 20 }); function updateAge() { setUser({ ...user, age: user.age + 1 }); // 스프레드 연산자로 새로운 객체 생성 }
4. 리렌더링: 상태 변화에 따른 UI 업데이트
업데이트 함수가 호출되어 상태가 변경되면, React는 해당 컴포넌트와 그 자식 컴포넌트들을 다시 렌더링하여 변경된 상태를 화면에 반영한다. React의 효율적인 업데이트 메커니즘 덕분에 실제로 변경된 부분만 DOM에 반영되어 성능상의 부담을 줄인다.
5. useImmer
활용: 복잡한 상태 관리의 대안
상태의 구조가 깊게 중첩되거나 업데이트 로직이 복잡해질 경우, useImmer
와 같은 Hook을 사용하여 불변성 관리를 더욱 간편하게 처리할 수 있다. Immer를 사용하면 마치 가변적으로 상태를 수정하는 것처럼 코드를 작성할 수 있으며, Immer가 알아서 불변성을 유지하는 새로운 상태를 생성해 준다.
import { useImmer } from 'use-immer';
const [state, updateState] = useImmer({
profile: {
address: {
city: 'Busan'
}
}
});
const updateCity = (newCity) => {
updateState(draft => {
draft.profile.address.city = newCity; // 직접 수정하는 것처럼 작성
});
};
useImmer
의 updateState
함수를 사용하더라도 내부적으로 useState
의 setState
가 호출되어 컴포넌트 리렌더링이 발생한다. 다만, 복잡한 상태 업데이트 로직을 더 직관적으로 작성할 수 있도록 돕는다는 장점이 있다.
결론
useState
는 React 컴포넌트에서 동적인 상태를 관리하는 기본적인 Hook이다. 상태 변수와 업데이트 함수를 통해 상태를 읽고 변경하며, 불변성 유지를 통해 React의 효율적인 렌더링을 가능하게 한다. 상태 관리의 복잡성에 따라 useImmer
와 같은 도구를 활용하여 개발 효율성을 높일 수 있다. useState
의 동작 원리를 정확히 이해하는 것은 효과적인 React 개발의 첫걸음이다.