🖥️Frontend/React

[React] RTK & TypeScript

뉴발자 2023. 11. 20.
728x90

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그림 1-1. Redux

 

 

RTK 기본 사용법

https://tlseoqja.tistory.com/44

 

[React] Redux & RTK

상태 관리 라이브러리 처음 리액트를 접하고 리덕스 강의를 보고 문법이 어렵다고 느껴졌다. 그래서 리덕스 대신 상태 관리 라이브러리인 zustand를 사용해서 상태를 관리했다. 확실히 zustand의 문

tlseoqja.tistory.com

 

이전 링크에서 RTK의 기본 사용 방법을 알아보았다.

 

이 코드를 TypeScript를 적용해서 사용하는 방법을 작성해보려고 한다.

 

 

타입스크립트란?

기존 자바스크립트의 단점을 보완하기 위해 개발된 정적 타입 언어(Static Type Language)이다.

 

자바스크립트의 확장된 언어이기때문에 자바스크립트의 모든 기능을 사용할 수 있다.

 

브라우저에서 실행하기 위해서 컴파일 과정을 거쳐야 한다.

 

더욱 자세한 내용은 아래 블로그를 참고하면 된다.

 

https://velog.io/@bbaa3218/TypeScript-1-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B0%9C%EB%85%90-%EB%B0%8F-%ED%83%80%EC%9E%85

 

[TypeScript 독학] #1 타입스크립트 개념 및 타입

시작하며 지난 6개월간 부트 캠프를 마치고 취업을 준비하면서 많은 회사에서 타입스크립트를 채택하고 있다는 것을 알게 되었다. 타입스크립트는 변수의 타입을 지정해야 하는 자바스크립트

velog.io

 

 

RTX & TypeScript

코드는 기존의 자바스크립트 코드와 유사하지만 타입이 추가되면서 바뀌는 부분들이 생긴다.

 

 

store.js → store.ts

// store.ts
import { configureStore } from "@reduxjs/toolkit";
import countReducer from "../slices/countSlice";

const store = configureStore({
  reducer: {
    count: countReducer,
  }
});

// 추가
// 스토어에 등록된 reducer의 타입을 리턴받아 RootState에 선언한다.
export type RootState = ReturnType<typeof store.getState>;

// 추가
// store의 dispatch 타입을 AppDispatch에 선언한다.
export type AppDispatch = typeof store.dispatch;

export default store;

 

RootState 타입과 AppDispatch 타입을 추가해 준다.

 

RootState는 useSelector로 리덕스의 state를 조회할 때 타입을 추론해주는 용도이고,

 

AppDispatch는 useDispatch로 state를 변경할 수 있는 선언된 액션함수를 호출할 때 사용한다.

 

 

hooks.ts

기존 sre/store 폴더 안에 hooks.ts 파일을 하나 생성하고 아래의 코드를 작성한다.

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

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

 

기존의 react-redux의 useDispatch와 useSelector 대신 사용할 useAppSelector와 useAppDispatch를 생성해준다.

 

이유는 기존의 함수를 사용할 시 타입 추론이 원하는 타입으로 추론되지 않는다.

그림 1-2. useSelector 타입 추론

 

그림 1-3. useDispatch 타입 추론

 

useSelector의 state undefined 타입을 가지게 되며 에러가 발생하게 되고

 

useDispatch는 AnyAction이란 any 타입을 추론하게 된다.

 

따라서 정확한 타입을 추론해서 사용하기 위해 useAppSelector와 useAppDispatch를 선언해줘야 한다.

 

countSlice.js → countSlice.ts

// countSlice.ts
import { createSlice } from "@reduxjs/toolkit";

// 추가
interface CountState {
  value: number;
};

// 추가
const initialState: CountState = {
  value: 1,
};

export const countSlice = createSlice({
  name: "count",
  initialState,
  reducers: {
    increment: (state) => {
      state.value += 1;
    },
    decrement: (state) => {
      state.value -=1;
    }
  }
});

export const { increment, decrement } = countSlice.actions;

export default countSlice.reducer;

 

기존 initialState의 경우 직접 객체를 넣어 value값을 설정해줬다.

 

이 방법을 사용해도 오류가 발생하진 않지만, 타입스크립트는 정확한 추론을 하는 것이 중요하기 때문에

 

따로 interface로 State의 속성을 지정해주고 State 타입의 initialState를 생성해줬다.

 

 

App.jsx → App.tsx

// App.tsx
import { store } from "./store/store";
import { useAppSelector, useAppDispatch } from "./store/hooks";
import { increment, decrement } from "./slices/countSlice";

const App = () => {
  return (
    <Provider store={store}>
      <div class="borderLayout">
        <h1>Root</h1>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
          <Left1 />
          <Right1 />
        </div>
      </div>
    </Provider>
  );
};

const Left1 = () => {
  return (
    <div class="borderLayout">
      <h1>Left1</h1>
      <Left2 />
    </div>
  );
};

const Left2 = () => {
  return (
    <div class="borderLayout">
      <h1>Left2</h1>
      <Left3 />
    </div>
  );
};

const Left3 = () => {
  // 변경
  // useAppSelector state의 값을 가져온다.
  const number = useAppSelector((state) => state.count.value);

  return (
    <div class="borderLayout">
      <h1>Left3 : {count}</h1>
    </div>
  );
};

const Right1 = () => {
  return (
    <div class="borderLayout">
      <h1>Right1</h1>
      <Right2 />
    </div>
  );
};

const Right2 = () => {
  return (
    <div class="borderLayout">
      <h1>Right2</h1>
      <Right3 />
    </div>
  );
};

const Right3 = () => {
  // 변경
  // useAppDispatch 선언하고 countSlice의 reducers에 정의된 함수를 호출한다.
  const dispatch = useAppDispatch();

  return (
    <div class="borderLayout">
      <h1>Right3</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
};

export default App;

 

useAppSelector와 useAppDispatch를 사용해 생성한 변수의 타입은 아래와 같다.

그림 1-4. useAppSelector
그림 1-5. useAppDispatch

 

 

참고 사이트

https://redux-toolkit.js.org/tutorials/typescript

 

TypeScript Quick Start | Redux Toolkit

 

redux-toolkit.js.org

 

 

 

 

 

 

 

 

 

 

728x90

댓글