: 상태관리 라이브러리
redux 등장배경
MVC패턴 형식으로 state가 변화되면 Model, View, Controller에 이벤트가 발생하고 값이 변화하는 구조였다. 이를 양방향 데이터 흐름이라고 한다. 이는 복잡하며 데이트 흐름이 한번에 판단하기 힘들다. 이러한 단방향 데이터 흐름이 있다.즉 redux이다. redux는 MVC패턴의 단점을 개선하는 것이 목적이다.
리덕스를 사용하면 컴포넌트들의 상태 관련 로직들을 다른 파일들로 분리시켜서 더욱 효율적으로 관리 할 수 있으며, 글로벌 상태 관리를 쉽게 할 수 있다.
Redux의 3가지 원칙
리덕스 사용을 위해서는 다음의 3가지 원칙을 지켜야 한다.
- 단일 스토어 : 하나의 애플리케이션 내부에는 하나의 스토어를 만들어 사용해야 한다. 여러 개의 스토어를 만들어 사용이 가능하지만 상태 관리가 복잡해지기 때문에 권장되지 않는다. 스토어 내부에는 앱 상태와 리듀서 및 내장 함수가 포함되어 있다.
- 읽기 전용 상태 : 리덕스는 읽기 전용 상태이다. 이는 불변성이 유지되어야하기 때문이다. 성능 유지를 위해 데이터 변경시 얕은 비교(shallow equality)를 하기 때문이다.
- 순수한 함수 리듀서 : 리듀서는 변화를 일으켜 새로운 상태를 반환합니다. state와 action을 파라미터로 전달받고 파라미터 외의 값에는 의존하지 않도록 조건에 맞는 순수한 함수로 사용해야 한다.
- 불변성을 지켜야하는 이유
- redux는 이전 state와 바뀐 state를 구분하는 방법이 1) 참조값이 바뀌었는지 확인하고 2)참조값이 바뀌면 state가 바뀌었다고 redux가 인식한다. 따라서, 해당 state가 바뀌면 state를 사용하는 컴포넌트에게 리렌더링을 요청하기 때문에 불변성을 지켜야 한다.
- 직접적으로 state를 변경하면 참조값이 변하지 않아 리렌더링 되지 않는다.
- immer라이브러리를 사용하여 쉽게 불변성을 유지한다.
미들웨어
리덕스에는 미들웨어(Middleware)라는 개념이 존재한다. 리덕스로 상태 관리를 할 때에 useReducer에서 사용하던 개념인 리듀서 함수를 사용한다. 리덕스의 미들웨어를 사용하면 액션 객체가 리듀서에서 처리되기 전에 우리가 원하는 작업들을 수행 할 수 있다.
미들웨어는 주로 비동기 작업을 처리 할 때 많이 사용한다.
리덕스 핵심 키워드
액션 (Action)
상태에 변화가 필요할 때 발생하는 하나의 객체를 의미한다. 액션의 이름인 type 필드를 필수로 포함한 구조로 되어 있다.
{
type: "ADD_TODO",
data: {
id: 0,
text: "리덕스 배우기"
}
}
{
type: "CHANGE_INPUT",
text: "안녕하세요"
}
액션 생성함수 (Action Creator)
액션을 만드는 함수이다. 단순히 파라미터를 받아와서 액션 객체 형태로 만들어 준다.
export function addTodo(data) {
return {
type: "ADD_TODO",
data
};
}
// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({
type: "CHANGE_INPUT",
text
});
이러한 액션 생성함수를 만들어서 사용하는 이유는 나중에 컴포넌트에서 더욱 쉽게 액션을 발생시키기 위함이다. 그래서 보통 함수 앞에 export 키워드를 붙여서 다른 파일에서 불러와 사용한다.
리듀서 (Reducer)
변화를 일으켜 새로운 상태를 반환하는 함수이다. 리듀서는 두가지의 파라미터를 받아온다.
function reducer(state, action) {
// 상태 업데이트 로직
return alteredState;
}
리듀서는 현재의 상태와 전달 받은 액션을 참고하여 새로운 상태를 만들어서 반환한다. useReducer 를 사용할때 작성하는 리듀서와 똑같은 형태를 가지고 있다.
카운터를 위한 리듀서를 작성한다면 다음과 같이 작성할 수 있다.
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
useReducer 에선 일반적으로 default: 부분에 throw new Error('Unhandled Action')과 같이 에러를 발생시키도록 처리하게 한다. 그러나 리덕스의 리듀서에서는 기존 state를 그대로 반환하도록 작성해야한다.
리덕스를 사용 할 때에는 여러개의 리듀서를 만들고 이를 합쳐서 루트 리듀서 (Root Reducer)를 만들 수 있다.
스토어 (Store)
리덕스에서는 한 애플리케이션당 하나의 스토어를 만든다. 스토어 안에는 현재의 앱 상태와 리듀서, 추가적으로 몇가지 내장 함수들이 있다.
디스패치 (dispatch)
디스패치는 스토어의 내장함수 중 하나이다. 디스패치는 액션을 발생시킨다. dispatch 라는 함수에는 액션을 파라미터로 전달한다. dispatch가 호출되면 스토어의 리듀서가 실행되어 새로운 상태를 반환한다.
구독 (subscribe)
구독 또한 스토어의 내장함수 중 하나이다. subscribe 함수는 함수 형태의 값을 파라미터로 받아온다. subscribe 함수에 특정 함수를 전달해주면, 액션이 디스패치 되었을 때 마다 전달해준 함수가 호출된다.
react-redux 라이브러리에서 제공하는 connect 함수 또는 useSelector Hook 을 사용하여 리덕스 스토어의 상태에 구독한다.
🔗 참고
https://kyounghwan01.github.io/blog/React/redux/redux-basic/#reducer-정의