Back
Featured image of post [Redux] Redux 사용해보기

[Redux] Redux 사용해보기

Redux는 어떤 순서로 작동할까?

개요

리덕스는 상태 관리를 위한 라이브러리이다. 리액트에서만 사용하는 것이 아니라 순수 자바스크립트 환경 & 여러 프론트엔트 프레임워크에서도 사용이 가능하다. 일례로 Vue프로젝트에서 상태 관리를 사용하기 위해 사용했던 flux 패턴도 리덕스에서 참고 하였다. (당시 작성한 게시글)

공식문서에서 제안하는 리덕스의 필요성은 다음과 같다.

  • 계속해서 바뀌는 상당한 양의 데이터가 있다.
  • 상태를 위한 단 하나의 근원이 필요하다.
  • 최상위 컴포넌트가 모든 상태를 가지고 있는 것은 더 이상 적절하지 않다.

설치하기

공식 문서를 살펴보면 Redux Toolkit을 강력하게 추천해주고 있다. 기존 Redux에서의 단점을 개선한 라이브러리인 것 같은데, 아직 리덕스를 사용해보지 않은 입장에서 어떻게 사용하는지는 알아야 할 것 같아 리덕스 코어 라이브러리를 설치하였다.

npm install redux

구성요소

untitled

Store

애플리케이션 상태(state)와 리듀서가 들어가 있으며 여러 내장 함수를 지니고있다.

  • getState: 현재 상태 트리를 반환한다.
  • dispatch: 액션 객체를 전달해 상태 변경을 일으킨다.

아래 코드가 기본적인 리덕스의 코드이다. (Node환경에서 테스트)

const { createStore } = require('redux');

//reducer 함수
const todos = (prev, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...prev, action.text];
    default:
      return prev;
  }
};

//store 생성 (reducer, initial data, enhancer)
const store = createStore(todos, ['씻기']);

//action store 함수
function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

//dispatch
store.dispatch(addTodo('공부하기'));
store.dispatch(addTodo('밥먹기'));

//state get
console.log(store.getState()); //[ '씻기', '공부하기', '밥먹기' ]

Action

상태에 어떠한 변화가 필요하면 발생한다. 객체로 표현되며 type 속성을 가지고 있다. (액션의 고유 이름)

{
	type: 'ACTION_NAME',
	data: 'data'
}

동적으로 상태 값을 핸들링하려면 매번 액션 객체를 만들어주는 것은 비효율적이다. 액션 생성 함수(action creator)를 사용하면 함수로 관리할 수 있다.

//action creator
const addItem = (data) => {
  return {
    type: 'ADD_ITEM',
    data
  };
};

Reducer

액션이 발생하면 리듀서 함수가 현재 상태에서 전달받은 객체를 파라미터로 전달 받아 새로운 상태를 반환한다. 두 개의 매개변수를 받는데 첫 번째 매개변수에 이전 상태 트리를 포함하고 있고, 두 번째 매개변수에 액션 객체를 전달 받는다.

const reducer = (prevState=[] , action) => {
    switch (action.type) {
        case 'ADD_TODO':
            return [...prevState, action.data];
        default:
            return prevState;
}

const addItem = (data) => {
    return {
        type: 'ADD_TODO',
        data
    };
};

store.dispatch(addItem("밥먹기"))

Dispatch

스토어의 내장 함수이다. 디스패치는 액션을 발생시키는 동작이다.

store.dispatch(addItem('밥먹기'));

Subscribe

subscribe 메서드 콜백 함수를 전달하면 상태가 변경될 때 마다 호출된다. (Action → Dispatch 상황)

const { createStore } = require('redux');
const store = createStore(reducer, initialState);

const subscribe = store.subscribe(() => console.log('dispatched!!'));

//구독을 취소하고 싶을 때 subscribe 반환 함수를 실행하면 된다.
subscribe();

Middleware

액션을 디스패치하는 과정 사이에 리듀서 함수에서 처리하기 전 미리 지정된 작업들을 수행할 수 있다. (Action, Reducer 사이의 미들웨어) createStore 세번째 인수에 enhancer 함수(미들웨어로 수행할 함수)를 전달해 사용한다.

//Middleware 구조 선언
const middleware = (store) => (dispatch) => (action) => {
  console.log('Before', action);
  dispatch(action);
  console.log('After');
};

const enhancer = applyMiddleware(middleware);

const store = createStore(reducer, apply, enhancer);

미들웨어를 활용한 비동기 처리

리덕스에서 비동기적인 함수를 수행하기 위해 사용하는 라이브러리가 Redux Thunk이다. 액션은 객체 형태이기 때문에 동기식으로만 동작한다. 액션을 함수 형태로 전달해 함수 타입일 때 전달 받은 dispatch를 실행시키는 형태이다.


const reducer = (prevState, action) => {
    switch (action.type) {
		.
		..
		...
        case 'GET_TODO':
            return {
                ...prevState,
                todos: [action.data]
            };
        default:
            return {...prevState};
    }
};

//비동기 동작을 위한 thunk미들웨어
const thunkMiddleware = store => dispatch => action => {
    if (typeof action === 'function') {
        return action(store.dispatch, store.getState);
    }
    return dispatch(action);
};

//store 생성
const store = createStore(reducer, initialState, applyMiddleware(thunkMiddleware));

//async action creator (비동기 액션은 함수 형태로 전달)
const getTodo = (data) => {
	return (dispatch, getState) => {
		//api를 호출하였다고 가정 (async)
		setTimeout(() => {
				dispatch(getTodo(['씻기', '공부하기', '밥먹기']));
		}, 2000);
	}
};

//액션 dispatch (기존과 다르게 함수를 전달했다.)
store.dispatch(requestTodo());
console.log('async!', store.getState());

주의사항

  • 하나의 애플리케이션에서는 단 하나의 스토어만 관리되어야한다. (단일 스토어)
  • 리덕스 상태는 읽기 전용이다. 기존의 객체에 변화를 주지 않고 새로운 객체를 생성 해주어야 한다.
    • 얕은 복사를 하기 때문에 상태 변경 추적이 가능하게 된다.
  • 리듀서 함수는 순수 함수로 작성해야 한다. (전달받는 파라미터에만 의존)
    • 순수 함수의 장점을 통해 combineReducers 메서드로 여러 리듀서들을 결합 시킬 수 있다.

정리

리액트를 공부하면서 사용해본 상태 관리는 context api, recoil 정도이다. 비교적 상태관리가 많아질 프로젝트에서 React-Redux를 도입해보기 전에 리덕스가 어떻게 돌아가는지 흐름을 잡아보았다. 공식 문서에도 잘 나와있어서 추가적인 redux-toolkit과 redux-saga에 대해서도 한번 공부해봐야겠다.

참고 자료