오늘은 Redux에 대해 알아보자
Redux란?
Redux
리덕스란 JavaScript
상태관리 라이브러리이다.
여기서 말하는 상태State
란 간단하게 말하자면 데이터를 말한다.
덧붙이자면 상태는 컴포넌트 내부에서 사용하는 데이터라 할 수 있다.
요즘 웹 사이트를 구성할 때, 사용자에게 보이는 UI들을 하나의 파일로 몽땅 구현하지 않고, 요소요소들을 컴포넌트 단위로 구성하여 조합하는 식으로 만드는 추세이다.
Redux의 3가지 원칙
- 진실은 하나의 근원으로부터
- 동일한 데이터는 항상 같은 곳에서 가져온다.
- 즉,
Store
스토어라는 하나뿐인 데이터 공간을 만들어 State를 관리한다는 의미이다. - 언제든 어디서든
Store
에 저장된 State를 가져다 쓸 수 있다.
- 상태는 읽기 전용이다.
- 리액트에서 setState 메소드를 활용해야만 상태 변경이 가능하다.
- 리덕스에서도 액션이라는 객체를 통해서만 상태를 변경할 수 있다.
- 변화는 순수 함수로 작성되어야 한다.
- 변경하는 순수 함수로만 가능하다.
- 이는 리듀서와 연관되는 개념이다.
- `Store(스토어) - Action(액션) - Reducer(리듀서)
Store, Action, Reducer의 의미와 특징
💡 Store(스토어)
Store는 상태가 관리되는 하나의 공간이다.
- 컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱에서 필요한 상태를 담는다.
- 컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.
💡 Action(액션)
- Action은 앱에서 스토어에 운반할 데이터를 말한다. (주문서와 같다)
- Action은 자바스크립트 객체 형식으로 되어 있다.
💡 Reducer(리듀서)
Action
을Store
에 바로 전달하는 것이 아닌,Reducer
에 전달해야 한다.Reducer
가 주문을 보고Store
의 상태를 업데이트하는 것이다.Action
을Reducer
에 전달하기 위해서는dispatch()
메서드를 사용한다.
reducer
함수는 기존의 state
와 action
을 받아서 새로운 state을 받아서 새로운 state를 만들어내는 함수이다.
이벤트 리스너라고 생각하면 된다. 리듀서는 순수함수로서 몇 가지 규칙을 가진다.
root reducer 살펴보기
리듀서는 하나의 리듀서만이 존재한다. 실무에서는 유지 보수를 위해 여러 개의 리듀서를 만든 뒤에 하나의 루트 리듀서로 병합한다. 실제로 어떤 모습을 가지고 있는지 살펴보자
function user(state = "sungjoong", action) {
return state;
}
function post(state = "post1", action) {
return state;
}
리덕스의 combinedReducer api를 활용해 리듀서들을 합친다.
const combined = combineReducer({ user, post });
이렇게 합쳐진 combined 이라는 루트 리듀서는 아래와 같은 모습을 가집니다.
function combined(state = {}, action) {
return {
user: user(state.user, action),
post: post(state.post, action)
};
}
즉, 우리가 dispatch를 통해 루트 리듀서를 호출하면 각각의 리듀서 함수가 실행되고 action이 존재하는 리듀서만이 새로운 상태를 반환 되는데. 눈 여겨볼 점은 combined 함수의 첫번째 인자로 넘긴 state 이고. 현재 초기값이 {}이기 때문에 각각의 리듀서 함수의 첫번째 인자로 넘겨진 state.a와 state.b는 undefined .
만약 초기 상태를 설정하고 싶다면 createStore api에서 2번째 인자인 preloadedState를 넘겨주면 된다.
💡 Dispatch(디스패치)
Dispatch
는store
의 내장 함수 중 하나로써,action
을 발생시킨다.action
을 파라미터로 전달하고reducer
를 호출한다
💡 Subscribe (구독)
- Subscribe는
sotre
의 내장 함수 중 하나로, 특정 함수를 전달해주면action
이dispatch
되었을 때마다 전달된 함수가 호출된다.
</br >
Action
객체가 dispatch()
메서드에 전달 -> dispatch()
를 통해 Reducer를 호출 -> Reducer
는 새로운 Store를 생성
Redux의 장점
장점
- 순수 함수를 사용해 상태를 예측 가능하게 만든다.
- 유지보수에 용이하다
redux dv tool
이 있어 디버깅에 유리하다.- 비동기를 지원하는
Redux Saga, Redux Thunk
등 다양한 미들웨어가 존재한다.
리덕스는 언제🤔 쓰는게 좋을까?
- 전역 상태가 필요하다고 느껴질 때
- 상태들이 자주 업데이트 될 때
- 상태를 업데이트 하는 로직이 복잡할 때
- 앱이 크고 많은 사람들에 의해 코드가 관리될 때
- 상태가 업데이트되는 시점을 관찰할 필요가 있을 때
Redux Thunk 란?
리덕스 창시자인 댄 아브라모프가 만든 가장 많이 사용되는 비동기 작업 미들웨어다. 이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있다.
미들웨어(middleware)란?
리덕스에서 미들웨어는 주로 비동기 처리 할 때 사용한다. 리듀서에서 발생한 예외를 서버로 전송하는 등의 목적으로 미들웨어를 활용할 수 있다.
미들웨어는 리듀서가 액션을 처리하기 전에 실행되는 함수로 액션과 리듀서 사이의 중간자라고 (이미지참고) 볼 수 있다.
리덕스 미들웨어 라이브러리 redux-thunk
, redux-saga
, redux-observable
, redux-promise-middleware
등을 서치해 사용할 수 있고, 만들어서 사용할 수도 있다.
실제 프로젝트를 작업 할 때에는, 미들웨어를 직접 만들어서 사용하는 경우는 그렇게 많지 않다.
redux-thunk는 결국 액션을 자바스크립트 객체뿐 아닌 함수로 보내는 것인데, 액션으로 함수를 보냈을때 이를 실행하는 것이다. 직접 구현하면서 이를 이해해보자
function createThunkMiddleware() {
return ({dispatch, getState}) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
next(action);
}
}
위 함수에서 리턴되는 함수가 결국 thunk middleware
함수로, 이 함수는 redux store
에서
dispatch
, getState
api를 받고 , 다음 미들웨어로 액션을 보내는 next
를 받는다.
결국 마지막 함수로 우리 dispatch
할 액션을 인자로 받는다.
우리가 함수를 dispatch
하게 되면 action
으로 받고 타입에 따라서 다르게 처리하는 것을 확인 할 수 있다. 따라서 우리가 thunk middleware
에 보낼 함수를 작성할때는 아래와 같이 작성한다.
const getUser = (arg) => (dispatch, getState) => {
// 비동기 로직
getState(something);
dispatch(something);
}
dispatch할 때는 아래와 같이 함수를 실행해서 보내주면 되고, 그럼 dispatch와 getState를 인자로 받는 함수 자체를 thunk middleware로 넘기게 된다.
dispatch(getUser(args));
정리
- 리덕스의 동작원리는 액션, 디스패치, 리듀서로 이루어진다.
- 리듀서는 순수함수로서 side effect를 발생시키면 안된다.
- 비동기 로직을 처리하기 위해 리덕스 미들웨어를 사용한다.
- redux-thunk는 함수를 액션으로 보냈을때 처리하기 위함이다.