Skip to main content

data

Use when implementing data fetching, API calls, server/client components, or SWR hooks

소스 정보

저장소
trycompai/comp
최근 소스 활동
2026년 4월 27일 13:56
감지된 SKILL.md 언어
영어
스타
2,035
포크
433

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
data
description
Use when implementing data fetching, API calls, server/client components, or SWR hooks
Source Cursor rule: `.cursor/rules/data.mdc`. Original Cursor alwaysApply: `false`. # Data Fetching ## Core Pattern: Server → Client → SWR ### 1. Server Page Fetches Data ```tsx // app/(app)/[orgId]/tasks/page.tsx export default async function TasksPage({ params }: { params: Promise<{ orgId: string }> }) { const { orgId } = await params; // From URL, NOT session const tasks = await getTasks(orgId); return <TaskListClient organizationId={orgId} initialTasks={tasks} />; } ``` ### 2. Client Component Receives Initial Data ```tsx // components/TaskListClient.tsx 'use client'; export function TaskListClient({ organizationId, initialTasks }: Props) { const { tasks, createTask, updateTask } = useTasks({ organizationId, initialData: initialTasks, }); // Initial render is instant - no loading state } ``` ### 3. SWR Hook with fallbackData ```tsx // hooks/useTasks.ts export function useTasks({ organizationId, initialData }: UseTasksOptions) { const { data, mutate } = useSWR( ['/v1/tasks', organizationId], // Include orgId for cache isolation async ([endpoint, orgId]) => { const response = await apiClient.get(endpoint, orgId); return response.data?.tasks ?? []; }, { fallbackData: initialData } ); const createTask = async (input: CreateTaskInput) => { await apiClient.post('/v1/tasks', input, organizationId); mutate(); // Revalidate }; const updateTask = async ({ taskId, input }: { taskId: string; input: UpdateTaskInput }) => { await apiClient.put(`/v1/tasks/${taskId}`, input, organizationId); mutate(); // Revalidate }; return { tasks: data ?? [], createTask, updateTask, mutate }; } ``` ## API Client Use `apiClient` from `@/lib/api-client`: ```tsx import { apiClient } from '@/lib/api-client'; await apiClient.get<ResponseType>('/v1/endpoint', organizationId); await apiClient.post<ResponseType>('/v1/endpoint', body, organizationId); await apiClient.put<ResponseType>('/v1/endpoint', body, organizationId); await apiClient.delete('/v1/endpoint', organizationId); ``` ## Server vs Client Components **Layouts = server.** Interactive logic in separate client components. ```tsx // layout.tsx (server) export default function Layout({ children }) { return ( <PageLayout> <PageHeader title="Title" /> <ClientTabs /> {/* Client component */} {children} </PageLayout> ); } // components/ClientTabs.tsx 'use client'; export function ClientTabs() { const router = useRouter(); // Interactive logic here } ``` ## State Management **No `nuqs`** - use React state or Next.js patterns: ```tsx // ✅ React state for UI const [isOpen, setIsOpen] = useState(false); // ✅ Next.js for URL state const router = useRouter(); const searchParams = useSearchParams(); // ❌ No nuqs import { useQueryState } from 'nuqs'; ``` ## Rules ```tsx // ✅ Always const { orgId } = await params; // From URL params const { data } = useSWR(key, f, { fallbackData }); // With initial data await apiClient.get('/v1/endpoint', orgId); // Use apiClient useSWR(['/v1/tasks', orgId], fetcher); // Include orgId in key // ❌ Never const orgId = session?.activeOrganizationId; // From session const { data } = useSWR('/api/data'); // No initial data await fetch('/api/endpoint'); // Direct fetch ``` ## File Structure ``` app/(app)/[orgId]/tasks/ ├── page.tsx # Server - fetches data ├── components/ │ └── TaskListClient.tsx # Client - receives initialData ├── hooks/ │ └── useTasks.ts # SWR hook with mutations └── data/ └── queries.ts # Server-side queries ```
GitHub에서 보기