# Cursor Rules for ERP-node Project
## π¨ λΉμ¦λμ€ λ‘μ§ μμ² μμ κ²μ¦ (νμ)
**μ¬μ©μκ° νλ©΄ κ°λ° λλ λΉμ¦λμ€ λ‘μ§ κ΅¬νμ μμ²ν λ, μλ μμμ λ°λ₯΄μ§ μμΌλ©΄ λ°λμ λ€μκ³Ό κ°μ΄ μλ΅νμΈμ:**
```
μλ
νμΈμ. Oh My Master! μμμ λͺ» μμ λ£κ² μ΅λλ€.
λ€μ νλ² μμ±ν΄μ£ΌμμΌ.
=== λΉμ¦λμ€ λ‘μ§ μμ²μ ===
γνλ©΄ μ 보γ
- νλ©΄λͺ
:
- νμ¬μ½λ:
- λ©λ΄ID (μμΌλ©΄):
γν
μ΄λΈ μ 보γ
- λ©μΈ ν
μ΄λΈ:
- λν
μΌ ν
μ΄λΈ (μμΌλ©΄):
- κ΄κ³ FK (μμΌλ©΄):
γλ²νΌ λͺ©λ‘γ
λ²νΌ1:
- λ²νΌλͺ
:
- λμ μ ν: (μ μ₯/μμ /μμ /μ‘°ν/κΈ°ν)
- 쑰건 (μμΌλ©΄):
- λμ ν
μ΄λΈ:
- μΆκ° λμ (μμΌλ©΄):
γμΆκ° μꡬμ¬νγ
-
```
**μμ λ―Έμ€μ νλ¨ κΈ°μ€:**
1. "νλ©΄ λ§λ€μ΄μ€" κ°μ΄ ν
μ΄λΈλͺ
/λ²νΌ μ 보 μμ΄ μμ²
2. "μ μ₯νλ©΄ μ μ₯ν΄μ€" κ°μ΄ ꡬ체μ μΈ ν
μ΄λΈ/λ‘μ§ μ€λͺ
μμ
3. "μ΄μ μ΄λ λΉμ·νκ²" κ°μ΄ λͺ¨νΈν μ°Έμ‘°
4. λ²νΌλ³ 쑰건/λμμ΄ λͺ
μλμ§ μμ
**μμ λ―Έμ€μ μ μ λ μμ
μ§ννμ§ λ§κ³ , μ μμμ 보μ¬μ£Όλ©° λ€μ μμ±νλΌκ³ μμ²νμΈμ.**
**μμΈ κ°μ΄λ**: [νλ©΄κ°λ°_νμ€_κ°μ΄λ.md](docs/screen-implementation-guide/νλ©΄κ°λ°_νμ€_κ°μ΄λ.md)
---
## π¨ μ΅μ°μ 보μ κ·μΉ: λ©ν°ν
λμ
**λͺ¨λ μ½λ μμ±/μμ μλ£ ν λ°λμ λ€μ νμΌμ νμΈνμΈμ:**
- [λ©ν°ν
λμ νμ ꡬν κ°μ΄λ](.cursor/rules/multi-tenancy-guide.mdc)
**AI μμ΄μ νΈλ λ€μ μν©μμ λ°λμ λ©ν°ν
λμ 체ν¬λ¦¬μ€νΈλ₯Ό νμΈν΄μΌ ν©λλ€:**
1. λ°μ΄ν°λ² μ΄μ€ λ§μ΄κ·Έλ μ΄μ
μμ± μ
2. λ°±μλ API (SELECT/INSERT/UPDATE/DELETE) μμ±/μμ μ
3. νλ‘ νΈμλ λ°μ΄ν° API νΈμΆ μμ±/μμ μ
4. ν
μ€νΈ μλ£ μ
**ν΅μ¬ μμΉ:**
- β
λͺ¨λ ν
μ΄λΈμ `company_code` νμ (company_mng μ μΈ)
- β
λͺ¨λ 쿼리μ `company_code` νν°λ§ νμ
- β
νλ‘ νΈμλ API νΈμΆ μ `autoFilter` μ λ¬ νμ
---
## shadcn/ui μΉ μ€νμΌ κ°μ΄λλΌμΈ
λͺ¨λ νλ‘ νΈμλ κ°λ° μ λ€μ shadcn/ui κΈ°λ° μ€νμΌ κ°μ΄λλΌμΈμ μ€μν΄μΌ ν©λλ€.
### 1. Color System (μμ μμ€ν
)
#### CSS Variables μ¬μ©
shadcnμ CSS Variablesλ₯Ό μ¬μ©νμ¬ ν
λ§λ₯Ό κ΄λ¦¬νλ©°, λͺ¨λ μμμ HSL νμμΌλ‘ μ μλ©λλ€.
**κΈ°λ³Έ μμ ν ν° (νμ μ¬μ©):**
- `--background`: νμ΄μ§ λ°°κ²½
- `--foreground`: κΈ°λ³Έ ν
μ€νΈ
- `--primary`: λ©μΈ μ‘μ
(Indigo κ³μ΄)
- `--primary-foreground`: Primary μ ν
μ€νΈ
- `--secondary`: 보쑰 μ‘μ
- `--muted`: μ½ν λ°°κ²½
- `--muted-foreground`: 보쑰 ν
μ€νΈ
- `--destructive`: μμ /μλ¬ (Rose κ³μ΄)
- `--border`: ν
λ리
- `--ring`: ν¬μ»€μ€ λ§
**Tailwind ν΄λμ€λ‘ μ¬μ©:**
```tsx
bg-primary text-primary-foreground
bg-secondary text-secondary-foreground
bg-muted text-muted-foreground
bg-destructive text-destructive-foreground
border-border
```
**μΆκ° μλ§¨ν± μ»¬λ¬:**
- Success: `--success` (Emerald-600 κ³μ΄)
- Warning: `--warning` (Amber-500 κ³μ΄)
- Info: `--info` (Cyan-500 κ³μ΄)
### 2. Spacing System (κ°κ²©)
**Tailwind Scale (4px κΈ°μ€):**
- 0.5 = 2px, 1 = 4px, 2 = 8px, 3 = 12px, 4 = 16px, 6 = 24px, 8 = 32px
**μ»΄ν¬λνΈλ³ κΆμ₯ κ°κ²©:**
- μΉ΄λ ν¨λ©: `p-6` (24px)
- μΉ΄λ κ° λ§μ§: `gap-6` (24px)
- νΌ νλ κ°κ²©: `space-y-4` (16px)
- λ²νΌ λ΄λΆ ν¨λ©: `px-4 py-2`
- μΉμ
κ°κ²©: `space-y-8` λλ `space-y-12`
### 3. Typography (νμ΄ν¬κ·ΈλνΌ)
**μ©λλ³ νμ΄ν¬κ·ΈλνΌ (νμ):**
- νμ΄μ§ μ λͺ©: `text-3xl font-bold`
- μΉμ
μ λͺ©: `text-2xl font-semibold`
- μΉ΄λ μ λͺ©: `text-xl font-semibold`
- μλΈ μ λͺ©: `text-lg font-medium`
- λ³Έλ¬Έ ν
μ€νΈ: `text-sm text-muted-foreground`
- μμ ν
μ€νΈ: `text-xs text-muted-foreground`
- λ²νΌ ν
μ€νΈ: `text-sm font-medium`
- νΌ λΌλ²¨: `text-sm font-medium`
### 4. Button Variants (λ²νΌ μ€νμΌ)
**νμ μ¬μ© ν¨ν΄:**
```tsx
// Primary (κΈ°λ³Έ)
// Secondary
// Outline
// Ghost
// Destructive
```
**λ²νΌ ν¬κΈ°:**
- `size="sm"`: μμ λ²νΌ (h-9 px-3)
- `size="default"`: κΈ°λ³Έ λ²νΌ (h-10 px-4 py-2)
- `size="lg"`: ν° λ²νΌ (h-11 px-8)
- `size="icon"`: μμ΄μ½ μ μ© (h-10 w-10)
### 5. Input States (μ
λ ₯ νλ μν)
**νμ μ μ© μν:**
```tsx
// Default
className="border-input"
// Focus (λͺ¨λ μ
λ ₯ νλ νμ)
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
// Error
className="border-destructive focus-visible:ring-destructive"
// Disabled
className="disabled:opacity-50 disabled:cursor-not-allowed"
```
### 6. Card Structure (μΉ΄λ ꡬ쑰)
**νμ€ μΉ΄λ ꡬ쑰 (νμ):**
```tsx
μ λͺ©
μ€λͺ
{/* λ΄μ© */}
{/* μ‘μ
λ²νΌλ€ */}
```
### 7. Border & Radius (ν
λ리 λ° λ₯κ·Ό λͺ¨μ리)
**μ»΄ν¬λνΈλ³ κΆμ₯:**
- λ²νΌ: `rounded-md` (6px)
- μ
λ ₯ νλ: `rounded-md` (6px)
- μΉ΄λ: `rounded-lg` (8px)
- λ°°μ§: `rounded-full`
- λͺ¨λ¬/λνμμ: `rounded-lg` (8px)
- λλ‘λ€μ΄: `rounded-md` (6px)
### 8. Shadow (κ·Έλ¦Όμ)
**μ©λλ³ κΆμ₯:**
- μΉ΄λ: `shadow-sm`
- λλ‘λ€μ΄: `shadow-md`
- λͺ¨λ¬: `shadow-lg`
- νμ€λ²: `shadow-md`
- λ²νΌ νΈλ²: `shadow-sm`
### 9. Interactive States (μνΈμμ© μν)
**νμ μ μ© ν¨ν΄:**
```tsx
// Hover
hover:bg-primary/90 // λ²νΌ
hover:bg-accent // Ghost λ²νΌ
hover:underline // λ§ν¬
hover:shadow-md transition-shadow // μΉ΄λ
// Focus (λͺ¨λ μΈν°λν°λΈ μμ νμ)
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
// Active
active:scale-95 transition-transform // λ²νΌ
// Disabled
disabled:opacity-50 disabled:cursor-not-allowed
```
### 10. Animation (μ λλ©μ΄μ
)
**κΆμ₯ Duration:**
- λΉ λ₯Έ νΌλλ°±: `duration-75`
- κΈ°λ³Έ: `duration-150`
- λΆλλ¬μ΄ μ ν: `duration-300`
**κΆμ₯ ν¨ν΄:**
- λ²νΌ ν΄λ¦: `transition-transform duration-150 active:scale-95`
- μμ μ ν: `transition-colors duration-150`
- λλ‘λ€μ΄ μ΄κΈ°: `transition-all duration-200`
### 11. Responsive (λ°μν)
**Breakpoints:**
- `sm`: 640px (λͺ¨λ°μΌ κ°λ‘)
- `md`: 768px (νλΈλ¦Ώ)
- `lg`: 1024px (λ
ΈνΈλΆ)
- `xl`: 1280px (λ°μ€ν¬ν±)
**λ°μν ν¨ν΄:**
```tsx
// λͺ¨λ°μΌ μ°μ μ κ·Ό
className="flex-col md:flex-row"
className="grid-cols-1 md:grid-cols-2 lg:grid-cols-3"
className="text-2xl md:text-3xl lg:text-4xl"
className="p-4 md:p-6 lg:p-8"
```
### 12. Accessibility (μ κ·Όμ±)
**νμ μ μ© μ¬ν:**
1. ν¬μ»€μ€ νμ: λͺ¨λ μΈν°λν°λΈ μμμ `focus-visible:ring-2` μ μ©
2. ARIA λ μ΄λΈ: μ μ ν `aria-label`, `aria-describedby` μ¬μ©
3. ν€λ³΄λ λ€λΉκ²μ΄μ
: Tab, Enter, Space, Esc μ§μ
4. μμ λλΉ: μ΅μ 4.5:1 (μΌλ° ν
μ€νΈ), 3:1 (ν° ν
μ€νΈ)
### 13. Class μμ (μΌκ΄μ± μ μ§)
**νμ μ΄ μμλ‘ μμ±:**
1. Layout: `flex`, `grid`, `block`
2. Sizing: `w-full`, `h-10`
3. Spacing: `p-4`, `m-2`, `gap-4`
4. Typography: `text-sm`, `font-medium`
5. Colors: `bg-primary`, `text-white`
6. Border: `border`, `rounded-md`
7. Effects: `shadow-sm`, `opacity-50`
8. States: `hover:`, `focus:`, `disabled:`
9. Responsive: `md:`, `lg:`
### 14. μ€λ¬΄ μ μ© κ·μΉ
1. **shadcn μ»΄ν¬λνΈ μ°μ μ¬μ©**: 컀μ€ν
μ€νμΌλ³΄λ€ shadcn κΈ°λ³Έ μ»΄ν¬λνΈ νμ©
2. **cn μ νΈλ¦¬ν° μ¬μ©**: μ‘°κ±΄λΆ ν΄λμ€λ `cn()` ν¨μλ‘ κ²°ν©
3. **ν
λ§ λ³μ μ¬μ©**: νλμ½λ©λ μμ λμ CSS λ³μ μ¬μ©
4. **λ€ν¬λͺ¨λ κ³ λ €**: λͺ¨λ μ»΄ν¬λνΈλ λ€ν¬λͺ¨λ νΈν νμ
5. **μΌκ΄μ± μ μ§**: κ°μ μ©λμ μ»΄ν¬λνΈλ κ°μ μ€νμΌ μ¬μ©
### 15. κΈμ§ μ¬ν
1. β νλμ½λ©λ μμ κ° μ¬μ© (μ: `bg-blue-500` λμ `bg-primary`)
2. β μΈλΌμΈ μ€νμΌλ‘ μμ μ§μ (μ: `style={{ color: '#3b82f6' }}`)
3. β ν¬μ»€μ€ μ€νμΌ μ κ±° (`outline-none`λ§ λ¨λ
μ¬μ©)
4. β μ κ·Όμ± λ¬΄μ (ARIA λ μ΄λΈ λλ½)
5. β λ°μν 무μ (λ°μ€ν¬ν± μ μ© μ€νμΌ)
6. β **μ€μ²© λ°μ€ κΈμ§**: μ¬μ©μκ° λͺ
μμ μΌλ‘ μμ²νμ§ μλ ν Card μμ Card, Border μμ Border κ°μ μ€μ²©λ 컨ν
μ΄λ ꡬ쑰λ₯Ό λ§λ€μ§ μμ
### 16. μ€μ²© λ°μ€ κΈμ§ μμΈ κ·μΉ
**κΈμ§λλ ν¨ν΄ (μ¬μ©μ μμ² μμ΄):**
```tsx
// β Card μμ Card
// μ€μ²© κΈμ§!
λ΄μ©
// β Border μμ Border
// β λΆνμν λνΌ
```
**νμ©λλ ν¨ν΄:**
```tsx
// β
λ¨μΌ Card
μ λͺ©
λ΄μ©
// β
μλ―Έμ μΌλ‘ λ€λ₯Έ μ»΄ν¬λνΈ μ‘°ν©
// β
그리λ/리μ€νΈ λ΄λΆμ Cardλ€
νλͺ© 1
νλͺ© 2
νλͺ© 3
```
**μμΈ μν© (μ¬μ©μκ° λͺ
μμ μΌλ‘ μμ²ν κ²½μ°λ§):**
- λμ보λμμ μΉμ
λ³ κ·Έλ£Ήνμ΄ νμν κ²½μ°
- 볡μ‘ν λ°μ΄ν° ꡬ쑰λ₯Ό μκ°μ μΌλ‘ ꡬλΆν΄μΌ νλ κ²½μ°
- λλκ·Έμ€λλ‘ λ± νΉμ κΈ°λ₯μ μν κ²½μ°
**μμΉ:**
- μ¬ννκ³ κΉλν λμμΈ μ μ§
- λΆνμν μκ°μ λ μ΄μ΄ μ κ±°
- μ¬μ©μκ° λͺ
μμ μΌλ‘ "λ°μ€ μμ λ°μ€", "μ€μ²©λ μΉ΄λ" λ±μ μμ²νμ§ μμΌλ©΄ λ¨μΌ λ 벨 μ μ§
### 17. νμ€ λͺ¨λ¬(Dialog) λμμΈ ν¨ν΄
**νλ‘μ νΈ νμ€ λͺ¨λ¬ ꡬ쑰 (νλ‘μ° κ΄λ¦¬ κΈ°μ€):**
```tsx
```
**νμ μ μ© μ¬ν:**
1. **λ°μν ν¬κΈ°**
- λͺ¨λ°μΌ: `max-w-[95vw]` (νλ©΄ λλΉμ 95%)
- λ°μ€ν¬ν±: `sm:max-w-[500px]` (κ³ μ 500px)
2. **ν€λ ꡬ쑰**
- DialogTitle: `text-base sm:text-lg` (16px β 18px)
- DialogDescription: `text-xs sm:text-sm` (12px β 14px)
- νμ μ λͺ©κ³Ό μ€λͺ
λͺ¨λ ν¬ν¨
3. **컨ν
μΈ κ°κ²©**
- νλ κ° κ°κ²©: `space-y-3 sm:space-y-4` (12px β 16px)
- κ° νλλ `` λ‘ κ°μΈκΈ°
4. **μ
λ ₯ νλ ν¨ν΄**
- Label: `text-xs sm:text-sm` + νμ νλλ `*` νμ
- Input/Select: `h-8 text-xs sm:h-10 sm:text-sm` (32px β 40px)
- λμλ§: `text-muted-foreground mt-1 text-[10px] sm:text-xs`
5. **νΈν° λ²νΌ**
- 컨ν
μ΄λ: `gap-2 sm:gap-0` (λͺ¨λ°μΌμμ κ°κ²©, λ°μ€ν¬ν±μμ μλ)
- λ²νΌ: `h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm`
- λͺ¨λ°μΌ: κ°μ ν¬κΈ° (`flex-1`)
- λ°μ€ν¬ν±: μλ ν¬κΈ° (`flex-none`)
- μμ: μ·¨μ(outline) β νμΈ(default)
6. **μ κ·Όμ±**
- Labelμ `htmlFor`μ Inputμ `id` λ§€μΉ
- Buttonμ μ μ ν `onClick` νΈλ€λ¬
- Dialogμ `open`κ³Ό `onOpenChange` νμ
**νμΈ λͺ¨λ¬ (κ°λ¨ν κ²½κ³ /νμΈ):**
```tsx
```
**μμΉ:**
- λͺ¨λ λͺ¨λ¬μ λͺ¨λ°μΌ μ°μ λ°μν λμμΈ
- μΌκ΄λ ν¬κΈ°, κ°κ²©, ν°νΈ ν¬κΈ° μ¬μ©
- μ¬μ©μκ° λ€λ₯Έ ν¬κΈ°λ₯Ό λͺ
μνμ§ μμΌλ©΄ `sm:max-w-[500px]` μ¬μ©
- μμ /μνν μμ
μ `variant="destructive"` μ¬μ©
### 18. κ²μ κ°λ₯ν Select λ°μ€ (Combobox ν¨ν΄)
**μ μ© μ‘°κ±΄**: μ¬μ©μκ° "κ²μ κΈ°λ₯μ΄ μλ Select λ°μ€" λλ "Combobox"λ₯Ό λͺ
μμ μΌλ‘ μμ²ν κ²½μ°λ§ μ¬μ©
**νμ€ Combobox ꡬ쑰 (νλ‘μ° κ΄λ¦¬ κΈ°μ€):**
```tsx
import { Check, ChevronsUpDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
// μν κ΄λ¦¬
const [open, setOpen] = useState(false);
const [value, setValue] = useState("");
// λ λλ§
νλͺ©μ μ°Ύμ μ μμ΅λλ€.
{items.map((item) => (
{
setValue(currentValue === value ? "" : currentValue);
setOpen(false);
}}
className="text-xs sm:text-sm"
>
{item.label}
))}
```
**볡μ‘ν λ°μ΄ν° νμ (λΌλ²¨ + μ€λͺ
):**
```tsx
{
setValue(currentValue);
setOpen(false);
}}
className="text-xs sm:text-sm"
>
{item.label}
{item.description && (
{item.description}
)}
```
**νμ μ μ© μ¬ν:**
1. **λ°μν ν¬κΈ°**
- λ²νΌ λμ΄: `h-8 sm:h-10` (32px β 40px)
- ν
μ€νΈ ν¬κΈ°: `text-xs sm:text-sm` (12px β 14px)
- PopoverContent λλΉ: `width: "var(--radix-popover-trigger-width)"` (νΈλ¦¬κ±°μ λμΌ)
2. **νμ μ»΄ν¬λνΈ**
- Popover: λλ‘λ€μ΄ 컨ν
μ΄λ
- Command: κ²μ λ° νν°λ§ κΈ°λ₯
- CommandInput: κ²μ μ
λ ₯ νλ
- CommandList: νλͺ© λͺ©λ‘ 컨ν
μ΄λ
- CommandEmpty: κ²μ κ²°κ³Ό μμ λ©μμ§
- CommandGroup: νλͺ© κ·Έλ£Ή
- CommandItem: κ°λ³ νλͺ©
3. **μμ΄μ½ μ¬μ©**
- ChevronsUpDown: λλ‘λ€μ΄ νμ μμ΄μ½ (μ€λ₯Έμͺ½)
- Check: μ νλ νλͺ© νμ (μΌμͺ½)
4. **μ κ·Όμ±**
- `role="combobox"`: ARIA μν λͺ
μ
- `aria-expanded={open}`: μ΄λ¦Ό/λ«ν μν
- PopoverTriggerμ `asChild` μ¬μ©
5. **λ‘λ© μν**
```tsx
```
**μΌλ° Select vs Combobox μ ν κΈ°μ€:**
| μν© | μ»΄ν¬λνΈ | μ΄μ |
|------|----------|------|
| νλͺ© 5κ° μ΄ν | `