React에서는 상태 관리를 위해 가장 많이 사용하는 훅이 useState입니다. 하지만 상태가 복잡하거나 여러 작업을 체계적으로 관리해야 하는 경우, useReducer 훅이 더 적합할 수 있습니다. 이번 글에서는 useReducer 훅의 사용법, 주요 개념, 그리고 다양한 예제를 통해 실제로 어떻게 활용할 수 있는지 알아보겠습니다.
1. useReducer란 무엇인가?
useReducer는 React의 훅 중 하나로, 컴포넌트 상태를 관리하기 위해 사용됩니다. 이 훅은 Redux와 유사한 방식으로 작동하며, 액션과 리듀서를 기반으로 상태를 업데이트합니다.
useReducer의 기본 구조는 다음과 같습니다:
const [state, dispatch] = useReducer(reducer, initialState);
- reducer: 상태 업데이트 로직을 정의하는 함수입니다. reducer는 현재 상태와 액션을 받아 새로운 상태를 반환합니다.
- initialState: 상태의 초기값입니다.
- state: 현재 상태를 나타냅니다.
- dispatch: 액션을 발생시키는 함수로, 이를 통해 상태 업데이트를 요청합니다.
2. useReducer의 동작 원리
useReducer는 세 가지 주요 요소로 구성됩니다:
2.1 Reducer 함수
리듀서는 순수 함수로, 현재 상태와 액션 객체를 받아 새로운 상태를 반환합니다.
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
throw new Error('Unknown action type');
}
}
2.2 Initial State
리듀서에서 사용할 상태의 초기값을 정의합니다. 이는 컴포넌트가 처음 렌더링될 때 설정됩니다.
const initialState = { count: 0 };
2.3 Dispatch 함수
dispatch는 액션 객체를 리듀서에 전달하여 상태를 업데이트하는 역할을 합니다.
dispatch({ type: 'INCREMENT' });
dispatch({ type: 'DECREMENT' });
3. 간단한 카운터 예제
useReducer를 활용해 간단한 카운터 컴포넌트를 만들어 보겠습니다.
import React, { useReducer } from 'react';
// 1. Reducer 함수 정의
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
throw new Error('Unknown action type');
}
}
// 2. 초기 상태 정의
const initialState = { count: 0 };
function Counter() {
// 3. useReducer 훅 사용
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
</div>
);
}
export default Counter;
위 예제에서 dispatch를 사용해 액션을 보냄으로써 상태를 업데이트합니다.
4. 복잡한 상태 관리 예제: Todo 리스트
useReducer는 상태가 복잡한 경우 특히 유용합니다. 예를 들어, Todo 리스트 관리에 활용해 보겠습니다.
import React, { useReducer, useState } from 'react';
// 1. Reducer 함수 정의
function reducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { id: Date.now(), text: action.payload, completed: false }];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
);
case 'REMOVE_TODO':
return state.filter(todo => todo.id !== action.payload);
default:
throw new Error('Unknown action type');
}
}
// 2. 초기 상태 정의
const initialState = [];
function TodoApp() {
const [state, dispatch] = useReducer(reducer, initialState);
const [text, setText] = useState('');
const handleAddTodo = () => {
dispatch({ type: 'ADD_TODO', payload: text });
setText('');
};
return (
<div>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new todo"
/>
<button onClick={handleAddTodo}>Add</button>
<ul>
{state.map(todo => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
>
{todo.text}
</span>
<button onClick={() => dispatch({ type: 'REMOVE_TODO', payload: todo.id })}>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
export default TodoApp;
이 Todo 앱은 다음 기능을 포함합니다:
- Todo 추가
- Todo 완료/미완료 토글
- Todo 삭제
useReducer를 통해 각 액션을 분리하고, 상태 관리를 체계적으로 처리할 수 있습니다.
5. 언제 useReducer를 사용해야 할까?
useReducer는 다음과 같은 상황에서 유용합니다:
- 상태가 복잡하거나 여러 하위 상태로 나뉘어 있는 경우
- 상태 업데이트 로직이 여러 단계로 이루어진 경우
- 상태 관리 로직을 컴포넌트 외부로 분리하고 싶은 경우
useState와 비교했을 때 useReducer는 더 명확한 상태 관리 패턴을 제공합니다. 특히, 상태 변화가 다양한 액션에 따라 이루어질 때 유용합니다.
6. 결론
useReducer는 React에서 강력한 상태 관리 도구로, 상태가 복잡하거나 여러 작업을 체계적으로 관리해야 할 때 유용합니다. 이번 글에서는 useReducer의 기본 개념과 활용법, 그리고 실제 예제를 통해 어떻게 적용할 수 있는지 알아보았습니다.
프로젝트에서 상태 관리가 복잡해질 경우, useReducer를 활용해 코드의 가독성과 유지보수성을 높여 보세요!
'React' 카테고리의 다른 글
React에서 상태 관리 라이브러리 사용하기 (0) | 2024.12.11 |
---|---|
React에서 상태 관리의 필요성 (0) | 2024.12.11 |
React에서 Context API 활용하기 (0) | 2024.12.11 |
React에서 useContext 훅 사용법 (1) | 2024.12.11 |
React Router의 기본 사용법 (0) | 2024.12.11 |