Next.js에서 Redux Toolkit + TypeScript 연동하기

Dave·2023년 9월 30일
0

NEXT

목록 보기
3/4
post-thumbnail

출처
How to use Redux in Next.js
Redux - Useage with TypeScript

폴더 구조는 아래와 같이 구성된다.

/store
	/slices
    slice.ts
hooks.ts
index.ts

1. 의존성 설치하기

리덕스 툴킷과 리액트와 리덕스를 연결해주는 패키지를 설치해준다.

npm install @reduxjs/toolkit react-redux

또한 리덕스 툴킷과 Next.js를 연동할 패키지도 설치해준다.

npm install next-redux-wrapper

2. 슬라이스 생성하기, store/slices/slice.ts

각 목적에 따라 나뉘어진 슬라이스 리듀서 함수를 만든다.

2-1. 초기 상태 및 초기 상태 타입 선언하기

interface InitialLangState {
  lang: string,
}

const initialState: InitialLangState = {
  lang: "en"
}

2-2. 슬라이스 리듀서 함수 생성하기

import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { HYDRATE } from "next-redux-wrapper"

우선 Redux Toolkit 패키지를 통해 슬라이스를 생성할 떄 사용되는 createSlice 메서드를, 리듀서 함수 내부 action의 payload 프로퍼티의 타입을 설정할 때 사용하는 PayloadAction을 불러온다.

이후 서버와 클라이언트의 상태 일치를 위한 HYDRATE도 next-redux-wrapper 패키지에서 불러온다.

const langSlice = createSlice({
  name: "lang",
  initialState,
  reducers: {
    changeCurrentLang: (state: InitialLangState, action: PayloadAction<string>) => {
      state.lang = action.payload
    }
  },
  extraReducers: {
    [HYDRATE]: (state, action) => {
      console.log('HYDRATE', state, action.payload)
      return {
        ...state,
        ...action.payload.lang,
      }
    }
  }
})

이후 슬라이스를 생성하고 객체 형태로 리듀서 함수를 넣어준다.

  1. name

해당 슬라이스의 이름

  1. initialState

해당 슬라이스의 초기 상태

  1. reducers

초기 상태를 제어할 리듀서 함수들, 액션 생성자 함수는 reducers 내부 프로퍼티 명으로 대체한다.

  1. extraReducers

말 그대로 슬라이스 외부에서 정의한 액션에 대한 처리 로직을 적어준다. 위 예시에서는 HYDRATE 단계일 때를 명시해준다.

2-3. 액션 생성자 함수 및 리듀서 함수 export 하기

export const { changeCurrentLang } = langSlice.actions

export const langReducer = langSlice.reducer

추후 컴포넌트에서 액션 생성자 함수를 디스패치할 수 있게 reducers 내부에서 선언한 액션 생성자 함수와 리듀서를 내보낸다.

3. 스토어 생성하기, store/index.ts

import { configureStore } from '@reduxjs/toolkit'

import { langReducer } from './slices/langSlice'

export const makeStore = configureStore({
  reducer: {
    lang: langReducer
  },
  devTools: true
})

우선 기존에 만든 슬라이스에서 리듀서 함수를 불러오고 configureStore 에 들어갈 객체 형태의 인수에서 reducer 프로퍼티에 해당 리듀서를 넣어준다.

이후 스토어, 상태에 대한 타입과 useDispatch, useSelector 훅에 적용할 타입을 따로 선언한다.

import { configureStore, ThunkAction } from '@reduxjs/toolkit'
import { Action } from 'redux'
import { createWrapper } from 'next-redux-wrapper'

// reducers
import { langReducer } from './slices/langSlice'

// Root reducer를 사용하는 대신, 각 slice의 reducer를 할당한다.
const makeStore = () => configureStore({
  reducer: {
    lang: langReducer
  },
  devTools: true,
})

export type AppStore = ReturnType<typeof makeStore>
export type AppState = ReturnType<AppStore['getState']>
export type AppDispatch = AppStore['dispatch']
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, AppState, unknown, Action>

export const wrapper = createWrapper<AppStore>(makeStore)

이후 createWrapper 메서드를 통해 Redux-toolkit과 Next.js를 연결해준다.

4. useDispatch, useSelector 훅에 타입 지정해주기, store/hooks.ts

import { useDispatch, useSelector } from "react-redux"
import type { TypedUseSelectorHook } from 'react-redux'
import type { AppState, AppDispatch } from "."

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

이후 컴포넌트에서 useSelector, useDispatch 훅을 사용할 경우 미리 타입이 선언이 된 useAppDispatch, useAppSelector를 불러와서 사용하도록 한다.

import { useAppDispatch, useAppSelector } from "../store/hooks"
import { changeCurrentLang } from "../store/slices/langSlice"

export default function HomePage() {

  const lang = useAppSelector((state) => state.lang.lang)
  const dispatch = useAppDispatch()
  ...
}
profile
프론트엔드를 희망했었던 화학공학과 취준생

0개의 댓글