본문 바로가기

React

Redux 기초 - (2) 스토어(Store), 데이터 흐름

 스토어




Redux 기초 - (1) 에서 '무엇이 일어날지'에 관한 액션과 '상태를 수정하는' 리듀서를 정의하였다.


이번에는 UI도 없이 Redux 기초 - (1) 에서 만들었던 로직들을 테스트해 볼 것이다.


이를 가능하게 하는 것이 스토어(Store)다. 

즉, 스토어는 액션과 리듀서를 가져오는 객체다.


Redux 애플리케이션은 단 하나의 스토어만을 가질 수 있다.  만약 데이터를 다루는 로직을 쪼개고 싶다면, 전 장에서 배운 리듀서 조합을 사용하면 된다.


우린 이전 장에서 combineReducers()를 통해 여러 리듀서를 하나로 합쳤다. 우리는 이것을 가져와서   createStore()에 넘기기만 하면 된다.


import { createStore } from 'redux';
import todoApp from './reducers';

let store = createStore(todoApp);


두번째 인수로 초기 상태를 지정해줄 수도 있다.

let store = createStore(todoApp, window.STATE_FROM_SERVER);


액션을 보내기




스토어를 만들었으니, 우리 프로그램이 작동하는지 볼 수 있다. 아무 UI도 없지만 우리가 만든 로직을 테스트 할 수 있다.

import { addTodo, completeTodo, setVisibilityFilter, VisibilityFilters } from './actions';

// 초기 상태를 기록합니다.
console.log(store.getState());

// 상태가 바뀔때마다 기록합니다.
let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

// 액션들을 보냅니다.
store.dispatch(addTodo('Learn about actions'));
store.dispatch(addTodo('Learn about reducers'));
store.dispatch(addTodo('Learn about store'));
store.dispatch(completeTodo(0));
store.dispatch(completeTodo(1));
store.dispatch(setVisibilityFilter(VisibilityFilters.SHOW_COMPLETED));

// 상태 변경을 더 이상 받아보지 않습니다.
unsubscribe();


데이터 흐름




Redux의 아키텍처는 엄격한 일방향 데이터 흐름을 따라 전개된다. 데이터 흐름을 예측하기 쉽게하기 위함이다. 이 개념에 익숙치 않다면 Flux의 일방향 데이터 흐름에 대한 공부(https://medium.com/swlh/the-case-for-flux-379b7d1982c6)를 더 해야한다. ( 물론 나는 아직 안함 )


모든 Redux 앱에서의 데이터는 아래와 같이 4가지 생명주기를 따른다.


import { createStore } from 'redux';
import todoApp from './reducers';

let store = createStore(todoApp);


1. 우리가 스토어를 통해 액션을 만드는 store.dispatch(action) 호출한다. 


 { type: 'LIKE_ARTICLE', articleId: 42 };
 { type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Megan' } };
 { type: 'ADD_TODO', text: 'Read the Redux docs.'};

액션은 무엇이 일어날지 정의된 오브젝트다. 위의 예제를 살펴보면, '42번 기사를 좋아합니다.', 'Read the... 할 일이 추가되었음' 이라는 액션이다.

우리는 store.dispatch(action)를 앱 내의 어디에서나 호출할 수 있다.


2. 스토어가 우리가 지정한 리듀서 ( todoApp ) 함수들을 호출한다.


스토어는 리듀서에게 현재 상태 트리액션 두가지 인수를 넘긴다. 예를 들언 할일 앱에서, 루트 리듀서는 다음과 같은 인자를 받을 것이다.

// 애플리케이션의 현재 상태(할일 목록과 선택된 필터)
 let previousState = {
   visibleTodoFilter: 'SHOW_ALL',
   todos: [{
     text: 'Read the docs.',
     complete: false
   }]
 };

 // 실행되는 액션(할일 추가)
 let action = {
   type: 'ADD_TODO',
   text: 'Understand the flow.'
 };

 // 리듀서가 다음 상태를 반환함
 let nextState = todoApp(previousState, action);

리듀서는 다음 상태를 계산하는 순수 함수라는 점을 기억해야 한다. 즉, 언제 호출하든 같은 결과값을 반환해야 한다. API 호출이나 라우터 전환 같은 사이드 이팩트를 일으켜서는 안된다. 만약 필요하다면, 액션이 전달되기 전에 행해저야 한다.


3. 루트 리듀서가 각각 리듀서의 출력을 합쳐서 하나의 상태 트리로 만든다.


우리는 루트 리듀서에서 상태 트리의 가지 하나씩( state.todos  , state.visibleTodoFilter  )들을 다루는 리듀서로 나눌 수 있도록  combineReducers() 헬퍼 함수를 이용하였다.


4. Redux 스토어가 루트 리듀서에 의해 반환된 상태 트리를 저장한다.


이 새 트리가 우리의 새로운 앱의 다음 상태다! store.subscribe(listener) 를 통해 등록된 모든 리스너가 불러지고 이들은 현재 상태를 얻기 위해 store.getState() 호출할 것이다.


이제 새로운 상태를 반영하여 UI가 변경 될 것이다. 우리가 React Redux으로 바인등을 했다면, 이 시점에 component.setState(newState)가 호출된다.

'React' 카테고리의 다른 글

React vs Vue  (0) 2019.02.28
Redux 시작하기 기초 - (마지막) React와 함께 사용하기  (0) 2017.11.21
Redux 기초 - (1)액션, 리듀서  (0) 2017.11.20
#4. React 성능과 렌더링  (0) 2017.06.19
Tip  (0) 2017.05.23