Redux의 동작원리 및 문제점
Redux란?
애플리케이션의 state를 관리하기 위한 오픈소스 JavaScript 라이브러리
필요성
웹 서비스가 발전하게 되면서 이제는 하나의 Page단위가 아니라 Page안에 여러 컴포넌트들이 하나의 화면에서 구성이 될 수 있도록 발전을 하게 된다.
- 웹 서비스의 발전과 개발 패턴에 대한 내용이 궁금하다면 프론트엔드 아키텍쳐 게시글을 통해 확인할 수 있습니다.
화면을 구성하는 component간에 데이터를 주고받기 위해서는 아래의 조건이 따른다.
Component간의 데이터를 주고받기 위한 조건
- 자식 컴포넌트들 간의 다이렉트 데이터 전달은 불가능하다.
- 자식 컴포넌트들 간의 데이터를 주고 받을 때는 상태를 관리하는 부모 컴포넌트르 통해서 주고 받는다.
위의 그림처럼 자식 컴포넌트에서 state가 변경 된다면 state가 사용되는 컴포넌트는 부모 컴포넌트에서부터 props로 전달받아야한다.
100번째 자식에게 props를 전달하기 위해선 그 사이의 모든 컴포넌트들의 props에 데이터를 하향적으로 전달해주어야 한다.
=> Props drilling이슈 발생
💡 해결: 다른 컴포넌트에서도 사용되는 state를 전역으로 관리하면 하위로 내려줄 필요가 없다.
redux 상태관리 라이브러리를 통해 필요한 state를 모든 컴포넌트에서 접근이 가능하게 되었다.
Props drilling 이슈 해결
동작 원리
Store
Store(스토어)는 상태가 관리되는 오직 하나의 공간이다.
컴포넌트에서 상태 정보가 필요할 때 useSeletor로 스토어에 접근해 state를 사용=> state.data
Action
- Store에 운반할 데이터로, 자바스크립트 객체 형태이다. Action을 Reducer에 전달한다.
Reducers
Action을 전달받아 Store의 상태를 업데이트한다.
Action을 Reducer에 전달하기 위해서는 dispatch() 메소드를 사용한다.
기존의 state를 받지만 그걸 직접 변경하는게 아니라 새로운 state 객체를 만들어 기존 state를 대체함
사용 방법
1. 설치
1
$ npm install redux
2. 적용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const { createStore } = require("redux");
// 초기 state
const initialState = {
compA: "a",
compB: 12,
compC: null
};
// action 생성
const changeCompA = (data) => {
return {
// action
type: "CHANGE_COMP_A",
data
};
};
// reducer 함수
const reducer = (prevState, action) => {
switch (action.type) {
case "CHANGE_COMP_A":
return {
...prevState,
compA: action.data
};
default:
return prevState;
}
};
// store 생성
const store = createStore(reducer, initialState);
// action reducer에 전달
store.dispatch(changeCompA((data = "b"))); // compA를 b로 변경
Redux에서의 비동기(Middleware)
Redux의 action은 객체고 동기적인 일 밖에 수행하지 못한다.
로그인, 게시글 작성등 서버를 거치는 비동기 작업을 위해서 middleware를 사용해야한다.
middleware란?
dispatch와 reducer 사이에 위치하여 어떤 역할을 수행하며 비동기 처리를 할 수 있다.
대표적인 redux middleware인 thunk에 대해 살펴보자.
thunk 구현해보기
1. middleware 정의
1
2
3
4
5
6
const thunkMiddleware = (store) => (dispatch) => (action) => {
if (typeof action === "function") {
return action(store.dispatch, store.getState);
}
return dispatch(action);
};
- redux-thunk middleware에서, 전달받을 action이 함수인 경우는 비동기, 객체인 경우 동기로 dispatch
2. store에 middleware 등록
1
2
const store = createStore(reducer, initialState, enhancer);
const enhancer = applyMiddleware(thunkMiddleware);
3. 예시) setTimeout으로 로그인 요청 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const logIn = () => {
return (dispatch, getState) => {
dispatch(logInRequest(data));
setTimeout(() => {
dispatch(
logInSuccess({
userId: 1,
nickname: "hajeong"
})
);
}, 2000);
};
};
const logInRequest = (data) => {
return {
type: "LOG_IN_REQUEST",
data
};
};
const logInSuccess = (data) => {
return {
type: "LOG_IN_SUCCESS",
data
};
};
store.dispatch(logIn());
dispath로 실행된 action이 함수이므로 비동기로 실행
logInRequest 실행하고 2초뒤에 loginSuccess 실행
redux에서 비동기 상태 관리 라이브러리로
Redux의 불편한 점
Redux로 비동기 데이터를 관리하는 일에는 여전히 불필요하게 느껴지는 반복되는 Boilerplate 코드가 필요
Redux를 사용하여 비동기 데이터를 관리하기 위해서는 상태를 세분화하여 관리하는 경우도 있다.
ex) LOADING | SUCCESS | ERROR 등
이러한 redux의 불편함으로 비동기 상태 관리 라이브러리를 탐색
가장 많이 사용하는 swr과 react-query를 비교해보기로 결정!
swr과 react-query비교 내용은 프론트엔드 아키텍쳐 게시글을 통해 확인할 수 있습니다.
📑 참고 자료
React.js - redux middleware(리덕스 미들웨어, redux-thunk, redux-saga)