조아마시

쓸모 있는 상세페이지 만들기

웹개발/reactjs

Redux + typescript : 확장 개념

joamashi 2025. 4. 14. 19:48
반응형

 

✅ 1. useAppSelector, useAppDispatch (타입 안전한 Hooks)

Redux를 사용할 때 타입을 명시적으로 설정하면 더 안전하게 사용할 수 있습니다.

// src/app/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'

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

그리고 기존 useSelector, useDispatch 대신 다음과 같이 사용합니다:

const dispatch = useAppDispatch()
const count = useAppSelector((state) => state.counter.value)

✅ 2. 비동기 Thunk (createAsyncThunk)

API 요청을 Redux 상태로 관리하려면 createAsyncThunk를 사용합니다.

// src/features/counter/counterSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'

// 비동기 thunk 생성
export const fetchCount = createAsyncThunk(
  'counter/fetchCount',
  async (amount: number) => {
    const res = await fetch(`https://api.example.com/count?amount=${amount}`)
    const data = await res.json()
    return data.count as number
  }
)

interface CounterState {
  value: number
  loading: boolean
}

const initialState: CounterState = {
  value: 0,
  loading: false,
}

const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchCount.pending, (state) => {
        state.loading = true
      })
      .addCase(fetchCount.fulfilled, (state, action) => {
        state.value = action.payload
        state.loading = false
      })
      .addCase(fetchCount.rejected, (state) => {
        state.loading = false
      })
  },
})

✅ 3. Redux Persist (데이터 로컬 저장)

앱을 새로고침해도 Redux 상태가 유지되도록 하려면 redux-persist를 사용합니다.

설치

npm install redux-persist

설정

// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import counterReducer from '../features/counter/counterSlice'
import { combineReducers } from 'redux'

const rootReducer = combineReducers({
  counter: counterReducer,
})

const persistConfig = {
  key: 'root',
  storage,
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export const store = configureStore({
  reducer: persistedReducer,
})

export const persistor = persistStore(store)

index.tsx 연결

import { PersistGate } from 'redux-persist/integration/react'
import { store, persistor } from './app/store'

<Provider store={store}>
  <PersistGate loading={null} persistor={persistor}>
    <App />
  </PersistGate>
</Provider>

✅ 4. 모듈화 구조 (기능 단위 구조)

기능별 디렉토리로 구성해 유지 보수를 쉽게 할 수 있습니다.

src/
├── app/
│   ├── store.ts
│   └── hooks.ts
├── features/
│   ├── counter/
│   │   ├── counterSlice.ts
│   │   └── Counter.tsx
│   └── user/
│       ├── userSlice.ts
│       └── UserInfo.tsx

각 슬라이스는 독립적으로 관리하며 store.ts에서 한 번에 조합:

import userReducer from '../features/user/userSlice'
import counterReducer from '../features/counter/counterSlice'

const rootReducer = combineReducers({
  user: userReducer,
  counter: counterReducer,
})

정리 요약

useAppSelector, useAppDispatch 타입 안정성 강화
createAsyncThunk API 비동기 요청 처리
redux-persist 새로고침에도 상태 유지
모듈화 구조 기능별 폴더로 관리

 

728x90
반응형