1. 개요
React 기반으로 웹 프로젝트를 개발할때, 컴포넌트의 상태 관리를 해야할 경우가 생기는데, 이때 사용하는 것이 Redux입니다. 리액트의 꽃이라고 할 수 있고, 처음 접하는 사람에게 굉장히 러닝 커브가 높은 부분입니다.(저도 그랬습니다.)
(상태 관리 하는 이유 : 리렌더링에 의한 자원 소모를 줄이기 위해서)
사실 Redux가 어렵기도 해서 사용하기 쉽게 라이브러리들이 개발이 되어서 MobX, xstate, recoil, zustand 등 여러가지 라이브러리가 등장하고, Redux를 굳이 사용하지 않아도 위 라이브러리로 간편하게 상태 관리를 할 수 있게 되었습니다.
하지만, 리액트 프로그래밍을 시작하는 사람으로서 어떤 맥락으로 상태관리가 이루어지는지 파악하는 것은 중요하다고 생각합니다.
2. 중요 키워드 및 개념
액션
사용자에 의해서 상태의 변화(ex: 버튼을 눌렀을 경우)가 생길때 이것을 액션이 발생되었다고 말합니다.
아래와 같이 타입과 value로 이루어져있습니다. type은 무조건 들어가야하며 value는 자유롭게 작성해도 됩니다.
{
type: "PUSH_BUTTON"
button: "pushed"
}
// 혹은
{
type: "PUSH_BUTTON"
isToggle: true
}
액션 생성함수
액션을 만드는 함수입니다. 파라미터(data)를 받아와서 액션 객체 형태로 리턴해서, 사용자가 사전에 지정한 액션을 호출할 수 있게 합니다.
export function pushButton(data) {
return {
type: "PUSH_BUTTON",
data
};
}
리듀서
변화를 일으키는 함수입니다. useReducer()와 똑같은 형태를 가지고 있으며, 현재의 상태(state)와 전달받은 액션(action)을 파라미터로 받고, 새로운 상태의 값(return +-1, 증분된 값)을 반환합니다.
// 카운터 기능을 예로 들었을 경우입니다.
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
액션에서 지정한 type을 switch-case로 확인하고, 전달받은 state 파라미터에 +1또는 -1를 추가해서 반환합니다.
이 현재의 state와 반환되는 state가 가장 중요합니다.
먼저 아래 스토어(store)에 대해 확인하겠습니다.
스토어
리덕스에서는 단 하나의 스토어를 만들게 되는데, 이 스토어 내부에는 현재의 앱 상태(state)와 리듀서(reducer)가 들어가 있습니다.
위와 같이 카운터를 만들었다고 가정하고, 리듀서 쪽에서 정의한 state를 다시 확인해보겠습니다.
카운터로 인해 숫자가 늘어나고 줄어드는 텍스트 박스(컴포넌트)가 있으며, 이 상태를 저희는 redux를 이용해 상태 관리를 하려고 합니다.
위 컴포넌트를 redux로 사용할 경우, 아래와 같이 useSelector()라는 함수를 이용해서 스토어(store)에 저장된 현재의 상태(state)를 가져와서 초기화를 해줍니다.
const count = useSelector((state: RootState) => state.counter.count);
return (
<div>{count}</div>
)
저희가 +1버튼을 누르거나 -1버튼을 누를때, 디스패치(dispatch)라는 것을 통해 액션 생성함수를 호출하도록 합니다.
디스패치
디스패치는 스토어(store)에서 제공하는 함수중의 하나로 액션을 발생시켜주는 도구입니다.
const dispatch = useDispatch(); // 디스패치 함수를 가져옵니다
// 각 액션들을 디스패치하는 함수들을 만들어줍니다
const onIncrease = () => {
dispatch(increase());
};
const onDecrease = () => {
dispatch(decrease());
};
위와 같이 디스패치 함수 안에 액션 생성함수를 지정하고, onIncrease()(액션 생성함수)를 호출하면, 액션 생성함수로 인해 액션이 생성되고, 그 다음 리듀서가 호출되며 현재의 상태(텍스트 박스의 숫자가 3이면 3, 혹은 19면 19)에 리듀서에서 지정한 return state +1이 반환되며, 이 값이 스토어(store)에 저장됩니다.
useSelector()는 스토어에 있는 값을 가져다가 텍스트 박스를 갱신시켜주게 되고, 결과적으로 +1이 된 값이 사용자에게 보여 지게 됩니다.
이 흐름!이 가장 중요하고 이 흐름을 이해하셨으면 리덕스를 다 이해하신 거라고 보면 됩니다.
원래라면 전체 코드를 딱 적고, 그 코드에 대한 설명을 하는게 편하긴 하지만, 리덕스 같은 경우는 전반적인 이해와 전체적인 흐름에 대한 이해가 필수적으로 들어가야 했기 때문에 이 글에 대해서는 흐름만 이해할 수 있도록 작성했습니다.
*위 그림에서 Middleware는 크게 중요하지 않기 때문에 그냥 넘어가셔도 무방합니다.
컴포넌트 쪽에서 작성한 전체코드는 아래와 같습니다.
const CounterContainer = (props: CounterContainerProps) => {
// 상태를 조회합니다. 상태를 조회 할 때에는 state 의 타입을 RootState 로 지정해야합니다.
const count = useSelector((state: RootState) => state.counter.count);
const dispatch = useDispatch(); // 디스패치 함수를 가져옵니다
// 각 액션들을 디스패치하는 함수들을 만들어줍니다
const onIncrease = () => {
dispatch(increase());
};
const onDecrease = () => {
dispatch(decrease());
};
const onIncreaseBy = (diff: number) => {
dispatch(increaseBy(diff));
};
return (
<Counter
count={count}
onIncrease={onIncrease}
onDecrease={onDecrease}
onIncreaseBy={onIncreaseBy}
/>
);
};
실제로 Redux를 사용해서 상태관리를 하는 부분에 대한 자세한 코드는 다음 글에서 작성하도록 하겠습니다.
'FrontEnd > React 심화' 카테고리의 다른 글
[React] Redux Toolkit 사용법 (1) | 2022.09.19 |
---|---|
[React] Redux 사용하기(2) - 카운터 구현 (0) | 2022.06.21 |
[React] Immer (produce) (0) | 2022.04.04 |
[React] Async & Await란? (0) | 2022.03.07 |
[React] Promise란? (0) | 2022.03.07 |