Loading...
;
}
// 5. JSX ๋ฐํ
return (
```
### **2. ์์ ์ฌ์ฉ**
```typescript
// โ
CSS ๋ณ์ ์ฌ์ฉ (ํ
๋ง ๋์)
className="bg-primary text-primary-foreground"
className="bg-card text-card-foreground"
className="bg-destructive text-destructive-foreground"
className="border-border"
className="text-muted-foreground"
// โ ํ๋์ฝ๋ฉ๋ ์์ (ํผํ๊ธฐ)
className="bg-blue-500 text-white"
```
### **3. ๋ฐ์ํ ๋์์ธ**
```typescript
// Mobile First ์ ๊ทผ
className="
p-4 // ๋ชจ๋ฐ์ผ: 16px padding
md:p-6 // ํ๋ธ๋ฆฟ: 24px padding
lg:p-8 // ๋ฐ์คํฌํฑ: 32px padding
flex-col // ๋ชจ๋ฐ์ผ: ์ธ๋ก ๋ฐฉํฅ
md:flex-row // ํ๋ธ๋ฆฟ+: ๊ฐ๋ก ๋ฐฉํฅ
text-sm // ๋ชจ๋ฐ์ผ: ์์ ํ
์คํธ
md:text-base // ํ๋ธ๋ฆฟ+: ๊ธฐ๋ณธ ํฌ๊ธฐ
"
```
### **4. ๊ฐ๊ฒฉ ์์คํ
**
```typescript
// Spacing Scale (4px ๋จ์)
className="p-2" // 8px
className="p-4" // 16px
className="p-6" // 24px
className="p-8" // 32px
className="gap-2" // 8px
className="gap-4" // 16px
className="gap-6" // 24px
```
### **5. ํฐํธ ํฌ๊ธฐ/๊ตต๊ธฐ ์ฌ์ฉ ์ ํ**
```typescript
// โ ๏ธ ์ฃผ์: ํฐํธ ํฌ๊ธฐ, ๊ตต๊ธฐ, ๋ผ์ธ ๋์ด๋ ๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ
// globals.css์ ์ ์๋ ๊ธฐ๋ณธ ์คํ์ผ ํ์ฉ
// โ ํผํ๊ธฐ (ํน๋ณํ ์๊ตฌ์ฌํญ ์์ผ๋ฉด)
className="text-2xl font-bold leading-tight"
// โ
๊ถ์ฅ (๊ธฐ๋ณธ๊ฐ ์ฌ์ฉ)
์ ๋ชฉ
// globals.css์ h1 ์คํ์ผ ์๋ ์ ์ฉ
๋ด์ฉ
// globals.css์ p ์คํ์ผ ์๋ ์ ์ฉ
```
---
## ๐ TypeScript ๊ฐ์ด๋
### **1. ํ์
์ ์**
#### **Interface vs Type**
```typescript
// โ
Props๋ interface ์ฌ์ฉ (ํ์ฅ ๊ฐ๋ฅ)
interface ButtonProps {
label: string;
onClick?: () => void;
variant?: "primary" | "secondary";
}
// โ
์ ๋์จ ํ์
์ type ์ฌ์ฉ
type Status = "active" | "inactive" | "pending";
// โ
๋ณต์กํ ํ์
์ type ์ฌ์ฉ
type ComplexType = {
id: string;
data: Record
;
} & BaseType;
```
#### **ํ์
์ฌ์ฌ์ฉ**
```typescript
// ๊ณตํต ํ์
์ ์
interface BaseEntity {
id: string;
createdAt: string;
updatedAt: string;
}
interface Product extends BaseEntity {
name: string;
price: number;
}
interface Order extends BaseEntity {
productId: string;
quantity: number;
}
```
### **2. ํ์
์์ ์ฑ**
#### **โ
DO: ๋ช
์์ ํ์
์ง์ **
```typescript
// State ํ์
๋ช
์
const [items, setItems] = useState([]);
const [selectedId, setSelectedId] = useState(null);
// ํจ์ ๋งค๊ฐ๋ณ์ ํ์
๋ช
์
function calculateTotal(items: Product[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
// ์ด๋ฒคํธ ํธ๋ค๋ฌ ํ์
const handleClick = (event: React.MouseEvent) => {
console.log(event.currentTarget);
};
```
#### **โ DON'T: any ํ์
์ฌ์ฉ**
```typescript
// โ ํผํ๊ธฐ
const [data, setData] = useState([]);
function process(input: any): any { }
// โ
๋์ ์ฌ์ฉ
const [data, setData] = useState([]);
function process(input: unknown): Result { }
```
### **3. ์ต์
๋ ์ฒด์ด๋ & Nullish Coalescing**
```typescript
// โ
์ต์
๋ ์ฒด์ด๋
const userName = user?.profile?.name;
// โ
Nullish Coalescing
const displayName = user?.name ?? "Guest";
// โ
์ต์
๋ ์ฝ๋ฐฑ
onSave?.();
```
---
## ๐ ์ํ ๊ด๋ฆฌ
### **1. useState ํจํด**
```typescript
// โ
๋จ์ ์ํ
const [isOpen, setIsOpen] = useState(false);
const [count, setCount] = useState(0);
// โ
๊ฐ์ฒด ์ํ (๋ถ๋ณ์ฑ ์ ์ง)
const [user, setUser] = useState({ name: "", email: "" });
const updateName = (newName: string) => {
setUser(prev => ({ ...prev, name: newName }));
};
// โ
๋ฐฐ์ด ์ํ
const [items, setItems] = useState- ([]);
const addItem = (item: Item) => {
setItems(prev => [...prev, item]);
};
const removeItem = (id: string) => {
setItems(prev => prev.filter(item => item.id !== id));
};
```
### **2. useEffect ํจํด**
```typescript
// โ
์ปดํฌ๋ํธ ๋ง์ดํธ ์ 1ํ ์คํ
useEffect(() => {
// ์ด๊ธฐ ๋ฐ์ดํฐ ๋ก๋
fetchData();
}, []); // ๋น ์์กด์ฑ ๋ฐฐ์ด
// โ
ํน์ ๊ฐ ๋ณ๊ฒฝ ์ ์คํ
useEffect(() => {
if (selectedId) {
loadDetails(selectedId);
}
}, [selectedId]); // selectedId ๋ณ๊ฒฝ ๊ฐ์ง
// โ
ํด๋ฆฐ์
ํจ์
useEffect(() => {
const timer = setInterval(() => {
// ์ฃผ๊ธฐ์ ์์
}, 1000);
return () => {
clearInterval(timer); // ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์ ๋ฆฌ
};
}, []);
```
### **3. ๋ก์ปฌ ์ํ vs Props**
```typescript
// โ
๋ก์ปฌ์์๋ง ์ฌ์ฉ๋๋ UI ์ํ
const [isHovered, setIsHovered] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
// โ
๋ถ๋ชจ๋ก๋ถํฐ ๋ฐ๋ ๋ฐ์ดํฐ
interface Props {
data: Product[]; // ์ฝ๊ธฐ ์ ์ฉ ๋ฐ์ดํฐ
onUpdate: () => void; // ์ํ ๋ณ๊ฒฝ์ ๋ถ๋ชจ๊ฐ ์ฒ๋ฆฌ
}
```
---
## ๐ ํ์ผ ๋ช
๋ช
๊ท์น
### **1. ์ปดํฌ๋ํธ ํ์ผ**
```
PascalCase.tsx
โโโ Dashboard.tsx
โโโ SalesManagement.tsx
โโโ ProductionManagement.tsx
โโโ UserProfile.tsx
```
### **2. UI ์ปดํฌ๋ํธ (shadcn/ui)**
```
kebab-case.tsx
โโโ button.tsx
โโโ dialog.tsx
โโโ table.tsx
โโโ dropdown-menu.tsx
```
### **3. ์ ํธ๋ฆฌํฐ ํ์ผ**
```
camelCase.ts / kebab-case.ts
โโโ utils.ts
โโโ helpers.ts
โโโ use-mobile.ts
```
### **4. ๋ฌธ์ ํ์ผ**
```
UPPER_CASE.md / PascalCase.md
โโโ README.md
โโโ TECH_STACK.md
โโโ DEVELOPMENT_ENVIRONMENT.md
โโโ Guidelines.md
```
### **5. ์คํ์ผ ํ์ผ**
```
kebab-case.css / lowercase.css
โโโ globals.css
โโโ custom-styles.css
```
---
## ๐ Git ์ํฌํ๋ก์ฐ
### **1. ๋ธ๋์น ์ ๋ต**
```bash
main # ํ๋ก๋์
๋ธ๋์น
โโโ develop # ๊ฐ๋ฐ ๋ธ๋์น
โโโ feature/* # ๊ธฐ๋ฅ ๊ฐ๋ฐ
โโโ bugfix/* # ๋ฒ๊ทธ ์์
โโโ hotfix/* # ๊ธด๊ธ ์์
```
### **2. ์ปค๋ฐ ๋ฉ์์ง ๊ท์น**
```bash
# ํ์: <ํ์
>: <์ ๋ชฉ>
feat: ํ๋งค๊ด๋ฆฌ ๊ฒฌ์ ์ฐ์ถ ๊ธฐ๋ฅ ์ถ๊ฐ
fix: ์์ฐ๊ด๋ฆฌ ๋ชจ๋ฌ ์คํฌ๋กค ์ค๋ฅ ์์
style: Dashboard ๋ ์ด์์ ๊ฐ์
refactor: MaterialManagement ์ปดํฌ๋ํธ ๋ฆฌํฉํ ๋ง
docs: README ์
๋ฐ์ดํธ
test: QualityManagement ํ
์คํธ ์ถ๊ฐ
chore: ์์กด์ฑ ์
๋ฐ์ดํธ
```
### **3. ์ปค๋ฐ ํ์
**
- `feat`: ์๋ก์ด ๊ธฐ๋ฅ
- `fix`: ๋ฒ๊ทธ ์์
- `style`: UI/์คํ์ผ ๋ณ๊ฒฝ
- `refactor`: ์ฝ๋ ๋ฆฌํฉํ ๋ง
- `docs`: ๋ฌธ์ ์์
- `test`: ํ
์คํธ ์ถ๊ฐ/์์
- `chore`: ๋น๋, ์ค์ ๋ฑ
---
## ๐งช ํ
์คํ
### **1. ์ปดํฌ๋ํธ ํ
์คํธ ์ฒดํฌ๋ฆฌ์คํธ**
```typescript
// ์๋ ํ
์คํธ ์ฒดํฌ๋ฆฌ์คํธ
// [ ] ์ปดํฌ๋ํธ๊ฐ ์ ์์ ์ผ๋ก ๋ ๋๋ง๋๋๊ฐ?
// [ ] Props๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ์ ๋ฌ๋๋๊ฐ?
// [ ] ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ์๋ํ๋๊ฐ?
// [ ] ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ด ์ฌ๋ฐ๋ฅธ๊ฐ?
// [ ] ์๋ฌ ์ํ๊ฐ ์ฒ๋ฆฌ๋๋๊ฐ?
// [ ] ๋ก๋ฉ ์ํ๊ฐ ํ์๋๋๊ฐ?
```
### **2. ๋ฐ์ํ ํ
์คํธ**
```
๋ธ๋ ์ดํฌํฌ์ธํธ ํ
์คํธ:
[ ] Mobile (< 768px)
[ ] Tablet (768px - 1024px)
[ ] Desktop (> 1024px)
๊ธฐ๊ธฐ๋ณ ํ
์คํธ:
[ ] iPhone (Safari)
[ ] Android (Chrome)
[ ] iPad (Safari)
[ ] Desktop (Chrome, Firefox, Safari)
```
### **3. ๋ธ๋ผ์ฐ์ ํธํ์ฑ**
```
[ ] Chrome (์ต์ )
[ ] Firefox (์ต์ )
[ ] Safari (์ต์ )
[ ] Edge (์ต์ )
```
---
## โก ์ฑ๋ฅ ์ต์ ํ
### **1. ๋ฆฌ๋ ๋๋ง ์ต์ ํ**
```typescript
// โ
useMemo - ๋น์ฉ์ด ํฐ ๊ณ์ฐ ๋ฉ๋ชจ์ด์ ์ด์
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
// โ
useCallback - ํจ์ ๋ฉ๋ชจ์ด์ ์ด์
const handleClick = useCallback((id: string) => {
console.log("Clicked:", id);
}, []);
// โ
React.memo - ์ปดํฌ๋ํธ ๋ฉ๋ชจ์ด์ ์ด์
export const MemoizedComponent = memo(function MyComponent({ data }: Props) {
return
{data}
;
});
```
### **2. ์กฐ๊ฑด๋ถ ๋ ๋๋ง**
```typescript
// โ
๋ถํ์ํ ์ปดํฌ๋ํธ ๋ ๋๋ง ๋ฐฉ์ง
{isVisible && }
// โ
๋ก๋ฉ ์ํ
{isLoading ? : }
```
### **3. ์ด๋ฏธ์ง ์ต์ ํ**
```typescript
// โ
ImageWithFallback ์ปดํฌ๋ํธ ์ฌ์ฉ
import { ImageWithFallback } from "./components/figma/ImageWithFallback";
```
---
## โฟ ์ ๊ทผ์ฑ (Accessibility)
### **1. ์๋งจํฑ HTML**
```typescript
// โ
์๋ฏธ์๋ HTML ํ๊ทธ ์ฌ์ฉ
์ ๋ชฉ
๋ด์ฉ
```
### **2. ARIA ์์ฑ**
```typescript
// โ
์คํฌ๋ฆฐ ๋ฆฌ๋๋ฅผ ์ํ ๋ ์ด๋ธ
// โ
์ํ ํ์
์ ์ฅ๋์์ต๋๋ค.
// โ
์จ๊น ์ฝํ
์ธ
๋ํ์์ ์ ๋ชฉ
```
### **3. ํค๋ณด๋ ๋ค๋น๊ฒ์ด์
**
```typescript
// โ
Tab ํค๋ก ์ด๋ ๊ฐ๋ฅ
๋งํฌ
// โ
Enter/Space๋ก ํ์ฑํ
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === "Enter" || e.key === " ") {
handleClick();
}
};
```
---
## ๐ ๋ณด์
### **1. XSS ๋ฐฉ์ง**
```typescript
// โ
React๋ ๊ธฐ๋ณธ์ ์ผ๋ก XSS ๋ฐฉ์ง
{userInput}
// ์๋ ์ด์ค์ผ์ดํ
// โ dangerouslySetInnerHTML ์ฌ์ฉ ๊ธ์ง (ํน๋ณํ ๊ฒฝ์ฐ ์ ์ธ)
```
### **2. ๋ฏผ๊ฐ ์ ๋ณด ์ฒ๋ฆฌ**
```typescript
// โ ํด๋ผ์ด์ธํธ ์ฝ๋์ ๋ฏผ๊ฐ ์ ๋ณด ํ๋์ฝ๋ฉ ๊ธ์ง
const API_KEY = "secret-key-123"; // ์ ๋ ๊ธ์ง!
// โ
ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ ๋๋ ์๋ฒ์ฌ์ด๋ ์ฒ๋ฆฌ
const API_URL = process.env.VITE_API_URL;
```
### **3. ์
๋ ฅ ๊ฒ์ฆ**
```typescript
// โ
์ฌ์ฉ์ ์
๋ ฅ ๊ฒ์ฆ
const validateEmail = (email: string): boolean => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
};
const handleSubmit = (email: string) => {
if (!validateEmail(email)) {
alert("์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ์ ์
๋ ฅํ์ธ์.");
return;
}
// ์ฒ๋ฆฌ
};
```
---
## ๐ ๋ฌธ์ํ
### **1. ์ปดํฌ๋ํธ ๋ฌธ์ํ**
```typescript
/**
* ์ฌ์ฉ์ ํ๋กํ ์นด๋ ์ปดํฌ๋ํธ
*
* @param {string} name - ์ฌ์ฉ์ ์ด๋ฆ
* @param {string} email - ์ฌ์ฉ์ ์ด๋ฉ์ผ
* @param {() => void} onEdit - ํธ์ง ๋ฒํผ ํด๋ฆญ ํธ๋ค๋ฌ
*
* @example
* console.log("edit")}
* />
*/
export function UserProfileCard({ name, email, onEdit }: UserProfileCardProps) {
// ...
}
```
### **2. ๋ณต์กํ ๋ก์ง ์ฃผ์**
```typescript
// โ
๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ์ฃผ์ ์ถ๊ฐ
function calculateDiscount(price: number, quantity: number): number {
// 10๊ฐ ์ด์ ๊ตฌ๋งค ์ 10% ํ ์ธ
// 50๊ฐ ์ด์ ๊ตฌ๋งค ์ 20% ํ ์ธ
// 100๊ฐ ์ด์ ๊ตฌ๋งค ์ 30% ํ ์ธ
if (quantity >= 100) {
return price * 0.7;
} else if (quantity >= 50) {
return price * 0.8;
} else if (quantity >= 10) {
return price * 0.9;
}
return price;
}
```
### **3. TODO ์ฃผ์**
```typescript
// TODO: ์ฑ๋ฅ ์ต์ ํ ํ์
// FIXME: ํน์ ์กฐ๊ฑด์์ ๋ฒ๊ทธ ๋ฐ์
// HACK: ์์ ํด๊ฒฐ์ฑ
, ์ถํ ๊ฐ์ ํ์
// NOTE: ์ค์ํ ์ ๋ณด ๋๋ ์ฃผ์์ฌํญ
```
---
## โจ ๋ฒ ์คํธ ํ๋ํฐ์ค
### **1. DRY (Don't Repeat Yourself)**
```typescript
// โ ์ค๋ณต ์ฝ๋
function formatUserName1(name: string) {
return name.trim().toUpperCase();
}
function formatProductName(name: string) {
return name.trim().toUpperCase();
}
// โ
์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ํจ์
function formatName(name: string): string {
return name.trim().toUpperCase();
}
const userName = formatName(user.name);
const productName = formatName(product.name);
```
### **2. ๋จ์ผ ์ฑ
์ ์์น**
```typescript
// โ ํ๋์ ์ปดํฌ๋ํธ๊ฐ ๋๋ฌด ๋ง์ ์ญํ
function ComplexComponent() {
// ๋ฐ์ดํฐ ๋ก๋ฉ
// ํผ ์ฒ๋ฆฌ
// ์ฐจํธ ๋ ๋๋ง
// ํ
์ด๋ธ ๋ ๋๋ง
// ... 1000+ ์ค
}
// โ
์ฑ
์ ๋ถ๋ฆฌ
function DataContainer() {
return (
<>
>
);
}
```
### **3. ๋ช
ํํ ๋ณ์๋ช
**
```typescript
// โ ๋ถ๋ช
ํํ ์ด๋ฆ
const d = new Date();
const arr = [...items];
const temp = x * y;
// โ
๋ช
ํํ ์ด๋ฆ
const currentDate = new Date();
const sortedItems = [...items];
const totalPrice = quantity * unitPrice;
```
### **4. Early Return ํจํด**
```typescript
// โ ์ค์ฒฉ๋ ์กฐ๊ฑด๋ฌธ
function processUser(user: User) {
if (user) {
if (user.isActive) {
if (user.hasPermission) {
// ์ฒ๋ฆฌ
}
}
}
}
// โ
Early Return
function processUser(user: User) {
if (!user) return;
if (!user.isActive) return;
if (!user.hasPermission) return;
// ์ฒ๋ฆฌ
}
```
### **5. ์๋ฌ ์ฒ๋ฆฌ**
```typescript
// โ
Try-Catch๋ก ์๋ฌ ์ฒ๋ฆฌ
async function fetchData() {
try {
const response = await fetch("/api/data");
const data = await response.json();
return data;
} catch (error) {
console.error("๋ฐ์ดํฐ ๋ก๋ ์คํจ:", error);
return null;
}
}
// โ
์๋ฌ ์ํ ํ์
const [error, setError] = useState(null);
{error && (
์ค๋ฅ
{error}
)}
```
---
## ๐จ ์ผ๋ฐ์ ์ธ ์ค์ & ํด๊ฒฐ์ฑ
### **1. Key Props ๋๋ฝ**
```typescript
// โ key ์์ด ๋ฆฌ์คํธ ๋ ๋๋ง
{items.map(item => {item.name}
)}
// โ
๊ณ ์ ํ key ์ฌ์ฉ
{items.map(item => (
{item.name}
))}
```
### **2. State ์ง์ ์์ **
```typescript
// โ State ์ง์ ์์
const [items, setItems] = useState([]);
items.push(newItem); // ๊ธ์ง!
// โ
์๋ก์ด ๋ฐฐ์ด ์์ฑ
setItems([...items, newItem]);
setItems(prev => [...prev, newItem]);
```
### **3. useEffect ์์กด์ฑ ๋ฐฐ์ด ๋๋ฝ**
```typescript
// โ ์์กด์ฑ ๋ฐฐ์ด ๋๋ฝ
useEffect(() => {
fetchData(userId);
}); // ๋ฌดํ ๋ฃจํ!
// โ
์์กด์ฑ ๋ช
์
useEffect(() => {
fetchData(userId);
}, [userId]);
```
### **4. ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง**
```typescript
// โ ๋งค๋ฒ ์๋ก์ด ๊ฐ์ฒด ์์ฑ
// โ
์์๋ก ๋ถ๋ฆฌ
const componentStyle = { margin: 10 };
```
---
## ๐ ์ฒดํฌ๋ฆฌ์คํธ
### **์ฝ๋ ๋ฆฌ๋ทฐ ์ ์ฒดํฌ๋ฆฌ์คํธ**
```
์ฝ๋ ํ์ง:
[ ] TypeScript ํ์
์ค๋ฅ ์์
[ ] ESLint ๊ฒฝ๊ณ ์์
[ ] ๋ถํ์ํ console.log ์ ๊ฑฐ
[ ] ์ฃผ์ ์์ฑ (๋ณต์กํ ๋ก์ง)
[ ] ๋ณ์๋ช
์ด ๋ช
ํํจ
๊ธฐ๋ฅ:
[ ] ์๊ตฌ์ฌํญ ๋ชจ๋ ๊ตฌํ
[ ] ์๋ฌ ์ฒ๋ฆฌ ๊ตฌํ
[ ] ๋ก๋ฉ ์ํ ๊ตฌํ
[ ] ์ฃ์ง ์ผ์ด์ค ์ฒ๋ฆฌ
UI/UX:
[ ] ๋ฐ์ํ ๋์์ธ ๋์
[ ] ๋ชจ๋ฐ์ผ/๋ฐ์คํฌํฑ ํ
์คํธ
[ ] ์ ๊ทผ์ฑ ์ค์
[ ] ํค๋ณด๋ ๋ค๋น๊ฒ์ด์
๊ฐ๋ฅ
์ฑ๋ฅ:
[ ] ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ์์
[ ] ํฐ ๋ฆฌ์คํธ ์ต์ ํ
[ ] ์ด๋ฏธ์ง ์ต์ ํ
[ ] ๋ฒ๋ค ํฌ๊ธฐ ํ์ธ
๋ฌธ์ํ:
[ ] README ์
๋ฐ์ดํธ (ํ์์)
[ ] ๋ณ๊ฒฝ ์ฌํญ ๊ธฐ๋ก
[ ] API ๋ฌธ์ ์
๋ฐ์ดํธ (ํ์์)
```
### **๋ฐฐํฌ ์ ์ฒดํฌ๋ฆฌ์คํธ**
```
[ ] ๋ชจ๋ ํ
์คํธ ํต๊ณผ
[ ] ๋ธ๋ผ์ฐ์ ํธํ์ฑ ํ์ธ
[ ] ์ฑ๋ฅ ์ธก์ ์๋ฃ
[ ] ๋ณด์ ์ทจ์ฝ์ ์ ๊ฒ
[ ] ๋ฐฑ์
์๋ฃ
[ ] ๋กค๋ฐฑ ๊ณํ ์๋ฆฝ
```
---
## ๐ ํ์ต ๋ฆฌ์์ค
### **ํ์ ๋ฌธ์**
1. `TECH_STACK.md` - ๊ธฐ์ ์คํ
2. `DEVELOPMENT_ENVIRONMENT.md` - ๊ฐ๋ฐ ํ๊ฒฝ
3. `guidelines/Guidelines.md` - ์ถ๊ฐ ๊ฐ์ด๋
### **๊ณต์ ๋ฌธ์**
- [React ๊ณต์ ๋ฌธ์](https://react.dev/)
- [TypeScript ํธ๋๋ถ](https://www.typescriptlang.org/docs/)
- [Tailwind CSS ๋ฌธ์](https://tailwindcss.com/docs)
- [shadcn/ui ๋ฌธ์](https://ui.shadcn.com/)
### **์ถ์ฒ ํ์ต ๊ฒฝ๋ก**
1. **1์ฃผ์ฐจ**: React & TypeScript ๊ธฐ์ด
2. **2์ฃผ์ฐจ**: Tailwind CSS & shadcn/ui
3. **3์ฃผ์ฐจ**: ํ๋ก์ ํธ ๊ตฌ์กฐ ํ์
4. **4์ฃผ์ฐจ**: ์ค์ ์ปดํฌ๋ํธ ๊ฐ๋ฐ
---
## ๐ฏ ํต์ฌ ์์น ์์ฝ
### **์ฝ๋ ์์ฑ ์์น**
1. **ํ์
์์ ์ฑ**: ๋ชจ๋ ์ฝ๋์ TypeScript ํ์
์ ์ฉ
2. **์ฌ์ฌ์ฉ์ฑ**: DRY ์์น ์ค์, ๊ณตํต ์ปดํฌ๋ํธ ํ์ฉ
3. **๊ฐ๋
์ฑ**: ๋ช
ํํ ๋ณ์๋ช
, ์ ์ ํ ์ฃผ์
4. **์ฑ๋ฅ**: ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง
5. **์ ๊ทผ์ฑ**: ๋ชจ๋ ์ฌ์ฉ์๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก
### **UI/UX ์์น**
1. **๋ฐ์ํ**: ๋ชจ๋ ํ๋ฉด ํฌ๊ธฐ ๋์
2. **์ผ๊ด์ฑ**: ๋์์ธ ์์คํ
์ค์
3. **ํผ๋๋ฐฑ**: ๋ก๋ฉ, ์๋ฌ, ์ฑ๊ณต ์ํ ํ์
4. **์ ๊ทผ์ฑ**: ํค๋ณด๋, ์คํฌ๋ฆฐ ๋ฆฌ๋ ์ง์
### **ํ์
์์น**
1. **๋ฌธ์ํ**: ์ฝ๋์ ๋ณ๊ฒฝ์ฌํญ ๋ฌธ์ํ
2. **์ปค๋ฎค๋์ผ์ด์
**: ๋ถํ์คํ ๋ถ๋ถ ์ง๋ฌธ
3. **์ฝ๋ ๋ฆฌ๋ทฐ**: ๊ฑด์ค์ ์ธ ํผ๋๋ฐฑ
4. **์ง์ ๊ณต์ **: ํ์ต ๋ด์ฉ ํ๊ณผ ๊ณต์
---
## ๐ ๋์ ๋ฐ๊ธฐ
### **์ง๋ฌธํ๊ธฐ ์ ์ฒดํฌ**
1. ๊ณต์ ๋ฌธ์ ํ์ธ
2. ๊ธฐ์กด ์ฝ๋ ์ฐธ๊ณ
3. ์๋ฌ ๋ฉ์์ง ๊ฒ์
### **์ง๋ฌธ ํ์**
```
์ ๋ชฉ: [์ปดํฌ๋ํธ๋ช
] ๊ฐ๋จํ ๋ฌธ์ ์ค๋ช
ํ๊ฒฝ:
- ํ์ผ: components/MyComponent.tsx
- ๋ธ๋ผ์ฐ์ : Chrome 120
๋ฌธ์ :
[์์ธํ ๋ฌธ์ ์ค๋ช
]
์๋ํ ํด๊ฒฐ์ฑ
:
1. ...
2. ...
์๋ฌ ๋ฉ์์ง:
[์๋ฌ ๋ฉ์์ง ๋ณต์ฌ]
```
---
## ๐ ๊ฒฐ๋ก
์ด ๊ฐ์ด๋๋ผ์ธ์ **SAM MES ์๋ฃจ์
** ๊ฐ๋ฐ์ ๊ธฐ์ค์ด ๋ฉ๋๋ค.
**ํต์ฌ ํฌ์ธํธ**:
โ
TypeScript๋ก ํ์
์์ ์ฑ ํ๋ณด
โ
Tailwind CSS๋ก ์ผ๊ด๋ ์คํ์ผ๋ง
โ
๋ฐ์ํ ๋์์ธ์ผ๋ก ๋ชจ๋ ๊ธฐ๊ธฐ ์ง์
โ
shadcn/ui๋ก ๋น ๋ฅธ ๊ฐ๋ฐ
โ
๋ฒ ์คํธ ํ๋ํฐ์ค ์ค์
์ด ๊ฐ์ด๋๋ฅผ ๋ฐ๋ฅด๋ฉด ๋์ ํ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์ด ์์คํ
์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
---
**๋ฌธ์ ์์ฑ์ผ**: 2025๋
10์ 17์ผ
**๋ฒ์ **: 1.0.0
**์์ฑ์**: SAM MES ๊ฐ๋ฐํ
**Happy Coding! ๐**