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

3. Redux-ToolKit 의 이론과 기본 사용

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

본격적으로 Redux(Tool-kit) 를 다루어보자

 

1. Redux-ToolKit

   Redux 의 개발사가 만든, 상위 버젼의 Redux 이다.

2. 기본 베이스

   사용 원리는 동일하다.

   1) 리듀서 함수를 통해, State 를 조작한다.

   2)  "하나" 의 저장소에 모든 State 를 담는다. 

 

              * 컴포넌트에서 사용하거나, Provider 하는 역할은 "react-redux" 가 하는 역할이므로, 생략한다.

             * "react-redux" 는 똑같이 사용된다. 우리는 리덕스를 교체한것이다.

 

 

3. 그럼 뭐가 다를까?

   1) Reducer Funciton 이 아니라, createSlice 를 통해, "Slice" 를 생성한다.

   2) createStore 가 아니라, configureStore 를 통해, "저장소"를 만든다.

   3 ) 원본객체를 변형하는 "척" 을 할 수 있다. 마음대로 원본객체를 변경해도,

             툴킷이 알아서, 복사하여, 원본객체변형이 일어나지 않게 만들어준다.

 

 

코드를 통해 자세하게 알아보자.

 

Redux-TookKit 의 장점1 :  createSlice 를 통한 코드의 가독성 및 안정화 [ reducerFn 의 교체 ]

import { createStore } from "redux"   // 일단 내비두었다. 곧 새걸로 교체될것이다.
import { createSlice } from "@reduxjs/toolkit";

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

const counterSlice = createSlice({
    name: "counter",                // 1. reducerFn이 함수이름을 갖듯, "name 은 필수로 지정해야한다."
    initialState : initialState,    // 2. "초기값도 반드시 지정해주도록한다."
    reducers :{                     // 3. reducers 프로퍼티에, 리듀서함수를 넣어주면된다.
        incremen(state){            // 4. 이때의 state는 항상 최신 값을 반환한다.
            state.counter++         // 5. 원본에 직접 손을 대도, toolkit안의 다른 패키지가, 이것을 복사하여 사용하게 만든다. (안정화 = 원본 절대 변경 불가)
        }                           //       (return 할 필요도 없다. 당연히 모든 value 에 대하여, 사라지지 않게 하기 위한 return 도 필요 없다)
        decerment(state){
            state.counter--
        }    
        increase(state, action){        // 6. action은 필요한 경우에만 써도 된다.
            state.counter = state.counter + action.payload      // 7. action.payload 에 기본적으로 action 으로 온 값이 저장된다.
        }      
        toggleCounter(state){
            state.showCounter = !state.showCounter
        }
    }
})

const store = createStore(counterSlice.reducer)    // 8. 이제, store 에 createSlice로 만든 슬라이스의 reducers 를 넣어준다.

export default store

 변경된 점을 살펴보자.

 

1. createSlice 를 통해, 기존 reducerFn 을 교체하였다.

2. createSlice 의 옵션으로, name ,initialState, reducers 가 존재한다.

3. reducers 안에, action 들이 들어가고, action 은 "함수 타입" 으로 생성된다.

4. *** 원본을 변경 할 수 있다. 해도 리덕스 툴킷은 자동으로, 복사본을 생성하고, 오버라이드 한다.

5. *** 모든 State 를 전부 꺼내 return 할 필요가 없다. 바뀌는 value 들만 리덕스툴킷이 변경해준다.

6. store 를 만들때, Slice 안의 reducer 만을 빼, export한다. ( 단수 reducer 임을 명심)

 

저장소를 만들때, Slice 안의 reducer "s" 가 아니고 reducer 인 이유

    ** "reducers 가 아니고" reducer 가 "단수" 인 이유 
            
        createStore 로 만들던, configureStore 로 만들던, "리덕스가 사용할(관리할) reducerFn(Slice)"은 "반드시 하나여야한다"

    ** 그럼 왜? configureStore 를 굳이 사용하는가?

        configureStore 는 "여러개의 Slice를 동시에 적용할 수 있다."

    ** 반드시 하나라고 했는데!

        configureStore 는 "여러개의 Slice 를 객체형태로 key 와 함께 넣으면, 알아서 merge 하여 하나롬 만들어준다."

 

Redux-TookKit 의 장점2 : configureStore 를 통한, "여러 리듀서 통합"   [ createStore 의 교체 ]

        import { createSlice, configureStore } from "@reduxjs/toolkit";     // 1. configureStore 로  교체

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

        const counterSlice = createSlice({
            name: "counter", 
            initialState, 
            reducers :{
            	...
            }
          				...
            
        const store = configureStore({          // 2. 이제, store 에 createSlice로 만든 슬라이스의 reducer 를 넣어준다.
            reducer : counterSlice.reducer     // 3. configureStore 에 들어가는 객체의 프로퍼티는 "reducer" 단수이다.
        })    

        export default store

 변경된 점을 살펴보자.

 

1. configureStore 로 저장소를 만든다.

2. 객체를 생성해, reducer 라는 프로퍼티 안에, Slice 의 reducer 를 넣어 export 한다.

 

앞서 궁금했던 여러 Slice 를 동시에 다루는 것을 미리 살짝 맛보아보자.

const CounterSlice = createSlice({
	name: ...,
	initialState : ...,
    reducers : {
    ...
    }
})

const newCounterSlice = createSlice({
	name: ...,
	initialState : ...,
    reducers : {
    ...
    }
})

const store = configureStore({
	reducer : { counter : CounterSlice.reduce, newCounter : newCounterSlice.reduce }
})

export default store

 

Redux-TookKit 의 장점3 : createSlice 와 액션식별자의 사용으로, 오타를 방지

 

앞서 createSlice 에는 기존 ReducerFn 과는 다르게, "함수형" 의 액션들이 지정되었다.

 

기존 Redux 는, 컴포넌트에, react-redux 로 연결후, dispatch 를 사용할때, 그 "액션타입"을 수기로 작성했다.

 

하지만! Redux-TookKit 은, "액션생성자 메서드" 를 통해, "액션을 생성" 할 것이다.

 

최종적으로 Store 를 완성해보자

 

< store / index.js >

import { createStore } from "redux"   // 일단 내비두었다. 곧 새걸로 교체될것이다.
import { createSlice } from "@reduxjs/toolkit";

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

const counterSlice = createSlice({
    name: "counter",         
    initialState : initialState, 
    reducers :{ 
        incremen(state){         
            state.counter++    
				...
        toggleCounter(state){
            state.showCounter = !state.showCounter
        }
    }
})

export const counterAction = counterSlice.actions	// actions 옵션을 통해, action 객체에 접근한다.

const store = createStore(counterSlice.reducer)

export default store

** "Slice의 actions 에 접근한 counterSlice.actions을 export 시킨다."

 

 

** "Slice의 actions 에 접근한 counterSlice.actions을 받아, reducer 를 사용한다."

< 컴포넌트 >

import { useSelector, useDispatch } from "react-redux";
import { counterAction } from "../store/index";		// counterAction 을 import 한다.

const Counter = () => {
    const counter = useSelector((state) => state.counter);		// counterState 를 가져올 수 있다.
    const showCounter = useSelector((state) => state.showCounter); // showCounterState 를 가져 올 수 있다.

    const dispatch = useDispatch();

    const toggleCounterHandler = () => {
        dispatch(counterAction.toggleCounter());		// counterAction 을 통해, Action 을 자동완성한다.
    };
			...
            
    };

    const userinputHandler = () => {
        dispatch(counterAction.userinput(5));       // ** payload 에 관하여
    };

    return (
        ...
    )
    
 ** export 한 counterAction 을 사용하면 "자동완성" 으로 Action 들이 생성된다.
 ** 오타 없이 간단하게, action 을 안정적으로 사용 가능하다.

 

** 페이로드 ( payload ) 에 관하여.

 

@reduxjs/toolkit 은 ,action 을 받아올때, 기본적으로 "payload 객체" 안에 저장되어 받아온다.

    [컴포넌트]
        const userinputHandler = () => {
            dispatch(counterAction.userinput(5));       // ** payload 에 관하여
        };
                    이렇게 사용했다면
    [리듀서]
        payload = {5} 처럼 받아 오는 것이다.   (key는 모르겠다)

"다른 예시로"

    [컴포넌트]
        const userinputHandler = () => {
            dispatch(counterAction.userinput({inputNum : 5, myNum : 10 }));   
        };
                    이렇게 사용했다면
    [리듀서]
        payload = { inputNum : 5, myNum : 10 }

    [만약 inputNum 과 myNum 을 각각 사용하는 Slice 라면 ]

        increase(){
            state.counter = state.counter + action.payload.inputNum + action.payload.myNum 
        }

<정리>
    "기본적으로 reducer 를 만들때, action 파라미터의 인수"로 "payload" 프로퍼티를 받은것이다.

    userinput(state, action) {
        state.counter = state.counter + action.payload;     // "payload" 프로퍼티를 받은것이다.
    },

 

 

댓글