redux-tutorial/09_middleware

๐Ÿ‘‰ https://github.com/happypoulp/redux-tutorial/wiki

Tutorial 09 - middleware

์šฐ๋ฆฌ๋Š” ์ด์ „์— ์ƒˆ๋กœ์šด ๊ฐœ๋… โ€œ๋ฏธ๋“ค์›จ์–ดโ€๋ฅผ ๊ณผ์ œ๋กœ ๋‚จ๊ฒจ๋†จ์—ˆ๋‹ค. ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์–ด๋–ป๊ฒŒ ๋น„๋™๊ธฐ ์•ก์…˜์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค„ ์ˆ˜ ์žˆ๋Š”๊ฑธ๊นŒ. ๊ทธ๋ž˜์„œ ๋Œ€์ฒด ๋ฏธ๋“ค์›จ์›Œ๊ฐ€ ๋ฌด์—‡์ผ๊นŒ?

์ผ๋ฐ˜์ ์œผ๋กœ ๋ฏธ๋“ค์›จ์–ด๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ A์™€ B ์‚ฌ์ด ์ผ๋ถ€์—์„œ A๊ฐ€ B์—๊ฒŒ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ๋ณ€ํ˜•์‹œํ‚ค๊ฒŒ ํ•˜๊ธฐ์œ„ํ•œ ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ A โ€”โ€“> B ๋Œ€์‹ , A โ€”> middleware 1 โ€”> middleware 2 โ€”> middleware 3 โ€“> โ€ฆ โ€”> B ์ด๋Ÿฐ ์‹์œผ๋กœโ€ฆ

๋ฆฌ๋•์Šค์—์„œ ๋ฏธ๋“ค์›จ์–ด๋Š” ์šฐ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ๋„์™€์ค„๊นŒ? ์Œ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ ๋น„๋™๊ธฐ ์•ก์…˜ creator๋Š” ๊ทธ๋ƒฅ ๋ฆฌ๋•์Šค๋กœ๋Š” ๋‹ค๋ฃฐ ์ˆ˜ ์—†์—ˆ์ง€๋งŒ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์•ก์…˜ creator์™€ reducer ์‚ฌ์ด์— ์žˆ๋‹ค๋ฉด, ์ด ํ•จ์ˆ˜๋ฅผ ๋ฆฌ๋•์Šค์— ๋งž๋Š” ์–ด๋–ค๊ฒƒ์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค:

action โ€”> dispatcher โ€”> middleware 1 โ€”> middleware 2 โ€”> reducers

๊ฐ ์•ก์…˜๋งˆ๋‹ค (ํ˜น์€ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ ๋น„๋™๊ธฐ ์•ก์…˜ creator ๊ฐ™์€ ๊ฒฝ์šฐ์˜ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ์–ด๋–ค๊ฒƒ์ด๋“ ) ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ณ  dispatch ๋œ๋‹ค. ์ด๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์›ํ•  ๋•Œ(์•„๋‹ˆ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š๋„๋ก ํ•˜๊ฑฐ๋‚˜ - ์ด๊ฒƒ์€ ๋•Œ๋•Œ๋กœ ํ•„์š”ํ•œ ํ–‰๋™์ž„) ์•ก์…˜ creator๊ฐ€ ์‹ค์ œ ์•ก์…˜์„ dispatch ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค€๋‹ค.

๋ฆฌ๋•์Šค์—์„œ ๋ฏธ๋“ค์›จ์–ด๋Š” ๋งค์šฐ ๊ตฌ์ฒด์ ์ธ ํŠน์ง•๊ณผ ์—„๊ฒฉํ•œ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ผ์•ผํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

var anyMiddleware = function ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            // ๊ตฌ์ฒด์ ์ธ ์ฝ”๋“œ๋Š” ์ด๊ณณ์—
        }
    }
}

์œ„์—์„œ ๋ณด์•˜๋‹ค์‹œํ”ผ, ๋ฏธ๋“ค์›จ์–ด๋Š” 3๊ฐœ์˜ ์ค‘์ฒฉ๋œ ํ•จ์ˆ˜๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค(์—ฐ์†์ ์œผ๋กœ ํ˜ธ์ถœ๋  ๊ฒƒ์ž„):

  1. ์ฒซ๋ฒˆ์งธ ํ•จ์ˆ˜๋Š” dispatch์™€ getState ํ•จ์ˆ˜๋ฅผ(๋งŒ์•ฝ ๋ฏธ๋“ค์›จ์–ด๋‚˜ action creator ๊ฐ€ ์ƒํƒœ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์•ผ ํ•œ๋‹ค๋ฉด) ๋‚จ์€ 2๊ฐœ์˜ ํ•จ์ˆ˜์— ์ œ๊ณตํ•œ๋‹ค.
  2. ๋‘๋ฒˆ์งธ๋Š” next ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ ๋ณ€ํ˜•๋œ ์ธํ’‹์„ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด ํ˜น์€ ๋ฆฌ๋•์Šค์— ๋ช…ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌํ•ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค(๋ฆฌ๋•์Šค๊ฐ€ ๋ชจ๋“  ๋ฆฌ๋“€์„œ๋“ค์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒํ•˜๊ธฐ ์œ„ํ•จ).
  3. ์„ธ๋ฒˆ์งธ๋Š” ์ด์ „ ๋ฏธ๋“ค์›จ์–ด์—์„œ ํ˜น์€ dispatch์—์„œ ๋ฐ›์€ ์•ก์…˜์„ ์ œ๊ณตํ•˜๊ณ  ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค(์•ก์…˜์ด ๊ณ„์†ํ•ด์„œ ํ˜๋Ÿฌ๊ฐ€๊ธฐ ์œ„ํ•จ). ๋˜๋Š” ๋‹ค๋ฅธ ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์•ก์…˜์„ ์ง„ํ–‰์‹œํ‚จ๋‹ค.

๋งŒ์•ฝ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์— ๋Œ€ํ•ด ํ•™์Šต์„ ํ•ด๋ณธ์‚ฌ๋žŒ์ด๋ผ๋ฉด ์œ„์—์„œ ํ•จ์ˆ˜ํ˜• ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ์„ ๊ฒƒ์ด๋‹ค: ์ปค๋ง์„ ์ด์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ์œ„ ํ•จ์ˆ˜๋ฅผ ์ด๋Ÿฐ์‹์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ชฐ๋ผ๋„ ๊ดœ์ฐฎ๋‹ค. ์ด ๋ถ€๋ถ„์„ ๊ฑด๋„ˆ๋›ฐ์–ด๋„ ๋ฆฌ๋•์Šค๋ฅผ ์ดํ•ดํ•˜๋Š”๋ฐ ์•„๋ฌด๋Ÿฐ ์ง€์žฅ์ด ์—†๋‹ค

// ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ `curry`(lodash, ramda ๋“ฑ๋“ฑ)
var thunkMiddleware = curry(
    ({dispatch, getState}, next, action) => (
        // ๊ตฌ์ฒด์ ์ธ ์ฝ”๋“œ๋Š” ์ด๊ณณ์—
    )
);

๋น„๋™๊ธฐ ์•ก์…˜ creator ๋ฅผ ๋งŒ๋“ค๊ธฐ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ thunk ๋ฏธ๋“ค์›จ์–ด๋ผ๊ณ  ๋ถ€๋ฅด๊ณ  ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์—์„œ ๊ฐ€์ ธ์™”๋Š”๋ฐ ์ด๋Ÿฐ์‹์ด๋‹ค(๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด es5๋ฌธ๋ฒ•์œผ๋กœ ํ•จ์ˆ˜ ๋ณธ๋ฌธ์„ ์ˆ˜์ •ํ•จ)

var thunkMiddleware = function ({ dispatch, getState }) {
    // console.log('Enter thunkMiddleware');
    return function(next) {
        // console.log('Function "next" provided:', next);
        return function (action) {
            // console.log('Handling action:', action);
            return typeof action === 'function' ?
                action(dispatch, getState) :
                next(action)
        }
    }
}

ํ•˜๋‚˜ ๋˜๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์“ฐ๋ ค๋ฉด ๋ฆฌ๋•์Šค์˜ helper ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค: applyMiddleware.

โ€œapplyMiddlewareโ€๋Š” ๋ฏธ๋“ค์›จ์–ด๋“ค์„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ–๊ณ ์žˆ๊ณ  createStore์™€ ํ•จ๊ป˜ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค. ๋งˆ์ง€๋ง‰ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋ฉด, โ€œstore์˜ dispatch์— ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ์ถ”๊ฐ€๋œ ๊ณ ์ฐจ storeโ€๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค.(์ฐธ๊ณ  https://github.com/reactjs/redux/blob/master/src/applyMiddleware.js)

์—ฌ๊ธฐ ๋ฆฌ๋•์Šค store์— ๋ฏธ๋“ค์›จ์–ด๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

import { createStore, combineReducers, applyMiddleware } from 'redux'

const finalCreateStore = applyMiddleware(thunkMiddleware)(createStore)
// ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์—ฌ๋Ÿฌ๊ฐœ ์“ธ๋•: applyMiddleware(middleware1, middleware2, ...)(createStore)

var reducer = combineReducers({
    speaker: function (state = {}, action) {
        console.log('speaker ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ์ƒํƒœ', state, '์™€ ์•ก์…˜', action)

        switch (action.type) {
            case 'SAY':
                return {
                    ...state,
                    message: action.message
                }
            default:
                return state
        }
    }
})

const store_0 = finalCreateStore(reducer)
// Output:
//     speaker ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ์ƒํƒœ {} ์™€ ์•ก์…˜ { type: '@@redux/INIT' }
//     speaker ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ์ƒํƒœ {} ์™€ ์•ก์…˜ { type: 'type: '@@redux/PROBE_UNKNOWN_ACTION_s.b.4.z.a.x.a.j.o.r' }
//     speaker ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ์ƒํƒœ {} ์™€ ์•ก์…˜ { type: 'type: @@redux/INIT' }

์ด์ œ middleware๋กœ ๋‹ค์‹œ ๋น„๋™๊ธฐ์•ก์…˜์„ dispatch ํ•ด๋ณด์ž:

var asyncSayActionCreator_1 = function (message) {
    return function (dispatch) {
        setTimeout(function () {
            console.log(new Date(), '์•ก์…˜ Dispatch')
            dispatch({
                type: 'SAY',
                message
            })
        }, 2000)
    }
}

console.log("\n", new Date(), '๋น„๋™๊ธฐ action creator ํ˜ธ์ถœ:', "\n")

store_0.dispatch(asyncSayActionCreator_1('Hi'))
// Output:
//     Mon Aug 03 2015 00:01:20 GMT+0200 (CEST) ๋น„๋™๊ธฐ action creator ํ˜ธ์ถœ:
//     Mon Aug 03 2015 00:01:22 GMT+0200 (CEST) '์•ก์…˜ Dispatch'
//     speaker ๊ฐ€ ์ „๋‹ฌ๋ฐ›์€ ์ƒํƒœ {} ์™€ ์•ก์…˜ { type: 'SAY', message: 'Hi' }

// ๋น„๋™๊ธฐ action creator ํ˜ธ์ถœ๋กœ ์ •ํ™•ํžˆ 2์ดˆ ๋’ค์— dispatch ๋จ!

// dispatch ๋œ ์•ก์…˜์„ ๋กœ๊ทธ๋กœ ์ฐ์–ด๋ณด๋Š” ๋ฐฉ๋ฒ•:

function logMiddleware ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            console.log('logMiddleware๊ฐ€ ๋ฐ›์€ ์•ก์…˜:', action)
            return next(action)
        }
    }
}

dispatch๋œ ์•ก์…˜๋“ค์„ ๊ฑด๋„ˆ๋›ฐ๋Š” ๋ฐฉ๋ฒ•(์ด๋Œ€๋กœ๋Š” ์“ฐ๊ธฐ๋ณด๋‹ค๋Š” ๋กœ์ง์„ ์ข€ ๋” ์ถ”๊ฐ€ํ•ด์„œ ์ผ๋ถ€ ์•ก์…˜๋“ค๋งŒ ์„ ํƒํ•ด์„œ ๋‹ค์Œ ๋ฏธ๋“ค์›จ์–ด๋‚˜ ๋ฆฌ๋•์Šค์— ์ „๋‹ฌ๋˜์ง€ ์•Š๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค):

function discardMiddleware ({ dispatch, getState }) {
    return function(next) {
        return function (action) {
            console.log('discardMiddleware๊ฐ€ ๋ฐ›์€ ์•ก์…˜:', action)
        }
    }
}

finalCreateStore ์—์„œ ์œ„์— logMiddleware ๋‚˜ discardMiddleware ๋ฅผ ํ˜ธ์ถœํ•ด๋ณด๋ฉดโ€ฆ ์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ ‡๊ฒŒ ์“ฐ๊ณ :

const finalCreateStore = applyMiddleware(discardMiddleware, thunkMiddleware)(createStore)

์•ก์…˜์ด thunkMiddleware ์™€ ๋ฆฌ๋“€์„œ์— ์ „๋‹ฌ๋˜์ง€ ์•Š์•„์•ผํ•œ๋‹ค.

http://redux.js.org/docs/introduction/Ecosystem.html#middleware ์—ฌ๊ธฐ์—์„œ ๋‹ค๋ฅธ ๋ฏธ๋“ค์›จ์–ด ์˜ˆ์ œ๋“ค์„ ๋” ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๊ฒƒ์„ ์ข…ํ•ฉํ•ด๋ณด๋ฉด:

  1. ์•ก์…˜๊ณผ ์•ก์…˜ creator ์“ฐ๋Š” ๋ฐฉ๋ฒ•
  2. ์•ก์…˜์„ dispatch ํ•˜๋Š” ๋ฐฉ๋ฒ•
  3. ๋ฏธ๋“ค์›จ์–ด๋กœ ๋น„๋™๊ธฐ์•ก์…˜ ๊ฐ™์€ ์ปค์Šคํ…€ํ•œ ์•ก์…˜์„ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•

Flux ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ”Œ๋กœ์šฐ์—์„œ ๋‚จ์€ ๋ถ€๋ถ„์€ ์ƒํƒœ๊ฐ€ ๊ฐฑ์‹ ๋จ์„ ์•Œ๋ฆฌ๋Š” ๋ถ€๋ถ„์ธ๋ฐ ์–ด๋–ป๊ฒŒ ๋ฆฌ๋•์Šค store ๊ฐฑ์‹ ์„ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๋Š”๊ฑธ๊นŒ?

๋‹ค์Œ: 10_state-subscriber.md