Redux란?
우선 Redux는 React에 종속된 라이브러리가 아닙니다. Javascript의 State를 관리하기 위해 등장한 Library입니다. 이점을 인지한채 글을 읽어주세요.
참고자료 : https://www.valentinog.com/blog/redux/
Redux는 State를 관리해주는 라이브러리이다.
React 개발시에는 아래와 같이 수 많은 State를 관리하게 됩니다.
- 사용자가 볼 data
- network fetching data
- 사용자가 볼 URL
- 사용자가 선택한 Item list
- 어플리케이션 error
물론, 부모 Component
에서 이를 관리할 수도 있습니다. 하지만 정말 많은 State들이 산재 되어 있다보면, 이 많은 데이터를 한곳에서 관리할 수 있는 방법을 찾게 될것입니다. 바로 Redux
가 그 해결방법입니다. Redux
는 State들을 하나의 Store
에서 유지합니다.
1. Redux 구성요소
Redux를 사용함에 있어 이해해야하는 두가지 요소가 있습니다. 바로 Reducer
와 action
입니다.
Reducer
reducer
는 2개의 인자를 전달받는, Javascript 함수입니다. 첫번째 인자는, 현재의 state
, 두번째 인자는 action
입니다. (action은 아래에서 설명드리겠습니다.)
reducer는 action의 type에 따라 이전의 state에 적절한 처리를하여 새로운 state를 return 합니다.
보통의 React에서는 state를 setState()
함수를 통해 변경을합니다. 하지만 Redux에서는 그렇게 하지 못합니다. Redux의 state는 불변
하기 때문입니다. 아래는 간단한 reducer의 예입니다.
const initialState = {
articles: []
};
function rootReducer(state = initialState, action) {
return state;
};
export default rootReducer;
위의 reducer는 사실 아직 아무것도 하지 않습니다. 단지 이해를 돕기 위한 매우 간단한 예입니다. rootReducer
는 state와 action을 인자로 받습니다. 이때 state는 초기값을 가지게 됩니다. 현재로써는, 이게 끝입니다.
Action
Action
은 Redux
의 State가 어떻게 변할지를 알려주는 인자입니다. Redux
에서는 State를 변경할 수 있는 유일한 방법을 store에 action
신호를 보내는 것이라고 규정하고 있습니다.
즉 Action은 application에서 Store에 보내는 일종의 데이터입니다.
Redux의 state는 불변하다고 했는데, 어떻게 state를 변경할 수 있을까요?, 불가능합니다. action을 통해 변화되는 state는 사실, 이전 state를 복사한 값에 새로운 data를 더한 것입니다. 아래는 Action의 간단한 예입니다.
xxxxxxxxxx
{
type: 'ADD_ARTICLE',
payload: { title: 'React Redux Tutorial', id: 1 }
}
type은 state를 어떻게 변화시키는지를 정의합니다. payload는 action의 type에 따라 필요한 state값을 담고 있습니다. 위 예제의 action은 payload에 담긴 데이터를 이용해 ARTICLE를 하나 추가하라는 뜻을 가지고 있습니다.
Reducer와 Action의 간단한예제와, 안좋은 예시
Reducer, Action을 생성하는 간단한 예를 보여드리겠습니다. 우선 위와 같은 디렉토리 구조를 만들어 줍니다.
redux/actions/ActionTypes.js
xxxxxxxxxx
export const ADD_ARTICLE = "ADD_ARTICLE";
redux/actions.index.js
ximport { ADD_ARTICLE } from "./ActionTypes";
export function addArticle(payload) {
return { type : ADD_ARTICLE, payload };
}
index.js에서는 addArticle
이라는 함수를 export합니다. 이 함수는 Action 객체를
생성하여 return합니다.
redux/reducer/index.js (결함이 존재)
xxxxxxxxxx
import { ADD_ARTICLE } from "../actions/ActionTypes";
const initialState = {
articles: []
};
function rootReducer(state = initialState, action) {
if (action.type === ADD_ARTICLE) {
state.articles.push(action.payload);
}
return state;
}
export default rootReducer;
앞서 Reducer에 대한 간단한 예에서 방금 만든 Action을 이용하여 조금 살을 붙혔습니다.
위의 예제는 제법 잘 동작하는듯 보입니다. 하지만 큰 결함이 존재합니다. 바로 immutable의 원칙을 지키지 않은 것입니다. rootReducer에서 ADD_ARTICLE action을 전달 받은 경우 위의 예에서는 articles라는 배열에 바로 payload를 추가하고 있습니다. 어떻게 변경하면 좋을까요?
redux/reducer/index.js (수정 후)
xxxxxxxxxx
import { ADD_ARTICLE } from "../actions/ActionTypes";
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;
위의 변경된 rootReducer에서는 새로운 Object를 생성해, 기존의 state의 값을 먼저 복사해 Object에 할당한 뒤, articles의 내용과, action에 담긴 payload를 합친 새로운 articles의 배열을 생성해 Object에 할당합니다.
|assign(target, ...sources)
javascript의 assign은 target 객체에 sources 객체의 속성들을 복사하여 담습니다.
|concat(...array)
javascript의 concat은 인자로 받은 array객체들을 합쳐서 하나의 새로운 배열을 만들어 반환합니다.
2. Redux Store 메소드들
Redux는 굉장히 작은 Library입니다(2KB). Redux Strore
는 state를 다루기위한 간단한 API들을 제공합니다. 그 중 중요한 메소드들은 아래와 같습니다.
getState
: 현재 어플리케이션의 state에 접근할 때 사용합니다.dispatch
: action을 dispatch할때 사용합니다.subscribe
: state의 변화를 listening할때 사용합니다
Redux 예제
이해도를 높이기 위해 직접 Redux를 사용해보도록 하겠습니다.
준비물
- node.js
프로젝트 구조
src
js
- actions
- constants
- reducers
- store
index.js
과정
- redux 설치
xxxxxxxxxx
npm i redux --save-dev
npm을 이용해 redux를 설치합니다.
- js/store/index.js
xxxxxxxxxx
import { createStore } from "redux";
import rootReducer from "../reducers/index";
const store = createStore(rootReducer);
export default store;
store를 생성하여 export합니다.
- js/reducers/index.js
xxxxxxxxxx
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;
articles배열에 article을 추가하는 행위가 정의된 rootReducer
를 생성하여 export합니다.
- js/constants/action-types.js
xxxxxxxxxx
export const ADD_ARTICLE = "ADD_ARTICLE";
ADD_ARTICLE 상수를 선언해 export합니다.
- js/actions/index.js
xxxxxxxxxx
import { ADD_ARTICLE } from "../constants/action-types";
export function addArticle(payload) {
return {type: ADD_ARTICLE, payload}
};
payload를 인자로 받아, action객체를 만들어 return하는 함수를 선언하여 named export 합니다.
- js/index.js
xxxxxxxxxx
import store from "./store";
import { addArticle } from "./actions";
window.store = store;
window.addArticle = addArticle;
store와 , addArticle 함수를 window 변수에 담아줍니다.
- /index.js
xxxxxxxxxx
import index from './js/';
/js/index.js 를 import 합니다.
테스트
- App 실행
xxxxxxxxxx
npm start
terminal창에서 위 프로젝트의 루트로 이동합니다. 그후 위의 명령어를 입력하여 app을 실행합니다.
- store.getState()
크롬 개발자도구(f12를 눌러 실행)의 console탭으로 이동하여 store.getState()
를 입력합니다. window.store에 우리가 만든 store를 담아 주었기 때문에 위와 같은 결과가 나타납니다.
- store.dispatch(action)
이번엔 store에 dispath를 하여 state에 변화를 주어봅시다. store.dispatch() 메소드의 인자로, addArticle({title: 'Hello Redux Test !!!', id:1})
을 넘겨줍니다. (dispatch의 인자로 action 값이 들어와야 하는데, addArticle(payload)이 action을 만들어 return 해줍니다. )
다시 store.getState() 통해 articles를 확인하면, 방금 생성한 article이 담겨있는 것을 볼 수 있습니다.
'Web > javascript' 카테고리의 다른 글
javascript - [코어자바스크립트] 불변객체와 복사(얕은복사와 깊은복사) (0) | 2019.11.24 |
---|---|
javascript - [코어자바스크립트] javascript의 메모리와 데이터할당 (데이터 할당의 자유와 효율성) (0) | 2019.11.21 |
Javascript - ES6 (` : 템플릿 리터럴) 문자열에 변수 포함시키기 (0) | 2019.09.01 |
javascript - 동기, 비동기 처리과정과 event loop (0) | 2019.05.14 |
javascript - 정규표현식 이란? (0) | 2019.05.08 |