# React 개발 가이드

## 스택 가정
- React 18+ + TypeScript
- Vite 또는 Next.js
- 상태 관리: Zustand (소규모) / Redux Toolkit (대규모)
- 스타일: Tailwind CSS 또는 shadcn/ui
- API 클라이언트: Axios + TanStack Query (서버 상태)

---

## 폴더 구조 원칙

```
src/
├── pages/          # 라우트 단위 페이지 (Next.js: app/ 또는 pages/)
├── components/
│   ├── ui/         # 재사용 원자 컴포넌트 (Button, Input, Modal)
│   └── features/   # 도메인별 조합 컴포넌트
├── hooks/          # 커스텀 훅 (use 접두사 필수)
├── store/          # Zustand store 또는 Redux slices
├── api/            # API 호출 함수 + TanStack Query hooks
├── types/          # 전역 TypeScript 타입
└── utils/          # 순수 유틸리티 함수
```

---

## 컴포넌트 작성 패턴

### 기본 구조
```tsx
// src/components/features/UserCard.tsx
interface UserCardProps {
  userId: string;
  onSelect?: (id: string) => void;
}

export function UserCard({ userId, onSelect }: UserCardProps) {
  const { data: user } = useUser(userId);  // API 훅
  
  if (!user) return <Skeleton />;
  
  return (
    <div onClick={() => onSelect?.(userId)}>
      {user.name}
    </div>
  );
}
```

### API 훅 패턴 (TanStack Query)
```typescript
// src/api/users.ts
export function useUser(id: string) {
  return useQuery({
    queryKey: ['user', id],
    queryFn: () => api.get<User>(`/api/v1/users/${id}`),
  });
}

export function useCreateUser() {
  return useMutation({
    mutationFn: (data: CreateUserRequest) =>
      api.post<User>('/api/v1/users', data),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
  });
}
```

### Zustand Store 패턴
```typescript
// src/store/authStore.ts
interface AuthState {
  user: User | null;
  token: string | null;
  setAuth: (user: User, token: string) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthState>((set) => ({
  user: null,
  token: null,
  setAuth: (user, token) => set({ user, token }),
  logout: () => set({ user: null, token: null }),
}));
```

---

## API 클라이언트 설정

```typescript
// src/api/client.ts
const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
  timeout: 10000,
});

api.interceptors.request.use((config) => {
  const token = useAuthStore.getState().token;
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

api.interceptors.response.use(
  (res) => res.data,
  (err) => {
    if (err.response?.status === 401) useAuthStore.getState().logout();
    return Promise.reject(err);
  }
);
```

---

## 라우팅 구조 (React Router v6)

```typescript
// src/pages/Router.tsx
const router = createBrowserRouter([
  { path: '/', element: <Layout />, children: [
    { index: true, element: <Home /> },
    { path: 'users', element: <UserList /> },
    { path: 'users/:id', element: <UserDetail /> },
  ]},
  { path: '/login', element: <Login /> },
]);
```

---

## 환경 변수 패턴

```
# .env.local (git 제외)
VITE_API_URL=http://localhost:8080
VITE_APP_NAME=MyApp
```

TypeScript에서 타입 안전하게 접근:
```typescript
// src/config.ts
export const config = {
  apiUrl: import.meta.env.VITE_API_URL,
  appName: import.meta.env.VITE_APP_NAME,
} as const;
```

---

## 컨벤션

| 항목 | 규칙 | 예시 |
|------|------|------|
| 컴포넌트 파일 | PascalCase.tsx | `UserProfile.tsx` |
| 훅 파일 | camelCase.ts, use 접두사 | `useAuth.ts` |
| 유틸 함수 | camelCase.ts | `formatDate.ts` |
| 상수 | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| 인터페이스 | PascalCase, I 접두사 없음 | `User`, `CreateUserRequest` |
