๐Ÿ‘ TS with Redux(RTK) ๐Ÿ‘

๊น€์ฒ ์ค€ยท2022๋…„ 5์›” 14์ผ
0

REACT

๋ชฉ๋ก ๋ณด๊ธฐ
23/38
post-custom-banner

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ , ๋ฆฌ์•กํŠธ , ๋ฆฌ๋•์Šค๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

ํƒฌํ”Œ๋ฆฟ ์‚ฌ์šฉํ•˜๊ธฐ

์ผ์ผํžˆ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ , ๋ฆฌ์•กํŠธ , ๋ฆฌ๋•์Šค ์…‹ํŒ…์„ ํ•  ํ•„์š”์—†์ด Create a React Redux App ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•˜๋ฉด ์…‹ํŒ…์ด ์™„๋ฃŒ๋œ ํ…œํ”Œ๋ฆฟ ์ƒํƒœ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ช…๋ น์–ด # Redux + TypeScript template
npx create-react-app my-app --template redux-typescript

์œ„ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•œ ๋’ค ์กฐ๊ธˆ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํด๋” ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ถ”ํ›„์— ๊ฐœ๋ฐœํ•  ๋•Œ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐ”๊ฟ”๋„ ์ƒ๊ด€์—†๋‹ค.

  • app directory๋Š” store.ts(redux store ์…‹ํŒ…)์™€ hooks.ts(useDispatch์™€ useSelector๋ฅผ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์…‹ํŒ…)์„ ํฌํ•จํ•˜๊ณ  ์žˆ๊ณ 
  • features directory๋Š” slice๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ๋กœ ๊ตฌ๋ถ„ํ•˜๊ณ  ์žˆ๋‹ค.

๊ทธ ์™ธ์— ๋‚˜๋จธ์ง€ ๊ตฌ์กฐ๋Š” ๊ธฐ์กด CRA ์…‹ํŒ…๊ณผ ๋น„์Šทํ•˜๋‹ค.

index.tsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from './app/store';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './index.css';

const container = document.getElementById('root')!;
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

๊ธฐ๋ณธ ์…‹ํŒ…์œผ๋กœ ํ”„๋กœ์ ํŠธ์—์„œ store๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก Provider๋กœ ๊ฐ์‹ธ์ฃผ๊ณ  store๊ฐ’์„ props๋กœ ํ• ๋‹นํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๋•์— index.tsx์—์„œ Provider(from react-redux), store ์…‹ํŒ…์„ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

์งš๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ
ํ”„๋กœ์ ํŠธ์— redux ์ƒํƒœ๊ฐ’ ์‚ฌ์šฉ ๋ฐ dispatch ๋“ฑ์˜ store์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ตœ์ƒ๋‹จ ๋ถ€๋ชจ์ปดํฌ๋„ŒํŠธ(App.tsx)๋ฅผ Provider๋กœ ๊ฐ์‹ธ์ฃผ๊ณ  store๋ฅผ props๊ฐ’์œผ๋กœ ํ• ๋‹นํ•ด์ค˜์•ผํ•œ๋‹ค.

store.ts

src/app/store.ts

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

store.ts๋ฅผ ์‚ดํŽด๋ณด์ž.

store.ts๋Š” store๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Dispatchํƒ€์ž…, Stateํƒ€์ž…์„ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์ •ํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.

configureStore

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
});

configureStore API๋ฅผ ํ†ตํ•˜์—ฌ store๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  reducer์— ์—ฌ๋Ÿฌ slice reducer๋ฅผ ํ• ๋‹นํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

AppDispatch

AppDispatch ํƒ€์ž…์€ store.dispatch์˜ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๋„๋ก ๋ฏธ๋ฆฌ ์ •์˜ํ•ด์ค€๋‹ค.

export type AppDispatch = typeof store.dispatch

store.dispatch
store์˜ ๋ฉ”์„œ๋“œ๋กœ์„œ ์•ก์…˜๊ฐ’์„ ๋ณด๋‚ด๋ฏ€๋กœ์จ ์ƒํƒœ ๋ณ€๊ฒฝ
dispatch๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ ๋””์ŠคํŒจ์น˜๋œ ์•ก์…˜์ด๋‹ค.

์ปค์Šคํ…€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” useDispatch ํƒ€์ž…์— ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.

hook.ts์—์„œ ํ™•์ธํ•ด๋ณด๋ฉด useDispach๋ฅผ ์ปค์Šคํ…€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

hook.ts

import type { AppDispatch } from "./store";
export const useAppDispatch = () => useDispatch<AppDispatch>();

useDispatch์— dispatch ํƒ€์ž…์„ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•ด์ฃผ๊ธฐ ์œ„ํ•ด AppDispatch ํƒ€์ž…์„ ์ปค์Šคํ…€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

RootState

RootState ์ปค์Šคํ…€ ํƒ€์ž…์€ store.getState์˜ ๋ฐ˜ํ™˜๊ฐ’ ํƒ€์ž…์„ type์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ state๋ฅผ selectํ•  ๋•Œ state ํƒ€์ž…์„ ์ •์˜ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ๋‹ค.

export type RootState = ReturnType<typeof store.getState> 

export const selectCount = (state: RootState) => state.counter.value;
                                   

store.getState
store์˜ ๋ฉ”์„œ๋“œ๋กœ์จ store์˜ ํ˜„์žฌ state tree๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค.

RootState๋ฅผ ์ปค์Šคํ…€ํ•ด๋†“์ง€ ์•Š์œผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

export const selectCount = (state: ReturnType<typeof store.getState>) => state.counter.value;  

AppThunk

import {  ThunkAction, Action } from "@reduxjs/toolkit";

...
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

thunkํ•จ์ˆ˜์˜ ํƒ€์ž…์„ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉํ•  ํƒ€์ž…์ด๋‹ค.
์œ„์—์„œ Actionํƒ€์ž…์€ Action ํƒ€์ž…์„ ์ •์˜ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” RTK ํƒ€์ž…์ด๋‹ค.

thunkํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

export const incrementIfOdd =
  (amount: number): AppThunk =>
  (dispatch, getState) => {
    const currentValue = selectCount(getState());
    if (currentValue % 2 === 1) {
      dispatch(incrementByAmount(amount));
    }
  };

hook.ts

hook.ts๋Š” useDispatch์™€ useSelector๋ฅผ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปค์Šคํ…€ํ•œ ํ›…์„ ์ •์˜ํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.

useAppDispatch

useAppDispatch๋Š” useDispatch๋ฅผ ์ด์šฉํ•˜์—ฌ dispatch๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ผ์ผํžˆ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€์•Š๊ณ  ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์ •ํ•œ ํ›…์ด๋‹ค.

export const useAppDispatch = () => useDispatch<AppDispatch>()


function Counter (){
const dispatch = useAppDispatch()

return ...
<button onClick ={() => dispatch(increment())}>์ฆ๊ฐ€</button>
}

useDispatch๋ฅผ ์œ„์ฒ˜๋Ÿผ ํ•จ์ˆ˜์•ˆ์—์„œ ์จ์ฃผ๋Š” ์ด์œ ๋Š” ๋ฆฌ์•กํŠธ ํ›…(useDispatch)์€ ํ•จ์ˆ˜์•ˆ์—์„œ ์‚ฌ์šฉ๋˜์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
Error Hook

export const useAppDispatch = useDispatch<AppDispatch>()
// React Hook "useDispatch" cannot be called at the top level.
// React Hooks must be called in a React function component or a custom React Hook function.

๋ฆฌ์•กํŠธ ํ›…
๋ฆฌ์•กํŠธ ํ›…์€ ์ปดํฌ๋„ŒํŠธ๋‚ด์—์„œ ์‚ฌ์šฉ๋˜๊ฑฐ๋‚˜ ํ›… ํ•จ์ˆ˜๋‚ด์—์„œ ์‚ฌ์šฉ๋˜์–ด์•ผํ•œ๋‹ค.
์ตœ์ƒ๋‹จ์ธ top-level์—์„œ ๋‹จ๋…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

const dispatch = useDispatch()

function Counter (){
const dispatch = useAppDispatch()

return ...
<button onClick ={() => dispatch(increment())}>์ฆ๊ฐ€</button>
}

ํ•˜์ง€๋งŒ ์œ„์ฒ˜๋Ÿผ useDispatch๋ฅผ ๊ตณ์ด ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์™œ ์—ฌ๊ธฐ์„œ๋Š” ์ปค์Šคํ…€ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ผ๊นŒ?

๊ณต์‹๋ฌธ์„œ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด ํƒ€์ž…์„ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ ๊ฐ™๋‹ค.

useAppSelector

useAppSelector๋Š” useSelector๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค ์ผ์ผํžˆ ํƒ€์ž…์„ ์ง€์ •ํ•˜์ง€์•Š๊ณ  ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์ •ํ•œ ํ›…์ด๋‹ค.

useSelector
useSelector ํ›…์€ store state ์ค‘ ์›ํ•˜๋Š” state๋ฅผ ๊ณจ๋ผ์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” react-redux ํ›…์ด๋‹ค.
useSelector์˜ ์ธ์ž๋กœ ํ•จ์ˆ˜๋ฅผ ํ• ๋‹นํ•˜๋ฏ€๋กœ์จ state์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
const desiredState = useSelector(state => state.count.number)
useSelector๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ์›ํ•˜๋Š” state๊ฐ’์ด๋‹ค.

import { TypedUseSelectorHook, useSelector } from "react-redux";
import type { RootState} from "./store";

export const useAppSelector : TypedUseSelectorHook<RootState> = useSelector

// ์‚ฌ์šฉ ์˜ˆ์‹œ
export type RootState = ReturnType<typeof store.getState> 
const count = useAppSelector((state:RootState) => state.count.value)  

useAppSelector์— TypedUseSeletorHook interface ํƒ€์ž…์„ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด useSelector์— ํƒ€์ž…์„ ์ง€์ •ํ•œ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

export type RootState = ReturnType<typeof store.getState> 
const count = useSelector((state:RootState) => state.count.value)  

useDispatch์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ useSelector๋„ ๊ตณ์ด ํƒ€์ž…์„ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ๊ณต์‹๋ฌธ์„œ์— ํƒ€์ž…์„ ์ง€์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋‹ค.

payload ์‚ฌ์šฉ๋ฒ•

payload๋ž€ ์•ก์…˜์ƒ์„ฑํ•จ์ˆ˜ ๋ฐ ๋ฆฌ๋“€์„œ์—์„œ ์ƒํƒœ๊ฐ’์„ ๋ฐ”๊พธ๊ธฐ ์œ„ํ•ด ์ „๋‹ฌํ•˜๋Š” ์ธ์ž์ด๋‹ค.

RTK์—์„œ๋Š” slice๋‚ด์—์„œ ์•ก์…˜์ƒ์„ฑํ•จ์ˆ˜์™€ ๋ฆฌ๋“€์„œ๋ฅผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— reducers ๋‚ด์—์„œ ํŽ˜์ด๋กœ๋“œ๊ฐ’์„ ์ •์˜ํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์งš๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ!
RTK๋ฅผ ์“ฐ์ง€ ์•Š์„๋•Œ๋Š” ์•ก์…˜์ƒ์„ฑํ•จ์ˆ˜์™€ ๋ฆฌ๋“€์„œ๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค.
์•ก์…˜์ƒ์„ฑํ•จ์ˆ˜์— ์ธ์ž๋กœ ํŽ˜์ด๋กœ๋“œ๊ฐ’์„ ์ „๋‹ฌํ•˜๋ฉด ๋ฆฌ๋“€์„œ์—์„œ ํŽ˜์ด๋กœ๋“œ๊ฐ’์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ํŽ˜์ด๋กœ๋“œ๊ฐ’์„ ์ƒํƒœ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š”๋ฐ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const textSlice = createSlice({
  name: "text",
  initialState,
  reducers: {
    minerNameChange: (state, action : PayloadAction<T>) => {

    state.miner = action.payload
    }
  })

ํŽ˜์ด๋กœ๋“œ ์ฝ”๋“œ(action.payload)๋ฅผ ์ž…๋ ฅํ•œ๋’ค ํ•ด๋‹น ์ƒํƒœ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ dispatch๋กœ ์•ก์…˜์ƒ์„ฑํ•จ์ˆ˜์— ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๋ฉด payload๊ฐ’์œผ๋กœ ์ „๋‹ฌ์ด ๋œ๋‹ค.

dispatch(minerNameChange(e.target.value))

์ด ๋•Œ payload๋กœ ์ „๋‹ฌํ•  ์ธ์ž๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ๋ผ๋ฉด ๋ฆฌ๋“€์„œ๋‚ด์—์„œ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผํ• ๊นŒ?

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•˜๋ฉด ๋œ๋‹ค.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

const textSlice = createSlice({
  name: "text",
  initialState,
  reducers: {
    transactionInputChange: (
      state,
      { // action ๊ตฌ์กฐ๋ถ„ํ•ด => {payload : ~~}
        payload: { key, value },
      }: PayloadAction<{ key: keyof textSliceInitState; value: string }>
    ) => {
      state[key] = value; 
 // === state[action.payload.key] = action.payload.value 
    },
  },
});

์œ„์™€ ๊ฐ™์ด payload : { a,b,c...} ๊ฐ์ฒด๋กœ์จ ์ „๋‹ฌํ•˜๊ณ ์‹ถ์€ ๊ฐ’์„ ํ”„๋กœํผํ‹ฐ์— ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

PayloadAction

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ payload์— ๋Œ€ํ•ด ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค„ ๋•Œ RTK์˜ PayloadAction์ด๋ผ๋Š” ํƒ€์ž…์„ ์ด์šฉํ•œ๋‹ค.

import { PayloadAction } from "@reduxjs/toolkit";

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด action ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค€๋‹ค.

const textSlice = createSlice({
  name: "text",
  initialState,
  reducers: {
    minerNameChange: (state, action : PayloadAction<string>) => {
    state.miner = action.payload
    }
  })

PayloadAction ์ œ๋„ˆ๋ฆญ<>์•ˆ์—๋Š” payload์˜ ํƒ€์ž…์„ ํ• ๋‹นํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
์œ„์™€ ๊ฐ™์ด payload๊ฐ’์ด string์ด ๋“ค์–ด๊ฐˆ ๊ฒƒ ๊ฐ™๋‹ค๋ฉด string์œผ๋กœ ์ง€์ •ํ•ด์ฃผ๊ณ  ๊ฐ์ฒด๋ผ๋ฉด ๊ฐ์ฒด๋ฅผ ํ• ๋‹นํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

 transactionInputChange: (
      state,
      { 
        payload: { key, value },
      }: PayloadAction<{ key: keyof textSliceInitState; value: string }>
    ) => {
      state[key] = value; 

PayloadAction ํƒ€์ž…

/*
์ฒ˜์Œ payload ํƒ€์ž…์€ P = void๋กœ ์„ค์ •๋˜์žˆ์–ด payload๊ฐ’์ด ์—†์–ด๋„ ์‹คํ–‰๊ฐ€๋Šฅํ•˜๊ฒŒ ์„ค์ •๋˜์–ด ์žˆ๋‹ค.

๋‘๋ฒˆ์งธ ์ œ๋„ˆ๋ฆญ ์ธ์ž๋กœ T๋Š” action.type์˜ ํƒ€์ž…์œผ๋กœ์จ string์œผ๋กœ ์ง€์ •๋˜์–ด์žˆ๋‹ค.

์„ธ๋ฒˆ์งธ, ๋„ค๋ฒˆ์งธ ์ œ๋„ˆ๋ฆญ ์ธ์ž๋Š” action meta , error ํƒ€์ž…์„ ์ง€์ •ํ•ด์ค„ ์ˆ˜  ์žˆ๋‹ค.
*/

type PayloadAction<P = void, T extends string = string, M = never, E = never> = {
    payload: P;
    type: T;
} & ([M] extends [never] ? {} : {
    meta: M;
}) & ([E] extends [never] ? {} : {
    error: E;
})

/*
An action with a string type and an associated payload. 
This is the type of action returned by createAction() action creators.

@template P โ€” The type of the action's payload.

@template T โ€” the type used for the action type.

@template M โ€” The type of the action's meta (optional)

@template E โ€” The type of the action's error (optional)
*/
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€