# * Redux thunk

Written by ๐Ÿ“ Wabi

๋ฆฌ๋•์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์—์„œ๋Š” ๋น„๋™๊ธฐ ์ž‘์—…์„ ์œ„ํ•ด์„œ ํŠน์ • ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. ์ด๋•Œ redux-saga, mobX, redux thunk ๋“ฑ ๋งŽ์€ ๋ฏธ๋“ค์›จ์–ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ์–ด๋–ค ๊ฒƒ๊ณผ ์•„ํ‚คํ…์ณ๋ง์„ ๊ตฌํ˜„ํ•˜๋ƒ์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‹ค๋ฅผ ๊ฒƒ ์ด๋‹ค.

Redux-thunk๋Š” ๊ทธ์ค‘์—์„œ๋„ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋น„๋™๊ธฐ์ž‘์—…์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด ์ด๋‹ค.
๋ฆฌ๋•์Šค๋ฅผ ๊ฐœ๋ฐœํ•œ Dan Abramov๊ฐ€ ๋งŒ๋“ค์—ˆ๊ณ , redux ๊ณต์‹ ๋งค๋‰ด์–ผ : ๋น„๋™๊ธฐ ์•ก์…˜ ์ƒ์„ฑ์ž์—์„œ๋„ Redux-thunk๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ž‘์—…์„ ๋‹ค๋ฃฌ๋‹ค. ๊ทธ๋งŒํผ Redux-thunk๋กœ ๋น„๋™๊ธฐ ์ž‘์—…์„ ๊ด€๋ฆฌํ•˜๋Š”๊ฑด ๋งค์šฐ ์ง๊ด€์ ์ด๊ณ  ๊ฐ„๋‹จํ•˜๋‹ค.

# Thunk?

thunk๋Š” ์„œ๋ธŒ๋ฃจํ‹ด(์ดํ•˜ 'ํ•จ์ˆ˜')์— ์ถ”๊ฐ€ ๊ณ„์‚ฐ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

์˜ˆ์‹œ)

const wabi = '29' + 'years old'; // '29years old'
1
const wabi = () => '29' + 'years old';

wabi(); //'29years old'
1
2
3

์œ„ ์ฝ”๋“œ์™€ ์•„๋ž˜ ์ฝ”๋“œ๊ฐ€ ๋‹ค๋ฅธ์ ์€ ํ•จ์ˆ˜ํ˜•ํƒœ๋กœ ๊ฐ์ŒŒ๋‹ค๋Š” ์ ์ด๋‹ค. ์ฆ‰, wabi() ๋ผ๊ณ  ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ํ•ด์•ผ ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค.


# Redux-thunk์˜ ์•ก์…˜ ์ƒ์„ฑ์ž ์˜ˆ์‹œ

redux-thunk๋Š” ์œ„ ์˜ˆ์‹œ์—์„œ thunk ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋ณธ ๊ฒƒ ์ฒ˜๋Ÿผ, ๋ฆฌ๋•์Šค์— ๊ฐ์ฒด๋ฅผ ๋ณด๋‚ด๊ธฐ๋งŒ ํ•˜๋Š”๊ฒƒ์ด ์•„๋‹Œ, ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์•ก์…˜ ์ƒ์„ฑ์ž๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฏธ๋“ค์›จ์–ด์ด๋‹ค.

์˜ˆ์‹œ) ์ผ๋ฐ˜์ ์ธ ์•ก์…˜ ์ƒ์„ฑ์ž

const highlight = (flag) => ({
    type:'HIGHLIGHT',
    flag
})

const insertJSON = (json) => ({
    type:'INSERTJSON',
    json
})

const insertValue = (selKey,entryInfo,val,rerender) => ({
    type:'INSERTVALUE',
    selKey,entryInfo,val,rerender
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

์œ„์—์„œ ๋ณด๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ์ผ๋ฐ˜์ ์ธ ์•ก์…˜ ์ƒ์„ฑ์ž๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์•ก์…˜๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…๋งŒ ํ•œ๋‹ค.

์ผ๋ฐ˜์ ์ธ ์•ก์…˜ ์ƒ์„ฑ์ž๋Š” ๋น„๋™๊ธฐ์ž‘์—…(= 1์ดˆ๋’ค ์‹คํ–‰ ๋˜๋Š” ํ˜„์žฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ž‘์—… ์ง„ํ–‰ ๋˜๋Š” ์ค‘์ง€)์ด ๋ถˆ๊ฐ€ํ•˜์ง€๋งŒ, redux-thunk๋Š” ๊ฐ€๋Šฅํ•˜๋‹ค.

์˜ˆ์‹œ 1) ์œ„ highlight ์•ก์…˜ ์ƒ์„ฑ์ž 1์ดˆ ๋’ค ์‹คํ–‰ ๋น„๋™๊ธฐ ์ž‘์—…

const highlight = (flag) => ({
    type:'HIGHLIGHT',
    flag
})
const highlightAsync = () => {
    return dispatch => { // dispatch ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
        setTimeout(() => {
            dispatch(highlight(true));
        }, 1000);
    }
}
//1์ดˆ ๋’ค ํ•˜์ด๋ผ์ดํŠธ flag๊ฐ€ true๋กœ ์ƒํƒœ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12

store.dispatch(highlightAsync()); ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ก์…˜ํƒ€์ž… HIGHLIGHT ์•ก์…˜์ด 1์ดˆ๋’ค ๋””์ŠคํŒจ์น˜๋œ๋‹ค.

์˜ˆ์‹œ 2) flag๊ฐ€ ํ˜„์žฌ ์ƒํƒœ์˜ ๋ฐ˜๋Œ€๋กœ ์ƒํƒœ๋ณ€ํ™”๋ฅผ ํ•˜๊ฒŒ ๋‚˜ํƒ€๋‚ธ๋‹ค. (1์ดˆ ๋’ค)

const highlight = (flag) => ({
    type:'HIGHLIGHT',
    flag
})
const highlightAsync = () => {
    return (dispatch,getState) => { // dispatch, getState ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
        const { highlight } = getState();
        setTimeout(() => {
            dispatch(highlight(!highlight));
        }, 1000);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

์œ„ ์˜ˆ์‹œ์—์„œ๋Š” getState ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ†ตํ•ด ํ˜„์žฌ์ƒํƒœ๊ฐ’์„ ํ™•์ธํ›„ ํ•ด๋‹น boolean๊ฐ’์˜ ๋ฐ˜๋Œ€๋กœ ์ƒํƒœ๋ณ€ํ™”๋ฅผ 1์ดˆ ๋’ค ์ผ์œผํ‚ค๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋˜๋Š” dispatch๋ฅผ ํ•˜๊ธฐ ์‹ซ์„ ๊ฒฝ์šฐ, return ์œผ๋กœ ์ƒํƒœ๋ณ€ํ™”๋ฅผ ์ค‘๋‹จ ํ• ์ˆ˜๋„ ์žˆ๋‹ค.

์˜ˆ์‹œ 3) ์ƒํƒœ๋ณ€ํ™” ์ค‘๋‹จ

const highlight = (flag) => ({
    type:'HIGHLIGHT',
    flag
})
const highlightAsync = () => {
    return (dispatch,getState) => { // dispatch, getState ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง„ ํ•จ์ˆ˜ ๋ฐ˜ํ™˜
        const { highlight } = getState();
        if(!highlight) return;
        //ํ•˜์ด๋ผ์ดํŠธ๊ฐ€ false ์ผ ๊ฒฝ์šฐ, ์ค‘๋‹จ
        setTimeout(() => {
            dispatch(highlight(highlight));
        }, 1000);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ํ˜„์žฌ ์Šคํ† ์–ด ์ƒํƒœ ๊ฐ’์— ๋”ฐ๋ผ์„œ ์•ก์…˜ dispatch๋ฅผ ์ค‘๋‹จ ์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค.


# Redux-thunk๋ฅผ ์ด์šฉํ•œ ์›น ์š”์ฒญ ์˜ˆ์‹œ

//์•ก์…˜ ํƒ€์ž… ์ •์˜
const API_REQUEST = "API_REQUEST";
const API_SUCCESS = "API_SUCCESS";
const API_FAILURE = "API_FAILURE";

//์•ก์…˜ ์ƒ์„ฑ์ž
const asyncRequest = () => ({
  type: API_REQUEST
})

const asyncSuccess = (data) => ({
  type:API_SUCCESS,
  data
})

const asyncFailure = (error) => ({
  type: API_FAILURE,
  error
})

//์›น ํ†ต์‹  ์•ก์…˜ ์ƒ์„ฑ์ž ๋‚ด๋ถ€ ๋“ค์–ด๊ฐˆ promise ๋ฐ˜ํ™˜ ํ•จ์ˆ˜ 'callAPI'
const callAPI = (data) => {
  const promise = fetch({...data
    //data ๋งค๊ฐœ๋ณ€์ˆ˜์— api ํ†ต์‹ ์„ ์œ„ํ•œ fetchํ•จ์ˆ˜์— ์‚ฌ์šฉ๋  ๋ณ€์ˆ˜ ์‚ฝ์ž….
  });
  return promise.then(response => response.json())
}

//์›น ํ†ต์‹  ์•ก์…˜
const dispatchAsyncAct = data => (dispatch,getState) =>{
  dispatch(asyncRequest);
  callAPI(data)
    .then(json => dispatch(asyncSuccess(json))
    .catch(error => dispatch(asyncFailure(error))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

์œ„ ์˜ˆ์‹œ์ฝ”๋“œ ์›น ํ†ต์‹  ๋กœ์ง์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฆฌ๋•์Šค ๊ตฌ์กฐ๋‚˜ ์ฝ”๋“œํŒจํ„ด์ด ๊ฐœ๋ฐœ์ž๋งˆ๋‹ค, ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ์•Œ๋งž๊ฒŒ ์“ฐ๋ฉด ๋œ๋‹ค.


# ์š”์•ฝ

redux-thunk๋Š” ๋ฆฌ๋“€์„œ์— ์•ก์…˜๊ฐ์ฒด๋ฅผ ๋ณด๋‚ด๋Š” ์ผ๋ฐ˜์ ์ธ ์•ก์…˜์ƒ์„ฑ์ž์—์„œ ํ•จ์ˆ˜ํ˜•ํƒœ(thunk)์˜ ์•ก์…˜์ƒ์„ฑ์ž, ์ฆ‰ ๋‚ด๋ถ€์— dispatch, getState๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ํ•จ์ˆ˜ ํ˜•ํƒœ๋ฅผ ๊ฐ€์ง„ ์•ก์…˜ ์ƒ์„ฑ์ž๋ฅผ ๋ฆฌ๋“€์„œ์— ๋ณด๋‚ด๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฏธ๋“ค์›จ์–ด ์ด๋‹ค.

Last Updated: 7/5/2019, 8:38:55 AM