본문으로 바로가기

 

[미들웨어란?]

redux 미들웨어는 액션을 dispatch 함수로 전달하고 리듀서가 실행되기 전과 실행된 후에 처리되는 기능을 말합니다. redux 패키지에서 지원하는 applyMiddleware 함수를 사용하면 미들웨어를 간단하게 구현할 수 있습니다.

기존의 redux 동작
미들웨어를 사용한 동작

 

 

[미들웨어 생성]

스토어 > 액션 > 미들웨어 > 리듀서 > 스토어 순으로 데이터가 기록이 됩니다. 

CallMiddleWare 함수는 다중 커링 구조로, 세 가지 인자를 순서대로 받습니다. 첫 번째 인자는 store, 두 번째 인자는 다음 미들웨어를 호출하는 함수로 예제에서는 미들웨어가 1개이기 때문에 reducer를 호출합니다.

커링(Currying) 함수

const CallMiddleWare = store => nextMiddle => action => {
    console.log('1. reducer 실행 전');
    console.log('1-1. action.type :' + action.type);
    console.log('1-2. store str :' + store.getState().data.str);
    console.log('');
  	
    // 미들웨어가 여러개인 경우 다음 미들웨어로 넘김
    // 미들웨어가 한 개인 경우 리듀서로 넘김
    let result = nextMiddle(action);
  
    console.log('2. reducer 실행 후');
    console.log('2-1. action.type :' + action.type);
    console.log('2-2. store str :' + store.getState().data.str);
    console.log('');
  
    // store.dispatch
    return result; 
}

export default CallMiddleWare;

 

 

[예제]

예제를 통해서 어떤 식으로 동작하는지 알아보도록 하겠습니다. Middleware를 통해 reducer에서 action에 작성된 내용을 읽어 store에 저장되어있는 data에 100씩 추가해보는 예제입니다.

 

파일 구조

src actions index.js
  reducers index.js
  index.js  
  App.js  
  StrAddButton.js  

 

src ➡️ index.js (src 폴더의 index.js)

리듀서로 스토어를 생성할 수 있습니다.createStore(reducers)

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {createStore, applyMiddleware} from 'redux';
import reducers from './reducers';
import CallMiddleWare from './CallMiddleWare';

const store = createStore(reducers, applyMiddleware(CallMiddleWare));

const listener = () => {
  ReactDOM.render(
    // App.js 에 props 데이터로 store 전달
    <App store={store}/>,
    document.getElementById('root')
  )
}

// 구독을 해야 store 데이터에 변화가 있을 때 listener 함수의 render를 실행하고 변경된 데이터를 렌더링
store.subscribe(listener);

// render 함수를 listener 함수로 감쌌기 때문에 초기 렌더링을 위해 수동으로 render
listener();

 

src ➡️ App.js (src 폴더의 App.js)

props에 스토어를 담아 하위 컴포넌트로 전달하면, 전달받은 컴포넌트에서 스토어에 접근할 수 있습니다.

컴포넌트에서 dispatch 함수를 사용하면 스토어 데이터를 변경할 수 있습니다.

import React, { Component } from 'react';
import StrAddButton from './StrAddButton';
import './App.css'

class App extends Component {
  render() {
    return(
      <div class="App-header">
        <h2>value: {this.props.store.getState().data.str}</h2>
        <StrAddButton store={this.props.store}/>
      </div>
    )
  }
}

export default App;

 

src ➡️ action ➡️ index.js (src 폴더에 있는 action 폴더의 index.js)

컴포넌트에서 dispatch가 실행되면, 리듀서 함수는 액션 값을 참조해 작업을 실행합니다.

export const ADD = 'ADD';
export const add = () => {
    return {
        type: ADD
    }
}

 

src ➡️ reducers ➡️ index.js (src 폴더에 있는 reducers 폴더의 index.js)

combineReducers 함수는 여러 함수가 있다면, 하나의 리듀싱 함수로 변환시키고 src의 index.js에서 사용한 createStore 함수의 파라미터로 넘겨집니다. 스토어 state 값에 변경이 발생했기 때문에 subscribe 함수가 동작해 화면이 다시 렌더링 됩니다.

import {ADD} from '../actions'
import {combineReducers} from 'redux';

// 리듀서 데이터의 초깃값을 선언, 할당
const initState = {
    str: 100
}

// action.type이 ADD인 경우 state변수 str에 100을 추가
const data = (state = initState, action) => {
    switch (action.type) {
        case ADD:
            return state, {
                str: state.str + 100
            };
        default:
            return state;
    }
}

// 리듀서를 스토어에 남겨주기 위한 함수 combineReducers
const App = combineReducers({
    data
})

export default App;

 

실행 결과

 

 

[Logger]

미들웨어의 경우 아래와 같은 로거를 콘솔에 남길 수 있습니다. 

위에서 강제로 준 로그보다 더 보기 좋고 비교하기 쉬워 개발할 때 도움 될 것입니다.

redux-logger 사용 방법