AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Redux Rtk

skill-ggombee-code-forge-redux-rtk · by ggombee

Redux Toolkit(RTK) 상태 관리 패턴. createSlice, configureStore, RTK Query(createApi), slices 디렉토리 구조.

No reviews yet
0 installs
35 views
0.0% view→install

Install

$ agentstack add skill-ggombee-code-forge-redux-rtk

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-ggombee-code-forge-redux-rtk)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Redux Rtk? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Redux Toolkit (RTK) 컨벤션


디렉토리 구조

store/
├── index.ts              # configureStore, RootState, AppDispatch
├── slices/
│   ├── authSlice.ts      # 인증 상태
│   ├── uiSlice.ts        # UI 전역 상태
│   └── filterSlice.ts    # 필터 상태
└── api/
    ├── orderApi.ts       # RTK Query API 슬라이스
    └── userApi.ts

configureStore

// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
import authReducer from './slices/authSlice';
import uiReducer from './slices/uiSlice';
import { orderApi } from './api/orderApi';

export const store = configureStore({
  reducer: {
    auth: authReducer,
    ui: uiReducer,
    [orderApi.reducerPath]: orderApi.reducer, // RTK Query
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(orderApi.middleware), // RTK Query 미들웨어
});

// TypeScript 타입 추출
export type RootState = ReturnType;
export type AppDispatch = typeof store.dispatch;

// 타입이 적용된 훅 (컴포넌트에서 사용)
export const useAppDispatch = () => useDispatch();
export const useAppSelector: TypedUseSelectorHook = useSelector;

createSlice

// store/slices/authSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface User {
  id: string;
  name: string;
  email: string;
}

interface AuthState {
  user: User | null;
  isAuthenticated: boolean;
  token: string | null;
}

const initialState: AuthState = {
  user: null,
  isAuthenticated: false,
  token: null,
};

export const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    login: (state, action: PayloadAction) => {
      state.user = action.payload.user;
      state.token = action.payload.token;
      state.isAuthenticated = true;
    },
    logout: (state) => {
      state.user = null;
      state.token = null;
      state.isAuthenticated = false;
    },
    updateUser: (state, action: PayloadAction>) => {
      if (state.user) {
        state.user = { ...state.user, ...action.payload };
      }
    },
  },
});

export const { login, logout, updateUser } = authSlice.actions;
export default authSlice.reducer;

// Selectors
export const selectUser = (state: RootState) => state.auth.user;
export const selectIsAuthenticated = (state: RootState) => state.auth.isAuthenticated;
// store/slices/uiSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface UIState {
  isSidebarOpen: boolean;
  activeTab: string;
  theme: 'light' | 'dark';
}

const initialState: UIState = {
  isSidebarOpen: true,
  activeTab: 'overview',
  theme: 'light',
};

export const uiSlice = createSlice({
  name: 'ui',
  initialState,
  reducers: {
    toggleSidebar: (state) => {
      state.isSidebarOpen = !state.isSidebarOpen;
    },
    setActiveTab: (state, action: PayloadAction) => {
      state.activeTab = action.payload;
    },
    setTheme: (state, action: PayloadAction) => {
      state.theme = action.payload;
    },
  },
});

export const { toggleSidebar, setActiveTab, setTheme } = uiSlice.actions;
export default uiSlice.reducer;

RTK Query (createApi)

// store/api/orderApi.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import type { RootState } from '../index';

interface Order {
  id: string;
  name: string;
  status: string;
}

interface OrderFilters {
  status?: string;
  page?: number;
}

export const orderApi = createApi({
  reducerPath: 'orderApi',
  baseQuery: fetchBaseQuery({
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      // 인증 토큰 자동 첨부
      const token = (getState() as RootState).auth.token;
      if (token) {
        headers.set('Authorization', `Bearer ${token}`);
      }
      return headers;
    },
  }),
  tagTypes: ['Order'], // 캐시 무효화 태그
  endpoints: (builder) => ({
    getOrders: builder.query({
      query: (filters) => ({
        url: '/orders',
        params: filters,
      }),
      providesTags: ['Order'],
    }),
    getOrder: builder.query({
      query: (id) => `/orders/${id}`,
      providesTags: (result, error, id) => [{ type: 'Order', id }],
    }),
    createOrder: builder.mutation>({
      query: (body) => ({
        url: '/orders',
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Order'], // 생성 후 목록 캐시 무효화
    }),
    updateOrder: builder.mutation & { id: string }>({
      query: ({ id, ...body }) => ({
        url: `/orders/${id}`,
        method: 'PATCH',
        body,
      }),
      invalidatesTags: (result, error, { id }) => [{ type: 'Order', id }],
    }),
    deleteOrder: builder.mutation({
      query: (id) => ({
        url: `/orders/${id}`,
        method: 'DELETE',
      }),
      invalidatesTags: ['Order'],
    }),
  }),
});

// 자동 생성된 훅 export
export const {
  useGetOrdersQuery,
  useGetOrderQuery,
  useCreateOrderMutation,
  useUpdateOrderMutation,
  useDeleteOrderMutation,
} = orderApi;

컴포넌트에서 사용

// ✅ 좋은 예: 타입 적용된 훅 사용
import { useAppDispatch, useAppSelector } from '@/store';
import { login, logout, selectUser } from '@/store/slices/authSlice';
import { useGetOrdersQuery, useCreateOrderMutation } from '@/store/api/orderApi';

function OrderListPage() {
  const user = useAppSelector(selectUser);
  const dispatch = useAppDispatch();

  const { data, isLoading, error } = useGetOrdersQuery({ status: 'active' });
  const [createOrder, { isLoading: isCreating }] = useCreateOrderMutation();

  const handleCreate = async (orderData: NewOrder) => {
    try {
      await createOrder(orderData).unwrap(); // unwrap으로 에러 처리
    } catch (err) {
      console.error('생성 실패:', err);
    }
  };

  return (
    
      사용자: {user?.name}
      {isLoading && 로딩 중...}
      {error && 오류 발생}
      {data?.orders.map((order) => (
        
      ))}
    
  );
}

Provider 설정

// src/main.tsx
import { Provider } from 'react-redux';
import { store } from '@/store';

function Root() {
  return (
    
      
    
  );
}

체크리스트

  • [ ] useAppDispatch, useAppSelector 사용 (기본 훅 대신)?
  • [ ] createSlice로 reducer + actions 함께 정의?
  • [ ] RTK Query API에 tagTypes 설정?
  • [ ] mutation 후 invalidatesTags로 캐시 무효화?
  • [ ] RTK Query 미들웨어 configureStore에 추가?
  • [ ] selector 함수를 slice 파일에 함께 정의?

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.