Redux-Thunk

김동현·2022년 2월 10일
1

Redux

목록 보기
5/5
post-thumbnail

side-effect

이전에 살펴보았지만 reducer 함수는 순수함수로 작성해야 합니다. side-effect가 없고, 동기적으로 동작하는 함수여야 합니다.

reducer 함수는 인자로 기존 상태 객체와 action 객체를 받아 새로운 상태 객체를 반환합니다. 이것은 redux의 reducer 함수 뿐만 아니라 useReducer 훅의 reducer 함수도 같은 방법으로 작동합니다.

즉, reducer 함수는 side-effect가 없고, 동기적으로 실행되는 함수여야 합니다.

만약 redux가 관리하는 상태값으로 side-effect를 처리해야하는 경우 reducer가 아닌 다른 곳에서 처리해야합니다.

예를 들어, redux가 관리하는 상태값을 Http 요청으로 보내야하는 경우 reducer 함수가 아닌 다른 곳에서 처리해야 합니다.

Middleware

미들웨어란 "프레임워크의 요청과 응답 사이에 추가할 수 있는 코드"라고 생각할 수 있습니다.

미들웨어의 가장 큰 특징은 "연결"할 수 있다는 점입니다. 각각의 미들웨어는 서로 독립적이며, 프레임워크 안에 여러개의 미들웨어를 추가하여 연결할 수 있습니다.

Redux의 미들웨어도 마찬가지입니다. Redux의 미들웨어는 Action이 Dispatching되어 Reducer에게 전달되는 과정 사이에서 동작합니다.

Middleware를 사용하는 이유

Redux를 사용하면서 미들웨어를 사용하는 이유는 크게 두가지로 나누어볼 수 있습니다.

1. "횡단 관심사를 처리하기 위해"

횡단 관심사란 애플리케이션의 여러 서비스에 걸쳐 공통적으로 실행되어야 하는 코드를 의미합니다. 로깅이나 에러 처리 등이 횡단 관심사라고 할 수 있습니다.

이러한 코드들을 매번 필요한 곳에 직접 작성하여 중복을 발생시키는 것이 아니라 미들웨어를 통해서 리덕스의 중간 과정에서 모두 처리한다면 좋은 설계를 가진 애플리케이션으로 만들 수 있습니다.

2. "관심사의 분리를 실현하기 위해"

API 호출과 같은 side-effect를 완전히 배제할 수 없습니다. API 호출과 같은 비동기 처리를 동반한 side-effect 뿐만 아니라 동기적인 코드일지라도 로직이 복잡한 경우도 존재합니다.

관심사의 분리와 응집도 측면에서 보자면 결국 상태값을 수정하는 것과 연관된 동작이므로 상태를 갖고 있는 리덕스 내에서 처리하는 것이 올바른 접근법으로 보입니다. 하지만 리덕스의 모든 과정은 순수하게 이루어집니다. 즉, side-effect 없이 이루어집니다.

이러한 동작을 수행하기 위해서 가장 단순한 방법으로는 리액트 컴포넌트 내부에서 side-effect를 모두 수행한 뒤에 그 결과를 통해 Action을 만들고 Dispatch하는 것입니다. 하지만 리덕스 Store의 상태를 다루기 위한 관심사라 리액트 컴포넌트와 리덕스 두가지 공간에 혼재되고, 리액트 컴포넌트가 다루는 관심사가 UI 뿐만 아니라 상태값 변경하는 것도 다루게된다는 점입니다. 이렇게 관심사가 제대로 분리되지 않는 방식이라는 한계가 있습니다.

이러한 문제를 해결하기 위해 리덕스 미들웨어를 활용할 수 있습니다.

redux-thunk

기본적으로 redux는 동기적으로 동작합니다. action 객체를 dispatch 하여 reducer 함수를 실행하고 reducer에 정의한 로직대로 처리한 이후에 반환값으로 상태를 변경합니다.

redux thunk는 action으로 우리가 원하는 동작을 수행하는 함수를 dispatch하면 미들웨어에서 전달한 action 함수를 호출하고 그 결과값을 다시 dispatch하여 Reducer에게 전달하는 방식으로 동작합니다.

thunk 함수

redux thunk는 "action 함수"를 반환하는 함수이며, action 함수 내 side-effect를 처리하는 로직을 갖도록 작성합니다. 그리고 action 함수를 dispatch 합니다.

이때 dispatch되는 action 함수가 바로 하나의 미들웨어로서 동작하는 함수를 의미합니다.

thunk 함수의 반환값에 작성된 action 함수는 async 함수로 정의 가능하며, 인수로 "dispatch 함수""getState 함수"를 전달받아 실행됩니다.

// thunk 함수
const thunk = (...extraArgs) => {

    // thunk 함수의 반환값은 action 함수
    // 반환값으로 작성한 action 함수는 인수로 dispatch 함수와 getState 함수 전달 받음
    // action 함수 async 함수로 정의 가능
    // action 함수 내 side-effect 처리
    // 다른 action dispatch 가능
    return async (dispatch, getState) => { 
        // side-effect 처리,,,
    };
}

action 함수 내부에서는 "다른 action을 dispatch"를 할 수 있으며 "현재 상태(getState)도 조회"할 수 있습니다. 그리고 내부에는 "side-effect 작업을 처리"할 수 있습니다.

리덕스 내부에서는 미들웨어에서 action을 dispatch하는 경우 해당 action을 가장 처음 미들웨어부터 다시 실행하게 됩니다.

❗️주의할 점

주의할 점으로 action 함수 내에서 여러번 dispatch 하여 상태를 변경하더라도 매번 컴포넌트가 재평가되지 않습니다.

redux 내부에서는 dispatch가 호출되면 action 함수 내 dispatch 호출될 때마다 redux가 관리하는 상태가 즉시 변경은 되지만 컴포넌트를 매번 재평가하지 않습니다.

컴포넌트의 재평가는 action 함수 실행이 끝난 뒤에 "한 번만 발생"합니다.


const slice = createSlice({
    name: 'myInfo',
    initialSate: {
        name: 'kim',
        age: 0
    },
    reducers: {
        increaseAge(stateObj, action) {
            stateObj.age++;
        },
        decreaseAge(stateObj, action) {
            stateObj.age--;
        }
    }
}

위 코드와 같이 slice 객채를 생성하면 slice 객체는 name과 age 상태를 갖고 있으며, slice.actions 객체에는 increaseAge와 decreaseAge 메서드를 갖고 있습니다.

increaseAge 호출하여 생성된 action 객체 dispatch시 age 값이 1씩 증가하고, decreaseAge 호출하여 생성된 action 객체 dispatch시 age 값이 1씩 감소합니다.

const Component = () => {
    console.log('Component runngin...');
    
    const dispatch = useDispatch();
    
    const thunk = () => {
        return (dispatch, getState) => {
            console.log(getState());
            dispatch(slice.actions.increaseAge());
            
            console.log(getState());
            dispatch(slice.actions.increaseAge());
            
            console.log(getState());
            dispatch(slice.actions.increaseAge());
        };
    };
    
    dispatch(thunk());
    ,,,
}

thunk 함수는 action 함수를 반환하며 thunk 함수 호출문을 dispatch 시 콘솔창에는 아래와 같은 결과가 나타납니다.

action 함수 내 dispatch 호출하여 상태를 변경할 때마다 getState 호출하여 상태값을 확인했습니다. "상태값이 즉각적으로 변경"된 것을 확인할 수 있습니다.

이때 상태가 변경될 때마다 컴포넌트가 재평가되지 않고 "action 함수의 실행이 끝난 후 컴포넌트 재평가는 딱 한 번 발생"하는 것도 확인할 수 있습니다.

action 함수 dispatch

thunk 함수는 action 객체를 반환하는 메서드처럼 사용됩니다. 즉, thunk 함수 호출문을 dispatch 함수의 인수로 전달합니다.

이는 dispatch가 가능한 것이 action 객체 뿐만 아리나 "thunk가 반환한 action 함수" 즉, dispatch와 getState를 인수로 전달받는 함수도 dispatch할 수 있습니다.

action 함수 내 외부 데이터를 필요로하는 경우 thunk 함수 호출할 때 인수로 전달하여 호출시 action 함수가 외부 데이터를 전달받을 수 있습니다.

// thunk 함수는 action 객체 반환 함수처럼 호출문을 dispatch 함수의 인수로 전달
dispatch(thunk(args));
profile
Frontend Dev

0개의 댓글