# react-component
> React 컴포넌트 개발 및 상태 관리. 새로운 UI, 폼, 데이터 시각화가 필요할 때 사용
- Author: joohyeona
- Repository: hyunseung1119/My_ClaudeCode_Skill
- Version: 20260202112523
- Stars: 0
- Forks: 0
- Last Updated: 2026-02-06
- Source: https://github.com/hyunseung1119/My_ClaudeCode_Skill
- Web: https://mule.run/skillshub/@@hyunseung1119/My_ClaudeCode_Skill~react-component:20260202112523
---
---
name: react-component
description: React 컴포넌트 개발 및 상태 관리. 새로운 UI, 폼, 데이터 시각화가 필요할 때 사용
allowed-tools: Read, Write, Edit, Bash
---
# React 컴포넌트 개발 가이드
## 📋 개발 프로세스
1. **컴포넌트 설계**: UI/UX 요구사항 분석
2. **파일 생성**: 명명 규칙에 따라 파일 생성
3. **Props 정의**: 명확한 인터페이스 설계
4. **상태 관리**: useState, useEffect, Custom Hooks
5. **스타일링**: CSS Modules 또는 Styled Components
6. **테스트**: 주요 기능 테스트 작성
## 🎯 컴포넌트 원칙 (2026 Best Practices)
### 필수 원칙
- ✅ **함수형 컴포넌트**: Class 컴포넌트 대신 Function + Hooks
- ✅ **Props 타입 정의**: PropTypes 또는 TypeScript
- ✅ **단일 책임**: 하나의 컴포넌트는 하나의 역할만
- ✅ **컨테이너/프레젠테이션 분리**: 로직과 UI 분리
- ✅ **재사용성**: 공통 컴포넌트는 `components/common/`에
### 선택적 개선
- 🔧 **Custom Hooks**: 로직 재사용
- 🔧 **Memo화**: 불필요한 리렌더링 방지
- 🔧 **Error Boundaries**: 에러 처리
## 📁 권장 파일 구조
```
src/
├── components/ # UI 컴포넌트
│ ├── common/ # 공통 재사용 컴포넌트
│ │ ├── Button.jsx
│ │ ├── Input.jsx
│ │ ├── Card.jsx
│ │ ├── Modal.jsx
│ │ └── Loading.jsx
│ ├── features/ # 기능별 컴포넌트
│ │ ├── auth/
│ │ │ ├── LoginForm.jsx
│ │ │ └── SignupForm.jsx
│ │ ├── dashboard/
│ │ │ ├── Dashboard.jsx
│ │ │ ├── StatsCard.jsx
│ │ │ └── Chart.jsx
│ │ └── profile/
│ │ ├── ProfileView.jsx
│ │ └── ProfileEdit.jsx
│ └── layout/ # 레이아웃 컴포넌트
│ ├── Header.jsx
│ ├── Sidebar.jsx
│ ├── Footer.jsx
│ └── MainLayout.jsx
├── hooks/ # Custom Hooks
│ ├── useAuth.js
│ ├── useAPI.js
│ ├── useForm.js
│ └── useDebounce.js
├── services/ # API 호출
│ ├── api.js # Axios 설정
│ └── authService.js
├── utils/ # 유틸리티 함수
│ ├── validators.js
│ ├── formatters.js
│ └── helpers.js
├── constants/ # 상수
│ ├── config.js
│ └── routes.js
├── contexts/ # React Context
│ ├── AuthContext.jsx
│ └── ThemeContext.jsx
└── styles/ # 전역 스타일
├── globals.css
└── variables.css
```
## 🛠️ 주요 패턴
### 1. 컨테이너/프레젠테이션 패턴
#### ✅ GOOD - 로직과 UI 분리
```jsx
// components/features/dashboard/DashboardContainer.jsx (컨테이너 - 로직)
import { useState, useEffect } from 'react';
import { fetchDashboardData } from '../../../services/dashboardService';
import DashboardView from './DashboardView';
import LoadingSpinner from '../../common/LoadingSpinner';
import ErrorMessage from '../../common/ErrorMessage';
const DashboardContainer = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadData = async () => {
try {
const result = await fetchDashboardData();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
loadData();
}, []);
if (loading) return ;
if (error) return ;
return ;
};
export default DashboardContainer;
// components/features/dashboard/DashboardView.jsx (프레젠테이션 - UI만)
import PropTypes from 'prop-types';
import StatsCard from './StatsCard';
import Chart from './Chart';
const DashboardView = ({ data }) => {
return (
{data.stats.map((stat, idx) => (
))}
);
};
DashboardView.propTypes = {
data: PropTypes.shape({
stats: PropTypes.arrayOf(PropTypes.object).isRequired,
chartData: PropTypes.object.isRequired,
}).isRequired,
};
export default DashboardView;
```
#### ❌ BAD - 로직과 UI 혼재
```jsx
const Dashboard = () => {
const [data, setData] = useState(null);
useEffect(() => { /* data fetch */ }, []);
return (
{/* 복잡한 UI + 로직이 모두 한 곳에 */}
{data && (
<>
{data.map(item => (
{ /* 복잡한 로직 */ }}>
{/* 복잡한 UI */}
))}
>
)}
);
};
```
### 2. Custom Hooks 패턴
```jsx
// hooks/useForm.js
import { useState } from 'react';
export const useForm = (initialValues, validate) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const handleChange = (name, value) => {
setValues(prev => ({ ...prev, [name]: value }));
// 즉시 검증
if (touched[name] && validate) {
const fieldErrors = validate({ ...values, [name]: value });
setErrors(prev => ({ ...prev, [name]: fieldErrors[name] }));
}
};
const handleBlur = (name) => {
setTouched(prev => ({ ...prev, [name]: true }));
if (validate) {
const fieldErrors = validate(values);
setErrors(prev => ({ ...prev, [name]: fieldErrors[name] }));
}
};
const handleSubmit = (onSubmit) => async (e) => {
e.preventDefault();
// 모든 필드 검증
if (validate) {
const validationErrors = validate(values);
setErrors(validationErrors);
if (Object.keys(validationErrors).length > 0) {
return;
}
}
await onSubmit(values);
};
const reset = () => {
setValues(initialValues);
setErrors({});
setTouched({});
};
return {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
reset,
};
};
// 사용 예시
const LoginForm = () => {
const validate = (values) => {
const errors = {};
if (!values.email) {
errors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = 'Email is invalid';
}
if (!values.password) {
errors.password = 'Password is required';
} else if (values.password.length < 6) {
errors.password = 'Password must be at least 6 characters';
}
return errors;
};
const { values, errors, handleChange, handleBlur, handleSubmit } = useForm(
{ email: '', password: '' },
validate
);
const onSubmit = async (formValues) => {
console.log('Submitting:', formValues);
// API call here
};
return (
);
};
```
### 3. Props 구조 분해 패턴
```jsx
// ✅ GOOD - 구조 분해로 명확성 향상
const UserCard = ({ name, email, avatar, onEdit, onDelete }) => {
return (
{name}
{email}
);
};
UserCard.propTypes = {
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
avatar: PropTypes.string,
onEdit: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
};
UserCard.defaultProps = {
avatar: '/default-avatar.png',
};
// ❌ BAD - Props 객체로 전달
const UserCard = (props) => {
return (
{props.name}
{props.email}
{/* props. 반복 */}
);
};
```
### 4. 리스트 렌더링 패턴
```jsx
// components/features/users/UserList.jsx
import PropTypes from 'prop-types';
import UserCard from './UserCard';
const UserList = ({ users, onEdit, onDelete }) => {
if (!users || users.length === 0) {
return No users found
;
}
return (
{users.map((user) => (
onEdit(user.id)}
onDelete={() => onDelete(user.id)}
/>
))}
);
};
UserList.propTypes = {
users: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
avatar: PropTypes.string,
})
).isRequired,
onEdit: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
};
export default UserList;
```
### 5. 조건부 렌더링 패턴
```jsx
// ✅ GOOD - 명확한 조건부 렌더링
const Dashboard = ({ user, loading, error }) => {
// Early return for loading state
if (loading) {
return ;
}
// Early return for error state
if (error) {
return ;
}
// Early return for no user
if (!user) {
return ;
}
// Main render
return (
Welcome, {user.name}
);
};
// ❌ BAD - 중첩된 삼항 연산자
const Dashboard = ({ user, loading, error }) => {
return loading ? (
) : error ? (
) : user ? (
{/* main content */}
) : (
);
};
```
### 6. 에러 바운더리 패턴
```jsx
// components/common/ErrorBoundary.jsx
import React from 'react';
import PropTypes from 'prop-types';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// 여기서 에러 로깅 서비스로 전송 가능
}
render() {
if (this.state.hasError) {
return (
Something went wrong
Error details
{this.state.error?.toString()}
);
}
return this.props.children;
}
}
ErrorBoundary.propTypes = {
children: PropTypes.node.isRequired,
};
export default ErrorBoundary;
// 사용
```
## 🎨 명명 규칙
### 파일명
```
✅ GOOD:
- Button.jsx (컴포넌트)
- UserProfile.jsx
- DashboardContainer.jsx
❌ BAD:
- button.jsx (소문자)
- user_profile.jsx (snake_case)
- dashboard-container.jsx (kebab-case)
```
### 함수명
```javascript
✅ GOOD:
- handleClick() // 이벤트 핸들러
- fetchUserData() // 데이터 가져오기
- isValidEmail() // Boolean 반환
- formatDate() // 포맷팅
❌ BAD:
- click() // 너무 짧음
- getUserDataFromAPI() // 너무 장황
- func1() // 의미 없음
```
### Custom Hooks
```javascript
✅ GOOD:
- useAuth()
- useForm()
- useDebounce()
- useLocalStorage()
❌ BAD:
- authHook() // use로 시작하지 않음
- hook1() // 의미 없음
```
## 🚀 실행 및 개발
### 개발 서버 실행
```bash
# Create React App
npm start
# Vite
npm run dev
# Next.js
npm run dev
```
### 빌드
```bash
# 프로덕션 빌드
npm run build
# 빌드 미리보기
npm run preview
```
### 테스트
```bash
# Jest + React Testing Library
npm test
# 특정 파일 테스트
npm test Button.test.jsx
# 커버리지
npm test -- --coverage
```
## 📚 추가 리소스
- [React 공식 문서](https://react.dev/)
- [React Patterns](https://reactpatterns.com/)
- [React Hook Form](https://react-hook-form.com/)