ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 초보 개발자의 Redux 깨우쳐보기! (thunk)
    Front-end/Redux 2021. 6. 25. 18:17
    반응형

    Redux는 이제 애증이 되어버렸다.

    Redux 외에도 contextAPI, mobX 등이 있는데, 물론 필요에 따라서 사용을 한다면 도움이 될 것이다.

    하지만 나는 단순히 Redux가 어렵다는 이유로 피해왔고, 그로 인해서 다른 선택을 했었다.

    그러나 아무리 mobX가 뜰 것 같다고 하고, 쓰기 편하다고 하더라도 여전히 Redux를 사용하는 회사나 프로젝트가 많고, 최소한 이해는 하고 있어야한다고 생각했다.

    더불어 지금 하는 프로젝트를 템플릿을 통해서 실시하다보니 템플릿은 Redux와 context API로 이루어져있었다.

    그리고 토이 프로젝트로 하는 것도 state 관리가 필요해서 어떤 것을 사용할까 고민했는데, 겸사겸사 Redux를 사용하기로 했다.

     

    내가 Redux를 무서워하는 이유는 이해가 잘 되지 않아서이다.

    사실 그 전부터 몇 번 보았다.

    그리고 Clone coding도 해보았다.

    그런데 초반 얼마 정도 후 외에는 이해가 되지 않았다.

    그래서 나중에는 PTSD처럼 괜히 피하게 되는 경우가 있었다.

    하지만 도와주는 사람이 있을 때!!! 한번 기대어보자 라고 생각하고 "이번에는 이해해보리라!" 라는 마음 으로 시작했다.

     

    ☠️  이 글은 redux install이나 react에 대해서 나오지 않고, 오로지 redux를 이해하고자 하는 마음에서 나를 위해 작성 되었음

    또한 간단한 숫자 증감을 다룰 예정이지만 middleware 사용을 위해서 비동기 처리를 한다.


    Redux를 이해하기 위해서는 우선 큰 그림과 명칭이 필요하다.

    바로 어떤 것들이 어떤 순서로 사용 되는지 이다.

    우선 이미지를 보자면 많은 분들이 알고 있는 flux architecture이다.

     

    기본적으로는 이 그림을 머리에 박아두는 것이 이해하는데 도움이 될 것이며,

    조금 더 나아가서는 Redux 공식홈페이지에서 볼 수 있는 아래와 같은 이미지가 있다.

     

    나의 경우에는 우선 이 큰 그림을 머리에 집어넣고 난 후에 조금 느낌이 다가왔던 것 같다.

     

    이미지를 보면 알겠지만 크게 Action, Reducer, Store라는 것이 필요하다.

    사용자마다 방식은 다를 수 있지만 나의 경우 액션(Action)은 또 크게 두가지로 구분 되는데, 바로 액션(Action)과 액션 생성함수(Action Creator)이다.

     

    Action은 상태에 어떤 변화가 필요할 때 발생시키는 것으로, 정의를 해준다고 할 수 있다.

    객체로 이루어져 있으며 그 객체는 type을 필수로 가져야하고 그 외의 값은 옵션이다.

    Action Creator는 액션을 만드는 함수이다.

    그래서 일반적으로 실행을 시킬 수 있는 함수모양으로 만들어준다.

     

    Reducer는 명령이 일치하는지 확인하고 그에 따라 어떤 변화가 나타나는 구간이라고 할 수 있다.

    if문을 사용해도 되지만 일반적으로 switch문을 이용하여 다양한 case를 만들어놓고 그에 합당하면 case에 맞는 return을 시키게 된다.

     

    Store는 이러한 reducers들을 저장해놓고, 현재의 앱상태, 내장 함수 등이 들어가게 되며, 리덕스에서는 한 Application 당 하나의 스토어를 만들게 된다.

     


    그렇기 때문에 이를 폴더로 만들어보면 나는 아래와 같은 구조로 만들어보았다

    src 폴더 안에는 App.js도 있고 index.js도 있고 여러가지가 있을 것이다.

    흔히 components라는 폴더를 만들고 안에 여러가지 컴포넌트들을 가지고 있을 것이다 바로 그 src 폴더이다.

    거기에 redux 라는 폴더를 만들고, 또 그 안에 actions, reducers, types, store.js를 만든다.

    actions에는 Action Creator 즉, 위에서 말한대로 실행 시킬 수 있는 함수 형태로 만들어질 것이고, types에는 그 actions를 정의하도록 할 것이다.

    reducers에는 action의 type과 비교 하여 return 할 수 있는 다양한 reducer를 만들것이고, reducers > index.js에서는 그런 reducers들을 한 곳에 모아서 마지막으로 store에 보낼 것이다.

     


    작동하는 순서를 한번 보자!

    만약 button 태그를 통해서 숫자 증감을 할 수 있도록 만들었고, User는 그것을 클릭 할 수 있다.

    (사람마다 방법이 다를 수 있지만 나에게 익숙한 방식으로 한다)

     

    1. types > count-type.js

    *  액션 타입을 만든다

    export const INCREMENT = "INCREMENT";
    export const DECREMENT = "DECREMENT";

    2. actions > index.js

    * 정리한 액션 타입을 type에 넣고 실행 할 수 있는 실행함수를 만든다

    import { INCREMENT, DECREMENT } from "../types/count-type";
    
    const increment = (number) => {
      return {
        type: INCREMENT,
        payload: number,
      };
    };
    
    const decrement = () => {
      return {
        type: DECREMENT,
      };
    };
    
    export const incrementAsync = () => (dispatch) => {
      dispatch(increment(5));
    };
    
    export const decrementAsync = () => (dispatch) => {
      setTimeout(() => dispatch(decrement()), 2500);
    };
    

    3. reducer > counter.js

    * switch문을 통해서 각각 case를 설정한다.

    * 이 때 action의 type과 reducer에서 switch문의 case는 같은 이름이어야 한다.

    * state-initialize를 해주지 않으면 기본값이 undefined가 나오게 된다.

    * action.payload는 옵션으로 사용된다

    const initialState = 0;
    
    const counterReducer = (state = initialState, action) => {
    
      switch (action.type) {
        case "INCREMENT":
          return state + action.payload;
        case "DECREMENT":
          return state - 1;
        default:
          return state;
      }
    };
    
    export default counterReducer;

    4. reducer > index.js

    * combineReducers를 이용해서 만든 reducer들을 모은다.

    * 여기서는 하나만 만들었으므로 큰 의미가 없지만, 나중에 여러 reducer들을 만들면 이렇게 모아서 한번에 보낼 수 있다.

    * reducer의 이름을 다른 것으로 하고 싶으면 아래의 counter처럼 해도 되지만 이름을 그대로 사용하고 싶으면 해도 상관 없다.

    import { combineReducers } from "redux";
    import counterReducer from "./counter";
    import exampleReducer from './example';
    
    const allReducers = combineReducers({
      counter: counterReducer,
      exampleReducer,
    });
    
    export default allReducers;

    5. store.js

    * createStore를 통해서 store 생성하고, 모아놓은 reducers, preloadedState, enhancer 순서로 집어넣는다

    * applyMiddleware를 통해서 middleware 사용 => 나중에 middleware를 많이 사용할 일이 있으면 [ ]에 계속 집어넣기만 하면 된다

    * composeWithDevTools를 통해서 리덕스 개발자도구 사용

    import { createStore, applyMiddleware } from "redux";
    import thunk from "redux-thunk";
    import allReducers from "./reducers/index";
    import { composeWithDevTools } from "redux-devtools-extension";
    
    const middleware = [thunk];
    
    export const store = createStore(
      allReducers,
      composeWithDevTools(applyMiddleware(...middleware))
    );

    6. App.js

    * useSelector : store에 빨대를 꽂아서 reducer를 사용 할 수 있다

    * dispatch : 이것을 통해서 콜백을 child에게 패스 할 수 있다.

    import "./App.css";
    import { useSelector, useDispatch } from "react-redux";
    import {incrementAsync, decrementAsync} from "./redux/actions/index";
    
    function App() {
      const counter = useSelector((state) => state.counter);
      const dispatch = useDispatch();
    
      return (
        <div className="App">
          <h1>Counter {counter}</h1>
          <button onClick={() => dispatch(incrementAsync())}>+</button>
          <button onClick={() => dispatch(decrementAsync())}>-</button>
        </div>
      );
    }
    
    export default App;

     

     

     

    만드는 순서는 이와 같지만 실행되어지는 순서를 보자면,

    1. App.js에서 button(+)을 클릭한다

    2. button에 onClick 이벤트가 설치되어있으므로 실행이되고, dispatch를 통해서 그 안의 콜백(increaseAsync())을 보내게 된다

    3. 보내어진 콜백함수는 actions에서 클릭을 한 함수(incrementAsync())를 실행시키게 된다.

    4. incrementAsync 함수가 가지고 있는 dispatch안의 함수로 이동하여 type과 payload를 안고 

    5. Reducer의 switch문으로 가서 case와 이름이 동일한 것을 찾아보고 같은 경우 return을 하게 된다.

     


    다른 사람들은 모르겠다.

    더러는 일찍 깨달은 사람도 있지만... 나는 머리가 나쁜가보다...

    정말 여러번 보고, 머리에 이미지를 박아놓고, 글을 써가면서 이해를 조금씩 하고 있다.

    만약 나처럼 이해가 안되고, Redux를 이해한 사람들이 신기한 분들이 있다면 포기하지말고, 보고 또 보고, 글로 써보고, 정리해보시길...!

    반응형
Designed by Tistory.