[Redux] Store 관련 함수 정리

Dodam·2024년 1월 28일
0

[Redux]

목록 보기
4/11

Store

리덕스 Store는 리덕스의 데이터들을 저장하기 위한 저장소이다.

State management vs State persistence

State management는 상태를 보관하는 것이 아니라 관리하기만 하는 것이고, State persistence는 상태를 지속시켜주는 것이다.
그리고 리덕스는 State management 라이브러리이다.

즉, 상태를 관리하기 위한 라이브러리이지 상태를 저장하고 지속시키기 위한 목적의 라이브러리는 아니다.
그래서 리덕스 Store의 데이터들은 하드디스크나 SSD 같은 저장장치에 저장되지 않는다.

그럼 리덕스 Store는 실제로 데이터들을 어떻게 관리할까?
리덕스 Store는 자바스크립트 객체나 배열 같은 변수를 통해 데이터들을 관리한다.

자바스크립트의 변수들은 메모리에 올라가고, 이 메모리는 휘발성이므로 컴퓨터 전원이 꺼지거나 프로그램 재 실행 시, 이전 데이터가 모두 날아가고 초기화된다.

리덕스 Store 역시 웹브라우저를 새로고침하거나 컴퓨터를 재부팅하면 데이터가 모두 날아가게 된다.

리덕스 Store에 있는 데이터들을 이렇게 tree 형태로 저장된다. 이러한 Tree를 State Tree라고 한다.

그리고 리덕스의 3가지 원칙 중 첫 번째인 Single source of truth에 따라, State Tree는 단 하나만 존재한다.
하나의 Tree에 계층 구조로 각각의 state들이 들어있는 형태라고 보면 된다.

Dispatcher

Dispatcher는 Action을 발송하는 역할을 한다. 그리고 발송한 Action의 수신자는 바로 Redux가 된다.
즉, Action이 발생했다는 것을 Redux에게 알리는 역할을 한다.

Flux Architecture의 전체 구성도를 보면, 오른쪽 위에 Dispatcher가 위치하고 있다.

Action이 생성되면 해당 Action이 Dispatcher로 전달되고, Dispatcher는 Action을 Redux로 보내서 처리하도록 만든다.
그리고 실제로 Action을 받아서 State에 변화를 주는 것이 바로 Reducer이다.

store.dispatch(action);

그리고 Redux에서는 이 Dispatcher 역할을 하는 함수가 바로 Store의 dispatch() 함수이다.
dispatch() 함수는 action 객체를 파라미터로 받아서, 해당 Action을 실제로 발송하는 역할을 한다.

store 관련 함수

createStore()

Store를 생성하는 역할을 하는 함수이다.
리덕스 Store에는 리덕스에서 관리하는 모든 상태값이 들어간다.

createStore(rootReducer, [preloadedState], [enhancer]) 
  • rootReducer : 여러 Reducer들을 합쳐서 하나로 만든 것을 rootReducer라고 하는데, createStore() 함수의 첫 번째 파라미터로 이 rootReducer가 들어간다.

  • preloadedState : 초기 상태값(preloadedState)이 들어간다.

  • enhancer : 여기에 리덕스 Store의 기능을 향상시켜주는 역할을 하는 함수들이 들어가게 된다.
    이러한 역할을 하는 함수를 middleware라고 부른다.
    참고로, createStore() 함수의 2, 3번째 파라미터는 optional 파라미터이다.

리덕스 Store를 생성하면 ADD_TODO라는 Action이 발생할 때마다 할 일 목록에 할 일들이 하나씩 추가되며, 이 할 일 목록들은 모두 리덕스 Store에서 관리된다.

그리고 Action을 발생시키지 않고 Store의 데이터를 변경하는 것은 불가능하다.

import { createStore } from 'redux';

function todoReducer(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([action.text]);
    default:
      return state;
  }
}

// createStore() 함수 호출
const store = createStore(todoReducer, ['처음 만난 리덕스']);

store.dispatch({
  type: 'ADD_TODO',
  text: '리덕스 강의 열심히 듣기'
});

// 출력 결과
// ['처음 만난 리덕스', '리덕스 강의 열심히 듣기']
console.log(store.getState());

applyMiddleware()

applyMiddleware(...middleware)

applyMiddleware() 함수에는 필요한 middleware들을 콤마로 구분해서 쭉 넣으면 된다.

middleware는 리덕스에 원하는 기능을 추가할 수 있게 해주는 함수이다.
이 함수는 정해진 규칙에 따라 Store의 dispatch()getState() 함수가 포함된 객체를 파라미터로 받게 된다.

middleware를 사용하면 Store에서 Action이 처리될 때 함께 동작하길 원하는 코드를 끼워넣을 수 있고,
여러 개의 middleware들은 하나로 합쳐질 수 있다.

applyMiddleware() 함수의 리턴 값은 주어진 middleware들이 적용된 Store enhancer 함수가 된다.

다음 코드는 createStore() 함수의 세 번째 파라미터로 applyMiddleware() 함수를 사용해서 loggerMiddleware라는 middleware를 넣어준 코드이다. 그리고 reducer는 외부에 별도의 파일로 작성한 이후에 import 해주었다.

loggerMiddleware는 Action이 발생했을 때 Store의 state 변화를 logging해주는 middleware이다.

import { createStore, applyMiddleware } from 'redux';
import todoReducer from './reducers';

function loggerMiddleware({ getState }) {
  return next => action => {
    console.log('dispatch 예정 action', action);

    // Middleware chain에 있는 다음 dispatch 함수를 호출
    const returnValue = next(action);

    console.log('dispatch 이후 state', getState());
    return returnValue;
  }
}

// applyMiddleware는 createStore() 함수의 세 번째 파라미터인 [enhancer]에 해당함
const store = createStore(todoReducer, ['처음 만난 리덕스'], applyMiddleware(loggerMiddleware));

store.dispatch({
  type: 'ADD_TODO',
  text: '리덕스 강의 열심히 듣기'
});
// 아래와 같이 loggingMiddleware에 의해 로깅됨
// dispatch 예정 action, { type: 'ADD_TODO', text: '리덕스 강의 열심히 듣기' }
// dispatch 이후 state, ['처음 만난 리덕스', '리덕스 강의 열심히 듣기']

getState()

State를 가져오는 역할을 하는 함수이다. 이 때 가져오는 State는 Store에 저장된 State Tree가 된다.
이 Root State tree를 통해서 리덕스에 있는 모든 데이터에 접근이 가능하다.
여기서 getState() 함수는 middleware에 파라미터로 전달된 것이다.

만약 middleware 내부가 아닌 외부에서 State Tree를 가져오려면, 만든 리덕스 Store를 사용해서 store.getState() 형태로 호출하면 된다.

import { createStore, applyMiddleware } from 'redux';
import todoReducer from './reducers';

function loggerMiddleware({ getState }) {
  return next => action => {
    console.log('dispatch 예정 action', action);

    // Middleware chain에 있는 다음 dispatch 함수를 호출
    const returnValue = next(action);

    console.log('dispatch 이후 state', getState());
    return returnValue;
  }
}

const store = createStore(todoReducer, ['처음 만난 리덕스'], applyMiddleware(loggerMiddleware));

store.dispatch({
  type: 'ADD_TODO',
  text: '리덕스 강의 열심히 듣기'
});

console.log(store.getState());

dispatch()

Action 객체를 파라미터로 받아서, 실제 Action을 발송하는 역할을 한다.
결과적으로 Dispatcher를 통해 Action이 발송되고, 이를 처리하는 과정에서 Store의 State에 변화가 생기게 된다.

코드의 가장 아래쪽에 있는 store.dispatch()가 바로 Action을 dispatch하는 코드이다.
여기에서는 typetext를 가진 Action 객체를 생성해서 전달한다.

이렇게 dispatch된 action은 Reducer에서 처리된다.

import { createStore, applyMiddleware } from 'redux';
import todoReducer from './reducers';

function loggerMiddleware({ getState }) {
  return next => action => {
    // ...
  }
}

const store = createStore(todoReducer, ['처음 만난 리덕스'], applyMiddleware(loggerMiddleware));

store.dispatch({
  type: 'ADD_TODO',
  text: '리덕스 강의 열심히 듣기'
});
profile
Good things take time

0개의 댓글