본문 바로가기
  • 삽질하는 자의 블로그
Redux, Redux-toolkit

2. Redux의 기본적인 사용법

by 이게뭐당가 2022. 12. 17.

1. Redux 는 Reducer 를 이용해 상태를 관리한다.

 

   *Reducer : 프로그래밍 언어로, "입력을 받아 결과를 내는 장치" 정도 로 해석할 수 잇겠다.

         useReducer와 같은 맥락이지만, 같은 것은 아니다.

 

변환을 위한 틀(Action) 을 저장해 두는 Reducer 함수 를 생성하고, 그 함수로 State 를 관리한다.

 

 컴포넌트에서, 그 State 를 임의로 변형하는 것은, 매우 큰 오류를 불러 올 수 있으므로,

  오로지 Reducer 함수를 통해서만, State 를 변형한다.

 

2. Redux 는 Store 를 만들어, 그 안에 State 를 전부 넣고 관리한다.

 

  앞서 만든 Reducer 함수를 넣은 store 를 만들어, 그 안에 저장, 배포 한다.

 

3. Redux 는 "리액트 만의 전유물이 아니다"

 

 그렇다는 것은,  React 컴포넌트에서 Redux 를 "연결" 하는 장치가 필요하다는 말이 된다.

  그것이 바로 "react-redux" 패키지이다.

 

4. 리액트 컴포넌트는, Redux 저장소와, Reducer 함수를 사용한 State 변환을 하기  위해,

   react-redux 의 useDispatch, useSelector 를 이용한다.

 

   useDispatch 는 원하는 type 의 Action 을 Reducer 에게 Dispatch(전달) 하여,

             Redux의 Reducer함수로부터 State 변환을 시키게 만든다.

 

   useSelector 는 store 에 있는 State 를 선택 하는 역할을 한다.

 

5. Store 의 모든 리듀서들을 모든 앱에 걸쳐 공급하려면, Provider 를 사용하여, App.js 를 감싸준다.

 

 

리덕스의 유의사항

 

1. 리듀서 함수를 통하지 않는 State 변경은 없다. 반드시, State 를 변경 할때에는 리듀서 함수를 사용한다.

 

2. 리듀서 함수 내에서, State 를 "직접 변경" 하지 않는다. 반드시, 새 객체에 담아, 변경한다.

 

3. 초기 State 를 지정했다면,  리듀서 함수내에서 return 할때, 변경하지 않더라도, 프로퍼티로 넣어준다.

   return 하는 객체에 그 키 값이 없다면, 그 값은 undefined 가 될 것이다.

 

 

 

자 그럼 코드로 알아보자.

 

1. 기본적인 리덕스 코드

<store/index.js>

import { createStore } from "redux";

const initialState = { counter: 0, showCounter: false };    // 1. 초기값을 빼서 사용하는 편이 좋다.

const counterReducer = (state = initialState, action) => {

    if (action.type === "INCREMENT") {
        return {
            counter: state.counter + 1,
            showCounter : state.showCounter   // 2. 반환되는 값은 "새 return 으로 전부 교체되므로" 사라지지 않기 위해서는, return 한다.
        };
    }
    if (action.type === "DECREMENT") {
        return {
            counter: state.counter - 1,
            showCounter : state.showCounter
        };
    }
    if (action.type === "USERINPUT") {
        return {
            counter: state.counter + action.number,
            showCounter : state.showCounter
        };
    }

    if(action.type ==="TOGGLE_COUNTER"){
        return{
            counter : state.counter,
            showCounter : !state.showCounter
        }
    }

    return state;
};

const store = createStore(counterReducer)

export default store

** 새 return 은 절대로 "merge" 되지 않는다.
** 새 return 은 완전히 "overwrite" 될 뿐이다.
** 초기값을 지정하고, return 하지 않았다면 그 프로퍼티는 자동으로 "undefined" 가 된다.

 

<App.js>

        import React from "react";
        import ReactDOM from "react-dom/client";
        import { Provider } from "react-redux";
        import store from "./store/index";		// store 의 값을 가져온다.

        import "./index.css";
        import App from "./App";

        const root = ReactDOM.createRoot(document.getElementById("root"));
        root.render(
            <Provider store={store}>	// store 는 Provider 의 공급값을 정해주는 키이다. 
                <App />
            </Provider>
        );
<컴포넌트>

import classes from "./Counter.module.css";
import { useSelector, useDispatch } from "react-redux";

const Counter = () => {
    const counter = useSelector((state) => state.counter);	// useSelector 로 state 를 선택한다.
    const dispatch = useDispatch();				// useDispatch 로, dispatch 에 접근한다.

    const toggleCounterHandler = () => {};

    const incrementHandler = () => {
        dispatch({ type: "INCREMENT" });		// dispatch 안에 액션 타입을 넣어, 리듀서함수를 사용한다.
    };

    const decrementHandler = () => {
        dispatch({ type: "DECREMENT" });
    };

    return (
        <main className={classes.counter}>
            <h1>Redux Counter</h1>
            <div className={classes.value}>{counter}</div>
            <div>
                <button onClick={incrementHandler}> 증가</button>
                <button onClick={decrementHandler}> 감소 </button>
            </div>
            <button onClick={toggleCounterHandler}>Toggle Counter</button>
        </main>
    );
};

export default Counter;

 

2. 페이로드를 이용해, 유동적인 리듀스 함수 만들기

< /store/index.js >

    import { createStore } from "redux";

    const initialState = { counter: 0, showCounter: false };  

    const counterReducer = (state = initialState, action) => {
                    ...

        }
        if (action.type === "USERINPUT") {
            return {
                counter: state.counter + action.number,			// action 의 프로퍼티로 온다.
                showCounter : state.showCounter
            };
        }

<컴포넌트>

	import {useSelector, useDispatch} from "react-redux"
    
    const Counter = () =>{
    	const counterState = useSelector((state)=> state.counter)
        const counterDispatch = useDispatch()
        
        const increase = () => {
        	counterDispatch({ type:"USERINPUT", number:5 })		// 페이로드를 이용하여, 추가 프로퍼티를 생성한다.
        }
    }

 

 

 

3.가장 중요한 유의사항, 절대 원본의 State를 변경하지 말 것

const initialState = {name : xx, age : yy}

const reducerFn = (state = initialState, action)=>{
    if(action.type === "AGE_INCREMENT"){
        state.age = state.age + 1               //  바로 이것! 원본을 그대로 건드려버리고

        return{
            name : state.name,
            age : state.age                     // 다시 객체로 리턴하는 행위!
        }
    }
}

const store = createStore(reducerFn)

export default store


** "객체" 와 "배열" 은 "참조값임을 명심하자" state.age 를 변경한 순간, "원본의 state 는 변형된다."

** 또한, 리덕스에서 state 를 직접 변경할 경우, "state 가 동기화되지 않거나, 얘기치 않은 버그가 발생 할 수 있다."

 

그렇다면... 생각해보자

 

1. 만약 수십개의 프로퍼티를 가진 객체가, 수십개가 있다면..? 그걸 다룰 Action 이 수 십 가지라면...?

 

유지보수가 가능할까?

const {} from "redux"

const reducerFn = ()=>{
	if(...){
    	return {
        	x1 : ..,
            x2 : ..,
            x3 : ..,
            x4 : ..,
            	...
        }
    }
        if(...){
            return {
                x1 : ..,
                x2 : ..,
                x3 : ..,
                x4 : ..,
                    ...
            }
        }
}

 

2. action 을 다룰때, 자동완성은 없으니, 오타라도 발생한다면?

  여러명의 개발자가 동시에  작업하면, 겹칠 수 있는 Action type 은 어떻게...? 

<store>

    if(action.type === "ASDASDGDVXCV"){
        return {
            ...
        }
    }
    
< 컴포넌트 >

	dispatch({ type : "ASDASDGDVdXCV"})	// 오타

 

3. 혹시나 리듀서 함수를 만들때, State 원본을 건드리는 실수를 했다면?

 

등등 수많은 문제점이 있다.

 

 

그러므로, 우리는 Redux-Toolkit  으로  Redux 를 사용할 것이다.

 

 

댓글