오늘은 Redux에 대해 알아보자

he

Redux란?

Redux 리덕스란 JavaScript 상태관리 라이브러리이다.

여기서 말하는 상태State란 간단하게 말하자면 데이터를 말한다. 덧붙이자면 상태는 컴포넌트 내부에서 사용하는 데이터라 할 수 있다.

요즘 웹 사이트를 구성할 때, 사용자에게 보이는 UI들을 하나의 파일로 몽땅 구현하지 않고, 요소요소들을 컴포넌트 단위로 구성하여 조합하는 식으로 만드는 추세이다.

Redux의 3가지 원칙

  1. 진실은 하나의 근원으로부터
    • 동일한 데이터는 항상 같은 곳에서 가져온다.
    • 즉, Store 스토어라는 하나뿐인 데이터 공간을 만들어 State를 관리한다는 의미이다.
    • 언제든 어디서든 Store에 저장된 State를 가져다 쓸 수 있다.
  2. 상태는 읽기 전용이다.
    • 리액트에서 setState 메소드를 활용해야만 상태 변경이 가능하다.
    • 리덕스에서도 액션이라는 객체를 통해서만 상태를 변경할 수 있다.
  3. 변화는 순수 함수로 작성되어야 한다.
    • 변경하는 순수 함수로만 가능하다.
    • 이는 리듀서와 연관되는 개념이다.
    • `Store(스토어) - Action(액션) - Reducer(리듀서)


Store, Action, Reducer의 의미와 특징

img

💡 Store(스토어)

Store는 상태가 관리되는 하나의 공간이다.

  • 컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱에서 필요한 상태를 담는다.
  • 컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.

💡 Action(액션)

  • Action은 앱에서 스토어에 운반할 데이터를 말한다. (주문서와 같다)
  • Action은 자바스크립트 객체 형식으로 되어 있다.

💡 Reducer(리듀서)

  • ActionStore에 바로 전달하는 것이 아닌, Reducer에 전달해야 한다.
  • Reducer가 주문을 보고 Store의 상태를 업데이트하는 것이다.
  • ActionReducer에 전달하기 위해서는 dispatch()메서드를 사용한다.

reducer함수는 기존의 stateaction을 받아서 새로운 state을 받아서 새로운 state를 만들어내는 함수이다. 이벤트 리스너라고 생각하면 된다. 리듀서는 순수함수로서 몇 가지 규칙을 가진다.

root reducer 살펴보기

리듀서는 하나의 리듀서만이 존재한다. 실무에서는 유지 보수를 위해 여러 개의 리듀서를 만든 뒤에 하나의 루트 리듀서로 병합한다. 실제로 어떤 모습을 가지고 있는지 살펴보자

function user(state = "sungjoong", action) {
  return state;
}

function post(state = "post1", action) {
  return state;
}

리덕스의 combinedReducer api를 활용해 리듀서들을 합친다.

const combined = combineReducer({ user, post });

이렇게 합쳐진 combined 이라는 루트 리듀서는 아래와 같은 모습을 가집니다.

function combined(state = {}, action) {
  return {
    user: user(state.user, action),
    post: post(state.post, action)
  };
}

즉, 우리가 dispatch를 통해 루트 리듀서를 호출하면 각각의 리듀서 함수가 실행되고 action이 존재하는 리듀서만이 새로운 상태를 반환 되는데. 눈 여겨볼 점은 combined 함수의 첫번째 인자로 넘긴 state 이고. 현재 초기값이 {}이기 때문에 각각의 리듀서 함수의 첫번째 인자로 넘겨진 state.a와 state.b는 undefined .

만약 초기 상태를 설정하고 싶다면 createStore api에서 2번째 인자인 preloadedState를 넘겨주면 된다.

💡 Dispatch(디스패치)

  • Dispatchstore의 내장 함수 중 하나로써, action을 발생시킨다.
  • action을 파라미터로 전달하고 reducer를 호출한다


💡 Subscribe (구독)

  • Subscribe는 sotre의 내장 함수 중 하나로, 특정 함수를 전달해주면 actiondispatch 되었을 때마다 전달된 함수가 호출된다.

</br >

Action 객체가 dispatch() 메서드에 전달 -> dispatch()를 통해 Reducer를 호출 -> Reducer는 새로운 Store를 생성

he

Redux의 장점

장점

  • 순수 함수를 사용해 상태를 예측 가능하게 만든다.
  • 유지보수에 용이하다
  • redux dv tool이 있어 디버깅에 유리하다.
  • 비동기를 지원하는 Redux Saga, Redux Thunk 등 다양한 미들웨어가 존재한다.

리덕스는 언제🤔 쓰는게 좋을까?

  • 전역 상태가 필요하다고 느껴질 때
  • 상태들이 자주 업데이트 될 때
  • 상태를 업데이트 하는 로직이 복잡할 때
  • 앱이 크고 많은 사람들에 의해 코드가 관리될 때
  • 상태가 업데이트되는 시점을 관찰할 필요가 있을 때

Redux Thunk 란?

리덕스 창시자인 댄 아브라모프가 만든 가장 많이 사용되는 비동기 작업 미들웨어다. 이 미들웨어를 사용하면 액션 객체가 아닌 함수를 디스패치 할 수 있다.

he

미들웨어(middleware)란?

리덕스에서 미들웨어는 주로 비동기 처리 할 때 사용한다. 리듀서에서 발생한 예외를 서버로 전송하는 등의 목적으로 미들웨어를 활용할 수 있다.

미들웨어는 리듀서가 액션을 처리하기 전에 실행되는 함수로 액션과 리듀서 사이의 중간자라고 (이미지참고) 볼 수 있다.

리덕스 미들웨어 라이브러리 redux-thunk, redux-saga, redux-observable, redux-promise-middleware 등을 서치해 사용할 수 있고, 만들어서 사용할 수도 있다. 실제 프로젝트를 작업 할 때에는, 미들웨어를 직접 만들어서 사용하는 경우는 그렇게 많지 않다.

redux-thunk는 결국 액션을 자바스크립트 객체뿐 아닌 함수로 보내는 것인데, 액션으로 함수를 보냈을때 이를 실행하는 것이다. 직접 구현하면서 이를 이해해보자

function createThunkMiddleware() {
  return ({dispatch, getState}) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState);
    }
    next(action);
  }
}

위 함수에서 리턴되는 함수가 결국 thunk middleware 함수로, 이 함수는 redux store에서 dispatch, getState api를 받고 , 다음 미들웨어로 액션을 보내는 next를 받는다. 결국 마지막 함수로 우리 dispatch할 액션을 인자로 받는다.

우리가 함수를 dispatch하게 되면 action으로 받고 타입에 따라서 다르게 처리하는 것을 확인 할 수 있다. 따라서 우리가 thunk middleware에 보낼 함수를 작성할때는 아래와 같이 작성한다.

const getUser = (arg) => (dispatch, getState) => {
  // 비동기 로직
	getState(something);
    dispatch(something);
}

dispatch할 때는 아래와 같이 함수를 실행해서 보내주면 되고, 그럼 dispatch와 getState를 인자로 받는 함수 자체를 thunk middleware로 넘기게 된다.

dispatch(getUser(args));

정리

  • 리덕스의 동작원리는 액션, 디스패치, 리듀서로 이루어진다.
  • 리듀서는 순수함수로서 side effect를 발생시키면 안된다.
  • 비동기 로직을 처리하기 위해 리덕스 미들웨어를 사용한다.
  • redux-thunk는 함수를 액션으로 보냈을때 처리하기 위함이다.