📖 리액트/리액트를 다루는 기술

18장 리덕스 미들웨어를 통한 비동기 작업 관리

놀러와요 버그의 숲 2022. 2. 16. 10:22
728x90
반응형

이 글은 『리액트를 다루는 기술』(개정판/ 김민준 저 / 길벗 출판사)이라는 책을 참고하여 썼습니다.

 

 

학습목표

 

1. 리덕스 기본 용어에 친숙해진다.

 

2. 리덕스에서 미들웨어의 역할을 이해한다.

 

3. 비동기 작업을 더욱 효율적으로 관리해주는 미들웨어 redux-thunk 와 redux-saga가 있음을 인지한다.

 

 

 

리덕스 기본 개념 복습

 

스토어

 

비유적으로 표현한다면 '은행(bank)'을 떠올리면 된다. 💰

 

store는 나의 data를 넣을 수 있는 장소를 생성한다.

정보가 저장되는 곳이라고 생각하면 된다.

 

한개의 프로젝트는 단 하나의 스토어만 가질 수 있다.

 

import {createStore} from 'redux'

const store = createStore()

 

이런식으로 만드는데, 아마 에러창이 뜰 것이다.

 

 

createStore()를 하려고 할 때 reducer라는 함수를 반드시 만들어줘야 한다. 

 

import {createStore} from 'redux'

const reducer =()=>{}

const store = createStore(reducer)

 

 

state

 

이 스토어안에는 state라는 실제 정보가 저장되어있다. 

여기서 주의할 점은 state에 직접 접근이 금지되어있다. 

(마치 리액트에서 state는 useState를 통해서만 데이터 변경이 가능하게 만드는 것이랑 유사한 것  같다.)

 

 

리듀서

 

 

리듀서는 유일하게 data를 바꿀 수 있는 곳이다.

리듀서는 한마디로 함수인데 data를 수정할 수 있는 곳이다.

(아까 state는 직접 접근 및 수정이 불가능 했으니까!)

 

리듀서는 변화를 일으키는 함수이다.

(나는 개인적으로 이 reducer라는 함수가 useState에서 두번째 인자로 들어가는 setState 함수랑 비슷하다고 느꼈다.)

 

리듀서가 현재상태인 state와 action객체를 파라미터로 받아온다. 

 

리듀서가 return 하는 값이 새로운 state가 된다.

 

const reducer = (state) =>{
return state
}

const countStore = createStore(reducer)

 

 

 

render

 

 

store 밖에 있는 것으로 보아, 우리가 짠 순수 코드이다.

render: UI를 만들어주는 역할

 

 

store 내장 함수들

 

 

아까 store가 은행이랑 유사하다고 하였는데, 은행에서는 은행 돈을 우리가 직접 못만진다.

보통 은행에서는 은행 창구 직원을 통해서 거래를 한다.

 

dispatch, subscribe, getState는 함수인데, 은행 창구 직원과 하는 역할이 비슷하다.

 

이들은 store의 내장 함수들이다.

이들을 통해서 state에 접근을 할 수 있는 것이다.

 

아래 그림은 getState의 설명에 대한 그림이다.

 

 

 

function render(){
var state = store.getState();
//...
document.querySelector('#app').innerHTML= `
<h1>WEB</h1>
...
`
}

 

 

 

구독(subscribe)

 

 

구독(subscribe)도 스토어의 내장 함수 중 하나이다.

subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출해주면, state가 바뀔 때 마다 UI가 갱신된다.

(약간 우리가 유튜브 구독하면 알림 뜨는 그런 느낌이랄까?)

 

 

action과 dispatch

 

<form onSubmit="
//...
store.dispatch({type: 'create' , payload:{title:title , desc:desc }});

우리는 유저들과 인터렉션도 많이 한다.

예를들어서 submit 버튼에 이벤트가 걸려있다고 생각해보자.

 

보면, store안에 있는 dispatch에 객체를 하나 전송한다. 

 

액션: 상태에 어떤 변화가 필요하면 action이란 것이 발생한다.

이는 하나의 객체로 표현이 된다. 

액션 객체는 type 필드를 반드시 가지고 있어야 한다. 이 값을 액션의 이름이라고 생각하면 좋다.

그 외의 값들은 나중에 상태 업데이트를 할 때 참고해야 할 값이며, 작성자 마음대로 넣을 수 있다.

 

{
	type: 'ADD_TODO',
    	data: {
    	  id:1,
          text: '리덕스 배우기'
      }
}

{
	type: 'CHANGE_INPUT',
   	text: '안녕하세요'
}

 

어떤 변화를 일으켜야 할 때마다 액션 객체를 만들어야 하는데 이는 매번 직접 작성하기 번거롭다. 또한 실수로 정보를 놓칠수 도 있다.

이러한 일을 방지하기 위해 이를 함수로 보통 만들어서 관리한다.

 

const changeInput = text =>({
	type: 'CHANGE_INPUT',
   	text
});

 

 

 

dispatch는 스토어의 내장 함수 중 하나이다.

dispatch의 역할은 크게 두가지이다.

 

1. reducer를 호출해서 state값을 바꾼다.

 

How?

=> dispath에 의해서 reducer에게 현재의 state값과 action객체가 전달되고, reducer를 호출한다.

 

reducer는 state를 입력값으로 받고, action을 참조해서 새로운 state값을 리턴하는 state 가공함수이다. 

 

const initialState = {
counter:1
};

function reducer(state = initalState, action){
switch(action.type){
	case INCREMENT:
    	return{
        	counter: state.counter +1
		};
	default:
    	return state;
  }
}

 

 

2. (1번 작업이 끝나면) subscribe를 이용해서 render함수를 호출한다

=>화면 갱신 

 

 

 

리덕스 전체 흐름 정리

 

 

리덕스가 동작하는 전체적인 흐름이라고 생각하면 된다. 

 

핵심

 

1. state와 state를 기반으로 render가 화면에 그려준다.

 

2. state의 직접 접근을 불가하기에, getState를 통해 state값을 가져오고 dispatch를 통해 값을 변경시킨다.

그리고 subscribe를 통해 값이 변경되었을 때 render된다.

 

3. reducer 함수를 통해서 state값을 변경한다. 

 

 

 

18.2 미들웨어란?

 

middleware: 클라이언트에게 요청이 오고, 그 요청을 보내기 위해 응답하려는 중간에 목적에 맞게 처리하는 말하자면 거쳐가는 함수

 

ex) 요청-응답 도중에 시간을 콘솔 창에 남기고 싶으면 미들웨어 함수를 중간에 넣어서 표시

 

 

리덕스에서 미들웨어

 

 

미들 웨어는 액션과 리듀서 사이의 중간자라고 볼 수 있다.

리덕스 미들웨어는 액션을 디스패치했을 때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행한다.

 

  • 특정 조건에 따라 액션이 무시되게 만들 수 있습니다.
  • 액션을 콘솔에 출력하거나, 서버쪽에 로깅을 할 수 있습니다.
  • 액션이 디스패치 됐을 때 이를 수정해서 리듀서에게 전달되도록 할 수 있습니다.
  • 특정 액션이 발생했을 때 이에 기반하여 다른 액션이 발생되도록 할 수 있습니다.
  • 특정 액션이 발생했을 때 특정 자바스크립트 함수를 실행시킬 수 있습니다.

 

//lib/loggerMiddleware.js

const loggerMiddleware = store => next => action =>{
//미들웨어 기본 구조
};

export default loggerMiddleware

 

위 코드에서 리덕스 미들웨어 구조를 볼 수 있다.

화살표 함수를 연달아서 사용했는데, 일반 function 키워드를 풀어서 쓴다면 다음과 같은 구조이다.

 

const loggerMiddleware = function loggerMiddleware(store){
	return function(next){
    	return function(action){
         //미들웨어 기본 구조
    };
  };
};

 

미들웨어는 결국 함수를 반환하는 함수를 반환하는 함수이다 🤔

 

여기에 있는 함수에서 파라미터로 받아오는 store는 리덕스 스토어를 , action은 디스패치된 액션을 가리킨다.

 

next 파라미터는 함수 형태이며, store.dispatch와 비슷한 역할을 한다.

차이점은 next(action)을 호출하면 그 다음 처리해야할 미들웨어에게 액션을 넘겨주고, 

만약 그 다음 미들웨어가 없다면 리듀서에게 액션을 넘겨준다. 

 

 

 

이번에 만들 미들웨어는 다음 정보를 순차적으로 콘솔에 보여주는 미들웨어이다.

 

1. 이전 상태

2. 액션 정보

3. 새로워진 상태

 

//loggerMiddleware.js

const loggerMiddleware = (store) => (next) => (action) => {
  console.log(action && action.type);
  console.log("이전상태", store.getState());
  console.log("액션", action);
  next(action);
  console.log("다음상태", store.getState());
  console.groupEnd();
};

export default loggerMiddleware;

 

만든 리덕스 미들웨어를 스토어에 적용해보자. 미들웨어는 스토어를 생성하는 과정에서 적용한다.

 

import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import App from "./App";
import rootReducer from "./modules";
import loggerMiddleware from "./lib/loggerMiddleware";

const store = createStore(rootReducer, applyMiddleware(loggerMiddleware));

ReactDOM.render(
  <Provider store={store}>

 

이처럼 미들웨어는 특정 조건에 따라 액션을 무시하거나, 액션 정보를 가로채서 변경한 후 리듀서에게 전달 할 수도 있다.

아니면 특정 액션에 기반하여 새로운 액션을 여러번 디스패치할 수도있다. 

 

 

18.2.2 redux-logger 사용하기 

 

redux-logger는 우리가 방금 만든 미들웨어보다 더 잘만들어진 라이브러리이고, 브라우저 콘솔 형식도 깔끔하다.

 

$ yarn add redux-logger

 

index.js를 다음과 같이 수정해보자.

 

//src/index.js

import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import App from "./App";
import rootReducer from "./modules";
import { createLogger } from "redux-logger";

const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger));

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")

 

 

 

18.3 비동기 작업을 처리하는 미들웨어 사용

 

미들웨어 속성을 사용하면 네트워크 요청과 같은 비동기 작업을 관리하면 매우 유용하다.

 

책에서 소개된 미들웨어는 크게 두가지이다.

 

 redux-thunk

 

:비동기 작업을 처리할 때 가장 많이 사용하는 미들웨어

객체가 아닌 함수 형태의 액션을 디스패치할 수 있게 해준다.

 

redux-saga

 

redux-thunk 다음으로 가장 많이 사용되는 비동기 작업 관련 미들웨어 라이브러리

특정 액션이 디스패치되었을 때 정해진 로직에 따라 다른 액션을 디스패치 시키는 규칙을 작성하여 비동기 작업 처리 

 

 기존 요청을 취소 처리해야 할 때(불필요한 중복 요청 방지)

 특정 액션이 발생했을 때 다른 액션을 발생시키거나, API 요청 등 리덕스와 관계없는 코드를 실행할 때

 웹소켓을 사용할 때

 API 요청 실패 시 재요청해야 할 때

 

장단점 정리

redux-thunk는 일반 함수로 이루어져 있기 때문에 간단명료하다는 장점.

redux-saga는 진입장벽이 있으나 복잡한 상황에서 더욱 효율적으로 작업을 관리할 수 있는 장점이 있다.

 

 

 

 

참고자료 

 

https://www.inflearn.com/course/redux-%EC%83%9D%ED%99%9C%EC%BD%94%EB%94%A9/dashboard

 

[무료] 생활코딩 - Redux - 인프런 | 강의

생활코딩에서 제공하는 Redux 강의로, 자바스크립트를 중급 이상 다루는 분들을 대상으로 리덕스를 이용해 더 단순한 코드로 더 복잡한 애플리케이션을 만드는 방법에 대한 수업입니다., - 강의

www.inflearn.com

 

https://nomadcoders.co/redux-for-beginners

 

초보자를 위한 리덕스 101 – 노마드 코더 Nomad Coders

Vanilla JS, Redux, React

nomadcoders.co