리액트 컨텍스트는 잠재적인 단점이 몇 개 존재한다. 앱이 복잡해 질수록 리액트 컨텍스트를 사용하면 설정이 복잡해 지고 중첩된 JSX코드로 이어질 수 있다. 테마를 변경하거나 인증 같은 저빈도 업데이트에는 아주 좋지만 데이터가 자주 변경되는 경우에는 좋지 않다.
npm install redux 설치
아래 코드는 지금 redux 사이트에 들어가면 리덕스 툴킷과 리액트 리덕스를 추가하여 리덕스 스토어를 생성하는 다른 방법을 권하기에 권한 방법과 알맞지 않은 코드이지만 기본적인 작동 방식을 이해하기 위해 코드를 작성하고 동작 과정을 기록하려고 한다.
리덕스 기본 실행 코드
const redux = require("redux");
const counterRudecer = (state = { counter: 0 }, action) => {
return {
counter: state.counter + 1,
};
};
const store = redux.createStore(counterRudecer); // 1
const counterSubscriber = () => {
// 저장소 상태가 변경될 때마다 트리거
const latestState = store.getState(); // 최신 상태 스냅샷 제공
console.log(latestState);
};
store.subscribe(counterSubscriber);
store.dispatch({ type: "increment" }); // 액션을 발송
// 리듀서와 구독 함수를 모두 리덕스가 실행
실행과정
액션 발생 -> 리듀서 실행 -> 상태 업데이트 -> 구독 함수 실행 -> UI 업데이트(이 코드는 console 출력)
실제 코드 실행시 출력되는 값은 2가 출력된다. 왜냐하면 처음 저장소가 초기화될 때 리덕스가 리듀서를 처음으로 실행하기 때문이다. state = { counter: 0 } 해준 이유도 처음에 리듀서가 실행될때 초기값이 정의되어 있지 않으면 오류가 발생한다. 따라서 초기에 리듀서 함수가 실행되어counter 값이 1이 되고 dispatch()로 인해 2가 된다.
npm install react-redux 추가 설치
기존에 코드와 다른점은 subscribe함수와 dispatch를 사용하지 않고 외부로 export하였다.
import { createStore } from "redux";
const counterReducer = (state = { counter: 0 }, action) => {
if (action.type === "increment") {
return {
counter: state.counter + 1,
};
}
if (action.type === "decrement") {
return {
counter: state.counter - 1,
};
}
return state;
};
const store = createStore(counterReducer);
export default store;
앞서 정의한 리덕스 저장소를 리액트 앱에서 사용하기 위해서는 다음과 같은 과정이 필요하다.

실제로 store에 저장되있는 데이터를 필요로 하는 컴포넌트에서 사용하는 방법은 아래와 같다.
useSelector
useDispatch
import { useSelector, useDispatch } from "react-redux";
import classes from "./Counter.module.css";
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector((state) => state.counter);
// react-redux는 이 컴포넌트를 위해 리덕스 저장소에 자동으로 구독을 설정
// 따라서 리덕스 저장소에서 데이터가 변경될 때마다 자동으로 업데이트되고 최신 counter를 받는다.
const incrementHandler = () => {
dispatch({ type: "increment" });
};
const decrementHandler = () => {
dispatch({ type: "decrement" });
};
const toggleCounterHandler = () => {};
return (
<main className={classes.counter}>
<h1>Redux - Counter</h1>
<div className={classes.value}>{counter}</div>
<div>
<button onClick={incrementHandler}>Plus</button>
<button onClick={decrementHandler}>Minus</button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;
dispatch 함수로 전달하는 action 객체 type은 사전에 정의 해놓은 redux store의 리듀서 함수의 타입과 일치하는 type을 전달하여 일치하는 조건에 문을 실행하게 함
지금까지는 단순히 dispatch를 이용하여 액션 개체를 전달하여 고정된 값만큼만 counter를 증가시켰는데 액션 객체로 type만 전달하는 것이 아니라 페이로드를 같이 전달함으로써 고정된 값이 아닌 가변적으로 데이터 값을 조작할 수 있다.
페이로드
페이로드는 액션에 포함된 데이터를 의미한다. 리덕스에서 액션은 단순히 무슨 일이 일어났는지 알려주는 역할만 하는 것이 아니라, 액션과 함께 추가적인 데이터를 전달할 수 있습니다. 이러한 추가적인 데이터가 바로 페이로드
counterReducer를 정의한 파일로 이동하여 아래 코드 추가
if (action.type === "increase") {
return {
counter: state.counter + action.amount,
};
}
Counter 컴포넌트에 페이로드를 전달하는 함수추가, amount라는 전달되는 데이터의 이름은 임의적인 것이고 고정된 것이 아니다.
const increaseHandler = () => {
dispatch({ type: "increase", amount: 5 });
};
순수함수 : 순수 함수는 입력값에 대해 항상 동일한 출력값을 반환하고, 외부 상태를 변경하지 않는 함수를 의미합니다. 즉, 함수의 실행 결과는 오직 입력값에만 의존하며, 외부 요인에 영향을 받지 않습니다.