이전 회사에서 상태 관리 라이브러리로Recoil
을 사용했었습니다. 깊게 사용하기보단 주로 전역 변수의 상태 관리를 위해 사용하였었는데 그 때 Redux
가 아닌 Recoil
을 사용했던 이유는 좀 더 사용방법이 간단하고, 단기간에 학습하여 적용하기가 쉽다는 점 때문이었죠. 그렇게 Recoil
을 사용하였지만, Redux
에 대한 궁금증이 계속 있었기에 한 번 학습해보면서 내용을 정리해보기로 하였습니다.
React에서 우리가 컴포넌트들을 작성하다보면 부모-자식 간에 props를 주고 받을 때가 있습니다. 이 props는 프로젝트가 커질수록 컴포넌트의 계단이 많아지고, 컴포넌트 간의 props를 전달하는 동작이 늘어나게 됩니다. props가 수많은 계단을 거치면서 불필요한 리렌더링을 일으키고, 코드의 가독성을 떨어뜨려 유지보수에 어려움이 발생할 수 있습니다.
이러한 문제를 상태 관리 라이브러리가 상태(state)를 전역으로 관리하여 계단을 거치지 않고 필요한 컴포넌트에서 바로 호출할 수 있도록 해줍니다.
리덕스란 자바스크립트 앱의 상태 관리 라이브러리입니다. 리덕스는 Javascript, React 또는 Angular와 같은 프레임워크에서도 사용할 수 있다보니 현재 상태 관리 라이브러리중에 가장 인기가 많습니다. 리덕스를 사용하면 컴포넌트들의 상태 관련 로직들을 다른 파일들로 분리시켜 더욱 효율적으로 관리할 수 있습니다.
우리가 상태에 어떤 변화를 주어야 할 때 액션을 발생시킵니다. 액션은 객체로 표현되며 다음과 같은 형식으로 되어있습니다.
{
type: "Add_ToDo"
내용: {
할일: "리덕스 배운거 정리하기"
일시: new Date()
}
}
type
은 필수적으로 갖고 있어야하며, 그 외에는 저장할 정보를 넣어줍니다. 이러한 액션을 나중에 컴포넌트에서 쉽게 액션을 발생시키기 위해 따로 액션 생성함수를 만들어서 사용합니다. 반드시 액션 생성함수를 사용해야하는 것은 아닙니다.
export const addToDo = text => {
return {
type: "ADD_ToDo",
할일: text
};
};
export function deleteToDo (id) => {
return {
type: "DELETE_ToDo",
id
};
};
리듀서는 변화를 일으키는 함수입니다. 리듀서는 두 가지의 파라미터를 받아오는데 다음과 같은 형태입니다.
function reducer(state, action) {
if (action.type === "ADD_ToDo") {
return (... addState(action.할일));
} else if (action.type === "DELETE_ToDo") {
return (... deleteState(action.id));
}
return state
}
이해하기 조금 쉽게 if, else
를 사용하여 작성해보았습니다만 받아오는 type
에 따라 다른 행동을 해야한다는 조건에 알맞는 문법이 있습니다. 바로 switch
를 사용하면 코드가 좀 더 보기 좋아질겁니다.
function reducer(state = [], action) {
switch (action.type) {
case "ADD_ToDo":
return (... addState(action.할일));
case "DELETE_ToDo":
return (... deleteState(action.id));
default:
return state;
}
훨씬 보기 편하지 않나요? 보통 Redux
에선 이렇게 리듀서를 구성합니다. 보통 리덕스에서는 여러개의 리듀서(Sub Reducer)를 만들고 이를 합쳐서 루트 리듀서(Root Reducer)를 만들 수 있습니다.
리덕스에서는 한 앱당 하나의 스토어를 만듭니다. 스토어에는 현재 앱 상태와 리듀서가 들어가고, 몇 가지 내장 함수를 포함하고 있습니다.
import createStore from "redux";
const store = createStore(reducer);
createStore
를 import하면 줄이 그어져서 이렇게 나올겁니다. 그냥 사용해도 문제가 없지만 보기 싫으시다면 이렇게 코드를 작성하면 됩니다.createStore
import { legacy_createStore as createStore } from "redux";
createStore
를 사용할 순 있지만 학습 목적 외에는 @reduxjs/toolkit
의 configureStore
를 사용하는 것을 권장한다고 하네요. 이 부분은 뒤에 toolkit
내용에서 다뤄보도록 하겠습니다.
디스패치는 스토어의 내장함수입니다. 액션을 발생시키는 함수라고 생각하시면 되는데 dispatch(action)
파라미터로 액션을 전달합니다. 호출하게 되면 스토어는 리듀서 함수를 실행시켜 해당 액션의 로직을 참고하여 새로운 상태를 만들게됩니다.
store.dispatch(addTodo("오늘은 쉬자!"));
//or
import { useDispatch } from "react-redux"
const dispatch = useDispatch();
dispatch(addTodo("오늘은 쉬자!"));
스토어의 내장함수이며 구독은 함수 형태의 값을 파라미터로 받습니다. 구독 함수에 특정 함수를 전달해주면 액션이 디스패치 되었을 때마다 전달해준 함수가 호출됩니다.
const showState = () => {
// 현재 스토어의 상태를 가져오는 method
const state = store.getState();
console.log(state);
};
// 액션이 dispatch 될 때마다 showState 함수 실행
store.subscribe(showState);
리액트 환경에서는 많이 사용되지는 않고, react-redux
라이브러리에서 제공되는 connect
, useSelector
로 스토어 상태를 구독합니다.
state
를 직접 수정하지 않고, setState
를 사용하는 것처럼 state
를 업데이트 해야합니다.state.push
와 같은 mutate
함수 사용 금지shallow equality
검사를 하기 때문이다.지금까지는 리덕스의 가장 기본적인 이론을 중점으로 글을 정리하였습니다. 리덕스의 핵심 키워드와 키워드의 가장 기본적인 코드의 형태, 그리고 우리가 리덕스를 사용하면서 반드시 지켜야 할 것들을 알 수 있었는데요. 다음 글에서는 제가 학습했던 강의를 토대로 코드 위주로 사용법을 한 번 알아보고자 합니다. 그럼 다음 글에서 뵙겠습니다 감사합니다!
.
.
.
.
.