react를 한다면 누구나 공부해야하는 기술 redux! 이시간 만들어둔 샘플을보며 학습해보자

Redux 사용이유?

기본적으로 사용목적을 말하기전에 기존의 component끼리 데이터를 주고 받는것부터 알아야한다.
왜냐하면 그 원리를 알아야 왜 불편한지 그리고 redux를 사용해야하는 이유를 알수 있기 때문이다.

Root Component(Parent) -> Component0(Child) -> Component1(Child) -> Component2(Child) -> Component3(Child)

위와 같이 Root컴포넌트를 시작해서 0 1 2 3자식 컴포넌트들이 나열되어 있다. 위에서 말했듯이 props를 이용해서 부모의 자식의 자식으로 계속해서 Component3(Child)에게 데이터를 전달하여 받아서 렌더링을 한다고 보자. 이렇게 예를 듣는것만으로도 상당히 구조가 갈수록 복잡해지는것을 알수가 있다.

(Store) <- Component3(Child) 짜잔 간단하게 변했다.

리덕스를 활용한다면 store에게서 Listener를 활용하여 데이터가 변경되었다면 받아 렌더링 하면된다.
복잡한 구조없이 오직 데이터가 나오는 store만 바라보면 되는것이다.

그리고 redux는 필수는 아니지만 거희 대부분의 react프로젝트에는 사용을 하고 있는것으로 안다. 간단한 어플리케이션에는 사용을 안할수 있지만 앱이 어느정도 수준을 넘어 복잡한 컴포넌트의 구성으로 이루어지게 되면 이때 사용하기를 적극 권장한다.

Redux 구성

리덕스의 구성에는 총3가지만 기억하면된다.

  • Store 스토어
  • Reducer 리듀서
  • Action 액션

간단하게 말하면 스토어는 애플리케이션의 상태(state)를 가지고 있는 곳. 그리고 리듀서는 스토어가 가지고 있는 상태를 변경시키기 위한 함수라고 보면돤다. 익숙한 setState와 비슷하다고 보면된다. 단지 스토어 변경에 사용된다. 다음으로 액션은 사용자의 입력,웹요청이 완료되었을때 처럼 어떤 상태변화를 일스킬수 있는 현상을 의미한다.

아래의 리덕스의 간단한 샘플을 조각으로 나눠서 이해해보자
그리고 나같은경우 리덕스 버전 "redux": "^4.0.1" 으로 하였다.

// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {createStore} from "redux";

// task의 초기상태
const initialState = {
    tasks : [
        {
            type: 'ADD_TASK',
            payload:{
                task: 'reducer 공부하기'
            }
        }
    ]
}

// reducer 정의
const taskReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'ADD_TASK':
            return {
                tasks: state.tasks.concat(action)
            }
        default:
            return state;
    }
}

// 액션생성함수 정의 -> (action.js 따로 관리하는게 좋음)
const addTask = (task) => {
    console.log("Create Action Task - "+ task)
    return {
        type:'ADD_TASK',
        payload:{
            task
        }
    }
}

const store = createStore(taskReducer);
store.dispatch(addTask("react study"));

console.log(store.getState().tasks)

ReactDOM.render(<App/>, document.getElementById('root'));
맨처음 시작은 역시 리덕스 import와 초기상태를 지정해주는것
import {createStore} from "redux";

// task의 초기상태
const initialState = {
    tasks : [
        {
            type: 'ADD_TASK',
            payload:{
                task: 'reducer 공부하기'
            }
        }
    ]
}

redux사용을 위해 createStore를 불러온다. 아래에서 store를 만들기 위해서이다.
다음으로는 스토어에서 사용할 초기상태를 지정해주는것인데 소스처럼 tasks의 구조를 가지고 있다. tasks에 여러개의 task가 오브젝트 형태로 들어갈것이다.

우리는 앞으로 위의 구조를 이용해 데이터를 dispatch()를 이용해 데이터를 넣고 listener으로 데이터를 가져올수 있다라는것만 알고 넘어가자.

여기서 언급해야할 이야기가 또 있다. 플럭스의 표준 액션에 대해서다. 간단히 아래처럼 표준으로 구성하면된다.

  • type (필수)
  • payload : 액션에 따라 데이터로 사용할수 있다.
  • error : 오류를 표현하고 싶은경우 true로 설정 이러한 경우 payload에도 Error객체를 넣어야한다.
  • meta : payload외으 정보를 액션에 포함시키고 싶은경우

위 형태는 표준이니 되도록 지켜주도록 하자

const addTask = (task) => {
    console.log("Create Action Task - "+ task)
    return {
        type:'ADD_TASK',
        payload:{
            task
        }
    }
}

이제 액션을 만들어 볼 차례이다. 일명 액션생성기를 위와 같이 따로만들어둔다. task의 인수를 받아서 리턴을 해주고 있다. (paylaod의 task는 task:task와 같다)

const taskReducer = (state = initialState, action) => {
    switch (action.type) {
        case 'ADD_TASK':
            return {
                tasks: state.tasks.concat(action)
            }
        default:
            return state;
    }
}

리듀서 정의 첫번째 인수로는 초기화 상태를 받아 정의 했으며 두번째 인수는 액션을 받는다. 받은 액션으로 swatch를 이용해 맞는 case문을 실행시킨다.
ADD_TASK 라는 액션이 들어오면 state를 합쳐 반환한다

const store = createStore(taskReducer);

스토어를 생성한다 인수로는 전에 만들었던 taskReducer를 넣어준다.
추가적으로 스토어 인수에는 (reducer, [preloadedState], [enhancer])를 넘겨줄수 있다.
첫번째 인수는 리듀서를 전달, 두번째 스토어의 초기값을 객채로 넘겨줄수 있다. 세번째 스토어의 기능을 확장할수 있게 해주는 서드파티 도구를 옵션으로 제공할수 있다.

store.dispatch(addTask("react study"));
console.log(store.getState().tasks)

dispatch를 활용하여 스토어에 실제 값을 넣어본다. 결과값은 store.getState()로 확인할수 있다.

최종결과

Alt text
정상적으로 스토어에 2개의 데이터가 들어간것을 확인할수가 있다.

이후에는 dispatch에 이어 subscribe를 이용하는 방법과 combinReducer, react-redux에 대해 알아보겠습니다.