목차
🧠 React에서 useReducer
를 사용하는 이유와 TypeScript로 구현하는 방법
현대적인 리액트(React) 애플리케이션에서는 컴포넌트의 상태(state)를 관리할 때 useState
훅을 가장 많이 사용하지만, 상태가 복잡해지거나 여러 상태 값들이 서로 연관되어 있을 경우에는 useReducer
훅을 사용하는 것이 더 적합하며, 이러한 패턴은 Redux와 유사한 액션 기반 상태 관리 방식으로도 확장 가능하다는 점에서 큰 장점을 가집니다. 특히 타입 안정성을 확보하고, 유지보수 가능한 코드를 작성하고자 할 때는 TypeScript를 도입하여 상태 객체와 액션 객체에 타입을 명확하게 정의해주는 것이 매우 효과적입니다.
📦 useReducer
기본 구조와 타입스크립트 도입
먼저 타입스크립트와 함께 사용할 때, 상태(state)와 액션(action)에 대해 명확한 타입을 정의하는 것이 핵심입니다.
<pre class="wp-block-syntaxhighlighter-code">import React, { useReducer } from 'react';
// 상태의 타입을 정의
type State = {
count: number;
};
// 액션의 타입을 정의
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number };
// reducer 함수: 상태와 액션을 받아서 새로운 상태를 반환
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: action.payload };
default:
return state;
}
}
// 초기 상태 정의
const initialState: State = { count: 0 };
const Counter: React.FC = () => {
// useReducer 훅 사용
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>현재 카운트 값: {state.count}</h1>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>리셋</button>
</div>
);
};
export default Counter;
</pre>
💡 이 코드에서 주목할 점
State
와Action
타입을 명시함으로써, 코드의 가독성과 유지보수성이 크게 향상됩니다.reducer
함수는 순수 함수로, 동일한 입력이 주어졌을 때 항상 동일한 출력을 보장합니다. 이 점은 테스트를 작성할 때 매우 유리하게 작용합니다.useReducer
를 통해dispatch
함수를 통해 액션을 발생시키고, 이는reducer
함수에 전달되어 상태를 업데이트하게 됩니다.reset
액션은payload
를 통해 초기화할 값을 외부에서 주입받을 수 있도록 설계되어 있어 유연성이 높습니다.
🧩 언제 useReducer
를 사용하면 좋을까?
- 상태 업데이트 로직이 복잡한 경우
예를 들어, 여러 액션 타입이 있고 그에 따른 상태 변화가 많다면useReducer
로 코드 구조를 분리하는 것이 훨씬 명확합니다. - 컴포넌트 내에서 여러 상태 값이 동시에 존재하고, 이 상태들 간에 관계가 있는 경우
useState
를 여러 번 사용하는 것보다useReducer
하나로 통합하는 것이 깔끔합니다. - 상태 변경 로직을 외부로 추출해서 테스트하고 싶을 때
reducer
함수는 순수 함수이기 때문에 별도로 테스트 코드 작성이 쉬우며, 단위 테스트에 유리합니다. - 상태 변경을 명시적이고 예측 가능한 방식으로 처리하고 싶을 때
액션 타입을 문자열로 명확하게 정의하고 switch문으로 처리하면, 디버깅이나 유지보수가 쉬워집니다.