5-1. 모듈 만들기

모듈은, 4장에서 배운 Duck 구조에서 사용하는 액션, 액션생성자, 리듀서가 모두 들어가있는 파일입니다.

우리는 어플리케이션의 기능별로 모듈들을 분리시킬껀데요, 앞으로 만들 모듈들은 다음과 같습니다.

  • base: 뷰, 검색 인풋
  • modal: 모달
  • contacts : 주소록 데이터

그럼, 순서대로 만들어보도록 하겠습니다.

base 모듈 만들기

src/modules/base.js

import { createAction, handleActions } from 'redux-actions';
import { Map } from 'immutable';

const CHANGE_SEARCH = 'base/CHANGE_SEARCH';
const SET_VIEW = 'base/SET_VIEW';

export const changeSearch = createAction(CHANGE_SEARCH); // keyword
export const setView = createAction(SET_VIEW); // view

const initialState = Map({
    keyword: '',
    view: 'favorite' // favorite, list
});

export default handleActions({
    [CHANGE_SEARCH]: (state, action) => state.set('keyword', action.payload),
    [SET_VIEW]: (state, action) => state.set('view', action.payload)
}, initialState)

이 모듈에서는 검색 인풋과, 뷰를 관리합니다. 간단하지요?

src/modules/modal.js

import { createAction, handleActions } from 'redux-actions';
import { Map } from 'immutable';

const SHOW = 'modal/SHOW';
const HIDE = 'modal/HIDE';
const CHANGE = 'modal/CHANGE';

export const show = createAction(SHOW); // { mode, contact: {[id], name, phone, color} }
export const hide = createAction(HIDE);
export const change = createAction(CHANGE); // { name, value }

const initialState = Map({
    visible: false,
    mode: null, // create, modify
    contact: Map({
        id: null,
        name: '',
        phone: '',
        color: 'black'
    })
});

export default handleActions({
    [SHOW]: (state, action) => {
        const { mode, contact } = action.payload;

        return state.set('visible', true)
                    .set('mode', mode)
                    .set('contact', Map(contact))
    },
    [HIDE]: (state, action) => state.set('visible', false),
    [CHANGE]: (state, action) => {
        const { name, value } = action.payload;

        return state.setIn(['contact', name], value);
    }
}, initialState)

이 모듈에서는 모달을 띄우고, 숨기고, 그리고 그 안에있는 인풋들의 값을 수정하는 액션들을 관리합니다.

contacts 모듈 만들기

src/modules/contacts.js

import { Map, List } from 'immutable';
import { createAction, handleActions } from 'redux-actions';

const CREATE = 'contact/CREATE';
const MODIFY = 'contact/MODIFY';
const REMOVE = 'contact/REMOVE';
const TOGGLE_FAVORITE = 'contact/TOGGLE_FAVORITE';

export const create = createAction(CREATE); // { id, name, phone, color }
export const modify = createAction(MODIFY); // { id, contact: { name, phone } }
export const remove = createAction(REMOVE); // id
export const toggleFavorite = createAction(TOGGLE_FAVORITE); // id

const initialState = List([
    Map({
        "id": "SyKw5cyAl",
        "name": "김민준",
        "phone": "010-0000-0000",
        "color": "#40c057",
        "favorite": true
    }),
    Map({
        "id": "r1s_9c10l",
        "name": "아벳",
        "phone": "010-0000-0001",
        "color": "#12b886",
        "favorite": true
    }),
    Map({
        "id": "BJcFqc10l",
        "name": "베티",
        "phone": "010-0000-0002",
        "color": "#fd7e14",
        "favorite": false
    }),
    Map({
        "id": "BJUcqqk0l",
        "name": "찰리",
        "phone": "010-0000-0003",
        "color": "#15aabf",
        "favorite": false
    }),
    Map({
        "id": "rJHoq91Cl",
        "name": "데이비드",
        "phone": "010-0000-0004",
        "color": "#e64980",
        "favorite": false
    })
]);

export default handleActions({
    [CREATE]: (state, action) => {
        return state.push(Map(action.payload));
    },
    [MODIFY]: (state, action) => {
        const index = state.findIndex(contact => contact.get('id') === action.payload.id);

        return state.mergeIn([index], action.payload.contact);
    },
    [REMOVE]: (state, action) => {
        const index = state.findIndex(contact => contact.get('id') === action.payload);

        return state.delete(index);
    },
    [TOGGLE_FAVORITE]: (state, action) => {
        const index = state.findIndex(contact => contact.get('id') === action.payload);
        return state.update(index, contact => contact.set('favorite', !contact.get('favorite')));
    }
}, initialState)

이번 모듈의 상태는 Immutable List 형태로 되어 있습니다. CREATE 를 제외한 액션들은 주소록의 id 를 가지고 index 를 찾아야하니, findIndex 함수를 통하여 index 를 구하세요.

combineReducers 로 리듀서 합치기

여러개로 분리된 서브 리듀서들을 하나로 합치겠습니다.

src/modules/index.js

import { combineReducers } from 'redux';

import base from './base';
import contacts from './contacts';
import modal from './modal';

export default combineReducers({
    base,
    contacts,
    modal
});

1-10 섹션에서도 combineReducers 를 다뤄본적이 있었죠? 그때 다뤘던 예제에서는 분리할 필요가 없는 리듀서를 억지로 분리시킨 느낌이였는데, 이번에는, 기능별로 제대로 구분을 해서 분리를 하니, 리듀서 분리의 필요성이 느껴지지 않나요? 모든 액션을 한곳에서 관리하는것보다는, 이렇게 서브 리듀서들을 분류를 해서 관리하는것이 훨씬 편합니다.

스토어 생성하기

src/index.js 파일에서 방금 합친 리듀서를 불러와서 스토어를 생성해주세요. 이 과정에서, Redux DevTools 도 호환시켜주겠습니다.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import { createStore } from 'redux';
import reducers from './modules';
import { Provider } from 'react-redux';
import './index.css';

// 스토어 생성
const store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

results matching ""

    No results matching ""