🖥️ jsx
// 재사용 가능한 Button 컴포넌트
const Button = ({ variant, children, onClick }) => {
return (
<button
className={`button ${variant}`}
onClick={onClick}
>
{children}
</button>
);
};
props로 컴포넌트로 커스터마이징 예시 코드
🖥️ import React from 'react';
import { AlertCircle, CheckCircle, Info } from 'lucide-react';
// 1. 기본적인 버튼 컴포넌트
const Button = ({
variant = 'primary', // primary, secondary, danger
size = 'medium', // small, medium, large
disabled = false,
children,
onClick
}) => {
// variant에 따른 스타일 매핑
const variantStyles = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
danger: 'bg-red-500 hover:bg-red-600 text-white'
};
// size에 따른 스타일 매핑
const sizeStyles = {
small: 'px-2 py-1 text-sm',
medium: 'px-4 py-2',
large: 'px-6 py-3 text-lg'
};
return (
<button
className={`rounded-md ${variantStyles[variant]} ${sizeStyles[size]}
${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};
// 2. 알림 메시지 컴포넌트
const Alert = ({
type = 'info', // info, success, error
message,
showIcon = true,
className = ''
}) => {
const alertStyles = {
info: {
bg: 'bg-blue-100 border-blue-500',
text: 'text-blue-900',
icon: <Info className="w-5 h-5" />
},
success: {
bg: 'bg-green-100 border-green-500',
text: 'text-green-900',
icon: <CheckCircle className="w-5 h-5" />
},
error: {
bg: 'bg-red-100 border-red-500',
text: 'text-red-900',
icon: <AlertCircle className="w-5 h-5" />
}
};
const style = alertStyles[type];
return (
<div className={`p-4 rounded-lg border-l-4 ${style.bg} ${className}`}>
<div className="flex items-center gap-2">
{showIcon && style.icon}
<span className={style.text}>{message}</span>
</div>
</div>
);
};
// 3. 입력 필드 컴포넌트
const Input = ({
type = 'text',
label,
placeholder,
error,
size = 'medium',
onChange,
required = false
}) => {
const sizeStyles = {
small: 'px-2 py-1 text-sm',
medium: 'px-3 py-2',
large: 'px-4 py-3 text-lg'
};
return (
<div className="flex flex-col gap-1">
{label && (
<label className="text-gray-700">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
</label>
)}
<input
type={type}
className={`border rounded-md ${sizeStyles[size]}
${error ? 'border-red-500' : 'border-gray-300'}
focus:ring-2 focus:ring-blue-500 focus:border-transparent`}
placeholder={placeholder}
onChange={onChange}
/>
{error && (
<span className="text-red-500 text-sm">{error}</span>
)}
</div>
);
};
// 사용 예시를 보여주는 컴포넌트
const Demo = () => {
return (
<div className="space-y-8 p-6">
<div className="space-y-2">
<h2 className="text-lg font-semibold">버튼 예시</h2>
<div className="flex gap-2">
<Button>기본 버튼</Button>
<Button variant="secondary" size="small">작은 버튼</Button>
<Button variant="danger" size="large" disabled>비활성화된 버튼</Button>
</div>
</div>
<div className="space-y-2">
<h2 className="text-lg font-semibold">알림 예시</h2>
<Alert type="info" message="정보 메시지입니다." />
<Alert type="success" message="성공적으로 저장되었습니다." />
<Alert type="error" message="오류가 발생했습니다." showIcon={false} />
</div>
<div className="space-y-2">
<h2 className="text-lg font-semibold">입력 필드 예시</h2>
<Input
label="이메일"
type="email"
placeholder="이메일을 입력하세요"
required
/>
<Input
label="비밀번호"
type="password"
error="비밀번호는 8자 이상이어야 합니다."
size="large"
/>
</div>
</div>
);
};
export default Demo;

🖥️ jsx
// 컴포지션을 통한 Form 컴포넌트 구현
const LoginForm = () => {
return (
<form>
<Input label="이메일" type="email" />
<Input label="비밀번호" type="password" />
<Button variant="primary">로그인</Button>
<Button variant="secondary">취소</Button>
</form>
);
};
관심사의 분리
캡슐화
각 컴포넌트가 독립적 단위로 작동하므로, 특정 기능을 수정할 때 다른 부분에 영향 주지 않는다.
컴포넌트별 책임이 명확하게 분리되어 있어 버그 수정이 용이.
테스트도 컴포넌트 단위로 진행할 수 있어 품질 관리가 쉽다.
➡️ 대규모 애플리케이션 개발에 특히 적합