[Redux] 기초
Redux
A predictable state container for JavaScript Apps.
Redux는 자바스크립트 앱을 위한 예측 가능한 상태의 저장소를 제공한다. Redux는 체계적이고 편리한 글로벌 상태 관리를 지원한다. Redux는 React에 종속된 라이브러리는 아니지만 React의 특징 때문에 같이 사용되는 경우가 많다.
Redux를 사용하는 이유
React에서 상태는 컴포넌트 안에서 관리된다. 상태의 영향을 받는 컴포넌트가 다수 존재하면, 공통의 조상 컴포넌트에 공유할 상태를 위치시키고 상태 및 상태 변경 함수를 props로 전달한다. 컴포넌트들이 단순한 구조를 가진 경우에는 상관 없으나, 프로젝트 규모가 커지면 상태 변경 로직의 복잡성을 유발하고 상태를 사용하지 않는 중간 컴포넌트가 상태를 넘겨받는 비효율적인 전달이 발생할 수 있다. Redux를 사용하면 상태 변경 로직을 컴포넌트로부터 분리할 수 있어서 복잡성을 낮추고 보다 쉽게 상태 관리를 할 수 있다.
Redux의 3가지 원칙
1. Single source of truth
Redux는 데이터 저장을 위해 Store를 제공한다. Store는 애플리케이션 내에서 상태가 관리되는 오직 하나의 공간으로써, 컴포넌트는 상태가 필요하면 Store에 접근해서 가져온다. 모든 컴포넌트에서 동일한 데이터를 가져오는 것이 보장된다.
2. State is read-only
React와 똑같이 Redux에서도 상태는 읽기 전용이다. 외부에서 상태에 직접적으로 접근하여 값을 변경하면 안된다. 상태를 변경할 때는 Action이라는 자바스크립트 객체를 이용한다. Action은 변경될 상태 정보를 가졌으며 Store에 해당 데이터를 운반해주는 역할을 한다.
3. Changes are made with pure functions
상태를 변경하는 함수 Reducer는 부수 효과가 없는 순수 함수여야 한다. Reducer는 현재 상태와 Action을 이용해 다음 상태를 만든다.
Redux의 개념
버튼으로 숫자를 변경시키는 간단한 카운터 예제로 Redux를 설명한다.
Action
Action(액션)은 상태를 변화시킬 때 생성하는 객체이다. Action 객체는 type
프로퍼티를 반드시 가져야 하고 변경될 상태에 관한 데이터(payload)를 가진다.
Action creator(액션 생성 함수)는 파라미터를 받아 Action을 생성하는 함수이다.
// Action type 정의
const ADD = 'ADD'; // 상수로 정의하여 실수를 방지
// Action creator 정의
const addToDo = (data) => {
return { // Action 객체
type: ADD,
data
};
};
// Action 객체 초기값
const initialState = {
number: 0
};
Reducer
Reducer(리듀서)는 변화를 일으키는 함수로 유일하게 상태를 변화시킬 수 있다. dispatch에 의해 호출되면 현재 상태와 전달 받은 액션의 타입과 payload를 참고해서 새로운 상태를 만들어 반환한다. 상태는 불변성을 유지해야 한다.
const reducer = (state = initialState, action) => {
let curCount = state.count;
switch (action.type) { // switch 문으로 분기
case 'ADD':
curCount += action.data;
return { count: curCount }; // 새로운 주소값을 가진 객체를 생성해 반환
default:
return state;
}
};
Store
Store(스토어)는 애플리케이션의 유일한 상태 저장 공간이다. 생성할 때 Reducer를 인자로 받는다. Store 안에는 현재의 앱 상태와 Reducer, 몇가지 내장 함수들이 있다.
const store = createStore(reducer, initialState?);
dispatch
dispatch(디스패치)는 Store의 내장 함수로 Action을 파라미터로 전달받아 Reducer를 호출한다. Reducer에서 상태가 변경되므로, dispatch를 호출하는 것이 상태 변경의 시작이다.
const dispatchAdd = (data) => { // Event handler
store.dispatch(addToDo(data)); // Action creator가 반환한 Action을 인자로 받음
};
addBtn.addEventListener('click', () => dispatchAdd(5));
subscribe
subscribe(구독)은 Store의 내장 함수로 상태가 변경되었을 때마다 전달된 콜백 함수를 호출한다. getState()
메소드로 현재 상태 값을 가져올 수 있다.
const onChange = () => {
numberSpan.innerText = store.getState().count; // 현재 상태를 가져와 DOM 요소 수정
};
store.subscribe(onChange);
Redux의 흐름
단방향 데이터 흐름을 위한 Redux의 동작 방식이다. Action 객체는 Dispatch에 전달되고, Dispatch는 Reducer를 호출해서 새로운 state 값을 생성한다.
Redux의 장점
- 예측 가능한 상태
Reducer가 순수 함수이므로 변경될 상태를 예측할 수 있다. - 유지보수 편리
기존 React처럼 상태가 복잡하게 얽혀있을 때 상태를 전달하는 모든 컴포넌트를 수정하지 않아도 된다. - 디버깅과 테스트 편리
Action과 state 로그를 기록하여 Redux dev tool을 이용해 상태가 변화되는 과정을 추적할 수 있다.
React Redux
React에서 전역 상태 관리를 위해 Redux를 사용하려면 react-redux
패키지를 설치해서 불러오면 된다. Redux를 사용하는 애플리케이션은 일반적으로 Actions, Reducer, Store를 디렉터리로 나누어 작성한다.
Store의 인스턴스 store
를 선언하고, Provider
컴포넌트로 앱과 store
를 연동할 수 있다. 이제 Provider
의 하위 컴포넌트에서 Store에 접근이 가능하다.
store/store.js
import { compose, createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/index';
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) // Redux devtool extension 사용
: compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));
// redux-thunk 미들웨어 사용
index.js
...
import store from './store/store';
import { Provider } from 'react-redux';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Action과 여러 Reducer를 정의했다면 combineReducers
를 사용해 Reducer를 하나로 합칠 수 있다.
reducers/index.js
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
// sub reducer 1, sub reducer 2, ...
});
Hooks: useSelector, useDispatch
useSelector
는 Store에 저장된 상태를 가져오는 Hook 함수이다. 함수 컴포넌트 내부에서 사용할 특정 Reducer를 연결하고, 현재 상태를 변수에 할당하여 접근할 수 있다.
import { useSelector, useDispatch } from 'react-redux';
//...
const state = useSelector(state => state.subReducer1);
dispatch 또한 useDispatch
로 간편하게 사용할 수 있다. dispatch를 호출하면, Reducer에 상태가 전달되어 Store의 상태가 갱신되고 상태를 사용하는 React 컴포넌트들은 새로운 상태값을 받아 리렌더링된다.
const dispatch = useDispatch();
//...
dispatch(actionCreator(...));
redux-thunk
redux-thunk는 Redux에서 비동기 작업을 처리하기 위해 가장 많이 사용되는 미들웨어이다. redux-thunk를 사용하는 방법은 객체 대신 함수를 리턴하는 Async Action Creator(비동기 액션 생성 함수)를 dispatch에 전달하는 것이다. 즉 함수를 dispatch에 전달한다.
thunk란 특정 작업(표현식)을 나중에 하기 위해 함수 형태로 감싼 것이며 여기서는 함수를 만들어 주는 Async Action Creator 함수가 thunk가 된다. redux-thunk에 의해 thunk 함수는 2개의 인자를 가진 함수를 리턴한다. 리턴되는 함수의 첫 번째 인자에 dispatch, 두 번째 인자에는 getState가 전달되어서 함수 내부에서 상태에 접근하거나 일련의 dispatch 과정을 진행 할 수 있다.
redux-thunk를 이용해 상태 변경과 side effect를 분리할 수 있다.
function thunk = (arguments?) => (dispatch, getState) => {
// 내부에서 다른 동기 혹은 비동기 액션을 호출
dispatch(otherActionCreator1(...));
...
setTimeout(() => {
dispatch(otherActionCreator2(...));
}, time)
}
Author And Source
이 문제에 관하여([Redux] 기초), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@idenk/Redux-기초저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)