미들웨어와 이를 활용한 리듀서
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const initialState = {
data: null,
isLoading: false,
error: null
};
// 액션 타입 정의
const FETCH_DATA_REQUEST = 'FETCH_DATA_REQUEST';
const FETCH_DATA_SUCCESS = 'FETCH_DATA_SUCCESS';
const FETCH_DATA_FAILURE = 'FETCH_DATA_FAILURE';
// 액션 생성 함수
const fetchDataRequest = () => ({
type: FETCH_DATA_REQUEST
});
const fetchDataSuccess = (data) => ({
type: FETCH_DATA_SUCCESS,
payload: data
});
const fetchDataFailure = (error) => ({
type: FETCH_DATA_FAILURE,
payload: error
});
// 비동기 작업을 수행하는 액션 생성 함수
const fetchData = () => {
return (dispatch) => {
dispatch(fetchDataRequest());
return fetch('https://thisisapi.com/data')
.then(response => response.json())
.then(data => dispatch(fetchDataSuccess(data)))
.catch(error => dispatch(fetchDataFailure(error)));
};
};
// 리듀서 함수
const dataReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_DATA_REQUEST:
return { ...state, isLoading: true };
case FETCH_DATA_SUCCESS:
return { ...state, isLoading: false, data: action.payload };
case FETCH_DATA_FAILURE:
return { ...state, isLoading: false, error: action.payload };
default:
return state;
}
};
// 미들웨어 적용하여 스토어 생성
const store = createStore(dataReducer, applyMiddleware(thunk));
미들웨어(middleware)란 무엇일까요?
두 개의 개체 사이에서 원만히 통신할 수 있도록 돕는 역할을 합니다.
리덕스 미들웨어는 액션을 디스패치했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행합니다.
즉, 리덕스 미들웨어는 액션과 리듀서 사이의 중간자라고 할 수 있습니다.
[액션]
=>[미들웨어]
=>[리듀서]
=>[스토어]
리듀서가 dispatch 보낸 액션을 처리하기 전에 미들웨어가 할 수 있는 작업은 여러 가지가 있습니다.
전달받은 액션을 단순히 콘솔에 기록하거나, 전달받은 액션 정보를 기반으로 액션을 아예 취소하거나, 다른 종류의 액션을 추가로 디스패치할 수도 있습니다.
그래서 미들웨어를 사용하는 이유는 다음과 같습니다.
아마도 대부분 리덕스 미들웨어를 사용하는 용도는 비동기 처리를 하기 위해서일 것 입니다.
Redux는 동기적인 작업 처리에 적합한 구조를 가지고 있습니다.
하지만 비동기 작업을 처리할 때는 Redux 미들웨어를 사용하여 비동기 작업의 결과를 처리하거나, 비동기 작업이 끝날 때까지 기다리는 등의 작업을 할 수 있습니다.
다시 말해 Redux 미들웨어를 사용하면 비동기 작업 처리를 간편하게 할 수 있습니다.
미들웨어를 사용하면 로깅 및 디버깅을 용이하게 할 수 있습니다.
Redux 미들웨어를 사용하면 액션과 상태를 가로채서 로깅하거나 디버깅하기 쉽게 할 수 있습니다. 미들웨어에서 액션과 상태를 로깅하면 앱의 문제점을 찾아내기가 쉬워집니다.
미들웨어를 사용하면 dispatch에서 보낸 액션이 reducer로 가기 전에 가로채어 추가 작업을 할 수 있습니다.
예를 들어, 특정 액션이 디스패치될 때마다 Google Analytics에 이벤트를 전송하거나, 특정 액션이 디스패치될 때마다 상태를 서버에 전송하는 등의 작업을 할 수 있습니다.
미들웨어를 사용하면 코드를 모듈화하고 재사용성을 높일 수 있습니다.
미들웨어를 작성하면 비슷한 작업을 하는 코드를 반복적으로 작성하지 않아도 됩니다. 또한 미들웨어는 여러 액션에서 재사용될 수 있으므로 코드의 재사용성을 높일 수 있습니다
리듀서가 미들웨어 역할을 하면 안됩니다.
리듀서는 순수 함수(pure function)로 작성되어야 하며, 액션과 이전 상태를 받아서 새로운 상태를 반환하는 단순한 함수입니다.
순수함수는 외부에서 할당받은 값을 변형시키면 안되고, 동일한 인자를 할당받았을 때 항상 같은 값을 반환해주는 함수입니다.
즉, 같은 액션이 dispatch되었을 때 항상 같은 new state를 반환해야합니다.
하지만 미들웨어는 순수 함수가 아닙니다. 미들웨어는 액션을 가로채서 이를 처리하고, 새로운 액션을 반환하는 함수입니다.
따라서, 리듀서에서 미들웨어 역할을 하게 되면 순수 함수의 원칙에서 어긋나게 됩니다.
만약 리듀서에서 비동기 처리를 한다고 해봅시다.
비동기 처리는 일반적으로 순수 함수의 특성을 위반합니다. 비동기 작업은 외부에서 발생하는 이벤트에 의해 실행되며, 이 이벤트는 순수 함수의 인자로 전달되지 않습니다.
따라서 비동기 작업을 직접 리듀서에서 처리하면 상태의 예측 가능성과 일관성을 보장할 수 없게 됩니다.
예를 들어, 비동기 처리를 리듀서에서 하면 다음과 같은 문제가 발생할 수 있습니다.
비동기 작업이 완료되기 전에 리듀서가 다음 상태를 반환하면, 예상치 못한 결과가 발생합니다.
리듀서는 순수 함수이므로 비동기 작업의 완료 여부를 알 수 없습니다. 이는 애플리케이션의 상태 변화 추적과 디버깅을 어렵게 만듭니다.
비동기 작업이 성공 또는 실패한 경우, 리듀서에서 이를 적절하게 처리하기 어렵습니다.
즉, 리듀서에서 미들웨어와 같이 처리는 할 수 있습니다.
하지만 이는 리액트와 리덕스가 추구하는 순수함수의 원칙에 어긋나므로 미들웨어를 사용하여 책임을 분리하고 리듀서를 순수함수로 만드는 것이 중요합니다.