복잡한 상태를 위한 useReducer
reducer 함수로 상태 전환을 모델링하고 액션 객체를 dispatch하며, 상태 로직이 복잡해지면 useState보다 useReducer를 우선 사용합니다.
복잡한 상태를 위한 useReducer은(는) CoddyKit의 무료 Frontend Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Frontend Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
useState만으로 충분하지 않을 때
상태 전환에 복잡한 로직이나 서로 관련된 여러 값이 포함되거나, 새 상태가 이전 상태에 단순하지 않은 방식으로 의존한다면 여러 useState 호출보다 useReducer를 사용하는 편이 더 깔끔합니다.
리듀서 함수 패턴
리듀서는 순수 함수입니다: (state, action) => newState. 현재 상태와 동작 객체를 받아 다음 상태를 반환합니다. 상태를 직접 변경하지 말고 항상 새 객체를 반환하세요.
type Action =
| { type: 'increment' }
| { type: 'decrement' }
| { type: 'reset'; payload: number };
function counterReducer(state: number, action: Action): number {
switch (action.type) {
case 'increment': return state + 1;
case 'decrement': return state - 1;
case 'reset': return action.payload;
default: return state;
}
}useReducer 문법
useReducer(reducer, initialState)는 현재 상태와 dispatch 함수를 반환합니다. 상태 전환을 실행하려면 동작 객체와 함께 dispatch를 호출하세요.
function Counter() {
const [count, dispatch] = useReducer(counterReducer, 0);
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset', payload: 0 })}>Reset</button>
</div>
);
}타입이 지정된 동작 — 판별된 유니온
동작에 TypeScript 판별 유니온 타입을 사용하면 리듀서의 switch에서 모든 경우를 빠짐없이 검사할 수 있습니다.
type CartAction =
| { type: 'ADD_ITEM'; item: CartItem }
| { type: 'REMOVE_ITEM'; id: string }
| { type: 'SET_QUANTITY'; id: string; qty: number }
| { type: 'CLEAR' };복잡한 상태 예시: 장바구니
상품 추가, 삭제, 수량 업데이트 작업이 있는 장바구니는 useReducer를 사용하기에 완벽한 사례입니다.
interface CartState {
items: CartItem[];
total: number;
}
function cartReducer(state: CartState, action: CartAction): CartState {
switch (action.type) {
case 'ADD_ITEM': {
const exists = state.items.find(i => i.id === action.item.id);
if (exists) {
const items = state.items.map(i =>
i.id === action.item.id ? { ...i, qty: i.qty + 1 } : i
);
return { ...state, items, total: calcTotal(items) };
}
const items = [...state.items, { ...action.item, qty: 1 }];
return { ...state, items, total: calcTotal(items) };
}
case 'CLEAR': return { items: [], total: 0 };
default: return state;
}
}dispatch는 안정적입니다
dispatch 함수는 안정적이므로 렌더링 사이에 변경되지 않습니다. 다시 실행되지 않으면서 prop으로 전달하거나 useEffect 의존성으로 사용해도 안전합니다.
useReducer와 useContext 결합
강력한 패턴은 컨텍스트가 상태와 dispatch를 모두 제공하도록 하는 것입니다. 그러면 모든 소비자가 prop을 여러 단계로 전달하지 않고도 상태를 읽거나 동작을 디스패치할 수 있습니다.
const CartContext = createContext<{ state: CartState; dispatch: Dispatch<CartAction> } | null>(null);
function CartProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(cartReducer, { items: [], total: 0 });
return <CartContext.Provider value={{ state, dispatch }}>{children}</CartContext.Provider>;
}useReducer와 useState 비교
다음과 같은 경우 useReducer를 선택하세요. 상태에 여러 하위 값이 있거나, 다음 상태가 복잡한 방식으로 이전 상태에 의존하거나, 전환 종류가 많고 명시적인 이름이 필요하거나, 컴포넌트와 상태 로직을 분리해 테스트하고 싶은 경우입니다.
리듀서를 독립적으로 테스트하기
리듀서는 순수 함수이므로 어떤 컴포넌트도 렌더링하지 않고 쉽게 테스트할 수 있습니다.
test('increment action increases count', () => {
expect(counterReducer(0, { type: 'increment' })).toBe(1);
expect(counterReducer(5, { type: 'increment' })).toBe(6);
});
test('reset action sets count to payload', () => {
expect(counterReducer(42, { type: 'reset', payload: 0 })).toBe(0);
});불변 업데이트를 위한 Immer
immer 라이브러리를 사용하면 상태를 직접 변경하는 것처럼 보이는 코드를 작성하면서도 새로운 불변 상태를 만들 수 있습니다. 리듀서에서 복잡하게 중첩된 객체를 업데이트할 때 유용합니다.
빠른 확인
리듀서의 함수 시그니처는 무엇인가요?
복습: useReducer
(state, action) => newState — 순수하며 상태를 직접 변경하지 않습니다. useReducer(reducer, initial)은 [state, dispatch]를 반환합니다. 안전성을 위해 판별 유니온 동작 타입을 사용하세요. dispatch는 안정적입니다. 전환이 많은 복잡한 상태에 적합합니다. 전역 상태 관리를 위해 useContext와 결합하세요. 순수 함수인 리듀서를 독립적으로 테스트하세요.
자주 묻는 질문
“복잡한 상태를 위한 useReducer” 강의는 무료인가요?
네 — “복잡한 상태를 위한 useReducer” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Frontend Academy 강의 전체를 잠금 해제할 수 있습니다. Frontend Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“복잡한 상태를 위한 useReducer”에서 뭘 배우나요?
reducer 함수로 상태 전환을 모델링하고 액션 객체를 dispatch하며, 상태 로직이 복잡해지면 useState보다 useReducer를 우선 사용합니다. 브라우저에서 직접 실행하는 실습 코드로 Frontend Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Frontend Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Frontend Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“복잡한 상태를 위한 useReducer” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Frontend Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Frontend Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 전역 상태를 위한 useContext
- 복잡한 상태를 위한 useReducer
- 성능을 위한 useMemo와 useCallback
- 사용자 지정 훅: 재사용 가능한 로직 추출