일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- datamodel
- database
- entityrelational
- SW
- storagemanger
- designpatternn
- Implementation
- db
- kafka
- DP
- json #typescript
- Today
- Total
i.am.developer
React Redux 공부 본문
https://www.valentinog.com/blog/redux/#read-this-redux-where-are-we-now
Redux란 무엇?
REDUX에서 쓰이는 것들 Action, Reducer, Middleware 가 있다. 근데 그것들을 묶어주는 것이 바로 Store이다. Redux에서 Store는 magic이며 모든 application의 state를 갖고 있는 것이다.
import { createStore } from "redux";
import rootReducer from "../reducers/index";
const store = createStore(rootReducer);
export default store;
보다시피 store는 createStore의 반환값이다. createStore는 Redux 라이브러리의 함수인데 reducer를 첫번째 인자로 받는다.
createStore에 Initial State를 전달할 수도 있다. 이경우는 server side render이나 state preloading에 유용하다. 그러나 지금은 initial state가 없다고 가정하자.
여기서 제일 중요한 것은 Redux의 state는 reducers에서 나온다는 것이다. Reducers produce the state of your application
Redux Reducer에 대해 알아가기
reducer가 머임? redux reducer는 그냥 javascript function이다. 두개의 parameters를 받는다. current state and action (지금은 action에 대해 신경쓰지 말자)
일반적으로 React component에서 local state는 그 안에서 바뀔 수 있다.(might be mutated in place). Redux에서는 그러면 안된다! Redux의 세번째 원칙(만든사람이 세운거)이 state는 immutable이고 in place에서 바뀌면 안된다는 것이다.
다른 말로, reducer는 반드시 순수(pure)해야한다. pure한 function은 반드시 주어진 입력에 대해 같은 output을 반환해야한다. Reducer라는 용어가 전문적처럼 보여도 Reducer를 추측하는건 그렇게 어렵지 않다.
Redux action과 named constants에 대해 알아가기
Redux reducers는 의심할 여지 없이 Redux에서 가장 중요한 개념이다. Reducers는 application의 state를 만듭니다. 그러나 Reducer가 언제 next state를 만드는지 어떻게 알 수 있을까요?
Redux의 두번째 원칙은 state를 바꾸는 단 한가지 방법이 store에 signal을 보내는 것입니다. 그리고 그 signal이 action입니다. 그래서 "dispatching an action"의 뜻이 store에 signal을 보낸다를 의미하는 겁니다.
헷갈리나요? 다행인 것은 Redux의 action은 그냥 Javascript Object에 불과하다는 겁니다. Action은 아래와 같이 생겼어요.
{
type: 'ADD_ARTICLE',
payload: { title: 'React Redux Tutorial', id: 1 }
}
보시다시피 Javascript Object이고 두개의 properties를 갖고 있습니다. : type, payload
Type 속성은 어떻게 state가 바껴야하는지를 알려주고 반드시 필요합니다. (The type property drives how the state should change and it's always required by Redux.)
Payload 속성은 어떻게 바껴야하는지에 대해 대신 알려주고, 만약 state에 새로운 데이터를 저장하는게 아니라면 빠져도 됩니다. (The payload property instead describes what should change, and might be omitted if you don't have new data to save in the store.)
Redux Best Practice로서 우리는 모든 Action을 함수 안에 모아놓습니다. 그러면 object 생성이 추상화됩니다. 그런 함수를 Action Creator라고 부릅니다. 어떻게 Action Creator를 만드는지 한번 살펴봅시다.
// src/js/actions/index.js
export function addArticle(payload) {
return { type: "ADD_ARTICLE", payload }
};
Type 속성이 String인걸 알 수 있습니다. String은 오타나 중복에 취약합니다. 그러므로 Action을 constant로 선언하는 것이 좋습니다. (마치 string Enum 처럼요)
// src/js/constants/action-types.js
export const ADD_ARTICLE = "ADD_ARTICLE";
그러면 이제 해당 Action의 Type을 Enum처럼 사용할 수 있게 됩니다.
// src/js/actions/index.js
import { ADD_ARTICLE } from "../constants/action-types";
export function addArticle(payload) {
return { type: ADD_ARTICLE, payload };
}
이쯤되면 여러개의 파일을 동시에 다루기 시작하면서 헷갈리다고 생각할 수 있어요. Redux를 혐오하는 사람들은 이런 이유로 싫어하기도 합니다. 그렇다면 Redux의 Duck 패턴을 따라가도 좋습니다. 하나의 관심사에 대해선 한 파일에 넣는 구조입니다.
이제 Redux application에 대해 한걸음 다가섰습니다! 그러나 먼저 우리는 Reducer가 새로운 Action에도 유연하도록 Reducer를 바꿀 필요가 있습니다.
Refactoring Reducer
마지막 섹션에서 "Reducer가 어떻게 다음 state를 생성하는지 알 수 있을까"에 대해 질문을 던졌습니다., 여기서의 핵심은 Redux Store입니다. Action이 dispatch되면 store는 message를 reducer에게 전달해줍니다.
이 시점에서 Reducer는 "action의 type을 함 봐야겠군?" 이라 하면서 Action의 Type에 따라 Reducer가 다음 상태를 생성합니다. 그리고 action payload를 새로운 state에 적용하게 됩니다.
// src/js/reducers/index.js
import { ADD_ARTICLE } from "../constants/action-types";
const initialState = {
articles: []
};
function rootReducer(state = initialState, action) {
if (action.type === ADD_ARTICLE) {
state.articles.push(action.payload);
}
return state;
}
export default rootReducer;
위와 같이 initial state에 action 의 payload를 push하면 됩니다....가 아니라!! 저건 잘못된 예입니다. Reducer의 제 3원칙, state는 immutable이다를 어기게 됩니다. Array.prototype.push
는 impure function입니다. 원본 array를 수정하게 되죠!(not immutable). 게다가 저 코드는 Initial State를 in place에서 수정하고 있습니다!
그렇다면 고쳐야겠지요. pure한 function으로 immutable하게, in place에서 수정하지 않게! 첫째로 Object.assign
으로 javascript object를 반환할 수 있습니다. 이 경우에는 original state를 변경하지 않습니다. 그 다음에 Array.prototype.concat
을 사용해서 original array를 보존합니다.
import { ADD_ARTICLE } from "../constants/action-types";
const initialState = {
articles: []
};
function rootReducer(state = initialState, action) {
if (action.type === ADD_ARTICLE) {
return Object.assign({}, state, {
articles: state.articles.concat(action.payload)
});
}
return state;
}
export default rootReducer;
이제 Initial state는 깨끗하게 남고, 결과 state는 initial state의 복사본이 됩니다. Redux에서 Mutation을 피하기 위해선 아래 두가지를 기억해주세요.
- arrays를 변경하기 위해선 concat, slice, spread 연산자를 사용해라
- object는
Object.assgin
이나 object spread 연산자를 사용해라.
이런 Immutability가 boilerplate처럼 느껴진다면 redux starter kit에서 이 문제를 해결해 놓았습니다. 필요하시다면 보시는 것을 추천드려요.
다음 섹션에서는 브라우저 console에서 redux를 사용해보겠습니다.