jotai-tanstack
Jotai + TanStack Query v5 상태관리 패턴. atoms/, queries/, queryKeys 구조.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Jotai + TanStack Query v5 상태관리 패턴. atoms/, queries/, queryKeys 구조.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
세션 핸드오프. 컨텍스트가 차기 전에 중요 맥락을 progress.md에 문서화하고 새 세션 킥오프 프롬프트를 클립보드에 복사한다. "핸드오프", "세션 정리하고 넘기자", "컨텍스트 정리", "이어갈 준비" 등의 요청에 사용.
MD 파일 또는 텍스트로 작업을 정의하면, 분석 → 디자인 확인 → 구현 → 검증 → 커밋 → PR까지 전체 플로우를 수행한다. "이거 만들어줘", "기능 구현해줘", "작업 시작하자", "이 티켓 진행해줘" 등 코드 작업 착수 발화에 사용.
Claude + OpenAI Codex 협업 스킬. MCP 서버 설정 시에만 사용 가능.
code-forge 상태 대시보드. REFLECT flag, quality 이벤트, notepad/decisions, usage 집계를 한 번에. forge-glow 같은 외부 도구는 --json으로 파싱.
교차 모델 토론. Agent Teams / Codex CLI / self-debate 모드 선택. 설계 결정, 아키텍처 선택 시 활용. "어느 쪽이 나을까", "설계 비교해줘", "토론시켜줘", "교차 검증해줘" 등의 요청에 사용.
화면 단위 E2E 테스트 자동화. Figma/코드 기반 테스트 케이스 도출 → Playwright 코드 생성 → Forge Loop(에스컬레이션 기반 자율 실행). "E2E 돌려줘", "화면 테스트 만들어줘", "이 페이지 자동 테스트" 등의 요청에 사용.
| name | jotai-tanstack |
| description | Jotai + TanStack Query v5 상태관리 패턴. atoms/, queries/, queryKeys 구조. |
| 상태 유형 | 도구 | 위치 |
|---|---|---|
| 서버 상태 (API 데이터) | TanStack Query v5 | packages/shared/queries/ |
| 전역 UI/세션 상태 | Jotai | packages/shared/atoms/ |
| 폼 상태 | React Hook Form | 컴포넌트 내부 |
| 로컬 UI 상태 | useState/useReducer | 컴포넌트 내부 |
packages/shared/
├── queries/
│ └── {도메인}/
│ ├── index.ts # useXxxQuery, useXxxMutation 훅
│ └── queryKeys.ts # 쿼리 키 정의
├── services/
│ └── {도메인}/
│ ├── index.ts # API 호출 함수
│ └── types.ts # 요청/응답 타입
// packages/shared/queries/{도메인}/queryKeys.ts
export const orderKeys = {
all: ['order'] as const,
lists: () => [...orderKeys.all, 'list'] as const,
list: (filters: OrderFilters) => [...orderKeys.lists(), filters] as const,
details: () => [...orderKeys.all, 'detail'] as const,
detail: (id: string) => [...orderKeys.details(), id] as const,
};
// packages/shared/queries/{도메인}/index.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { orderKeys } from './queryKeys';
import { orderService } from '@repo/shared/services/order';
export const useOrderQuery = (orderId: string) => {
return useQuery({
queryKey: orderKeys.detail(orderId),
queryFn: () => orderService.getOrder(orderId),
});
};
export const useUpdateOrderMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: orderService.updateOrder,
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: orderKeys.detail(variables.orderId),
});
},
});
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: 0,
refetchOnWindowFocus: false,
},
},
});
// packages/shared/atoms/uiAtoms.ts
import { atom } from 'jotai';
export const sidebarOpenAtom = atom(false);
export const selectedTabAtom = atom<TabType>('all');
// ❌ 안티패턴: 관련 상태를 개별 atom으로
export const filterTypeAtom = atom('all');
export const filterStartDateAtom = atom(null);
export const filterEndDateAtom = atom(null);
// ✅ 대안: 관련 상태를 하나의 atom으로 그룹화
export const filterAtom = atom<FilterState>({
type: 'all',
startDate: null,
endDate: null,
status: [],
});
// ✅ 좋은 예: 도메인 훅 사용
import { useClearOrderListCache } from '@repo/shared/queries/order';
const Component = () => {
const clearCache = useClearOrderListCache();
const handleRefresh = () => { clearCache(); };
};
// ❌ 나쁜 예: queryClient 직접 조작
const Component = () => {
const queryClient = useQueryClient();
const handleRefresh = () => {
queryClient.invalidateQueries({ queryKey: ['order', 'list'] });
};
};
// ❌ 안티패턴
const [data, setData] = useState(null);
useEffect(() => { fetchData().then(setData); }, []);
// ✅ 대안
const { data, isLoading } = useQuery({ queryKey: ['data'], queryFn: fetchData });
// ❌ 안티패턴
const [filteredItems, setFilteredItems] = useState([]);
useEffect(() => { setFilteredItems(items.filter(item => item.active)); }, [items]);
// ✅ 대안
const filteredItems = useMemo(() => items.filter(item => item.active), [items]);
// ❌ 안티패턴: 서버 데이터를 atom에 동기화
const { data } = useOrderListQuery();
useEffect(() => { if (data) setOrders(data); }, [data]);
// ✅ 대안: React Query를 단일 진실 공급원으로
const { data: orders } = useOrderListQuery();
const activeOrders = useMemo(() => orders?.filter(o => o.status === 'active'), [orders]);
// ❌ 안티패턴: 인라인 객체
useQuery({ queryKey: ['order', { id: orderId, options: { includeDetails: true } }] });
// ✅ 대안: queryKeys 팩토리 함수
useQuery({ queryKey: orderKeys.detail(orderId) });
새로운 상태를 추가할 때 확인: