# 컴포넌트 타입별 개발 규칙 > **메인 문서**: [components.md](components.md) > **관련 문서**: [layout-json-components.md](layout-json-components.md) | [sirsoft-admin_basic 컴포넌트](../../templates/_bundled/sirsoft-admin_basic/docs/components.md) --- ## 목차 1. [핵심 원칙](#핵심-원칙) 2. [기본 컴포넌트 (Basic Component)](#1-기본-컴포넌트-basic-component) 3. [집합 컴포넌트 (Composite Component)](#2-집합-컴포넌트-composite-component) 4. [집합 컴포넌트 재사용 가이드라인](#3-집합-컴포넌트-재사용-가이드라인) 5. [레이아웃 컴포넌트 (Layout Component)](#4-레이아웃-컴포넌트-layout-component) --- ## 핵심 원칙 ``` 필수: 기본 컴포넌트 사용 (HTML 태그 직접 사용 금지) 필수: 기본 컴포넌트만 사용 (Div, Button, H2 등) 필수: 집합 컴포넌트 재사용 우선 ``` --- ## 1. 기본 컴포넌트 (Basic Component) **정의**: HTML 기본 태그에 대응하는 최소 래핑 컴포넌트 **타입**: `basic` **예시**: Button, Input, Div, Icon, H1, H2, P, Span, Form, Table, Ul, Li, Nav 등 **특징**: - DOM 요소에 직접 매핑 - 최소한의 래핑만 수행 - props를 HTML 속성으로 전달 - 스타일링은 className으로 처리 ### 패턴 ```tsx // templates/[vendor-template]/src/components/basic/Button.tsx import React from 'react'; export interface ButtonProps extends React.ButtonHTMLAttributes { variant?: 'primary' | 'secondary' | 'danger' | 'success'; size?: 'sm' | 'md' | 'lg'; } /** * 기본 버튼 컴포넌트 */ export const Button: React.FC = ({ children, variant = 'primary', size = 'md', className = '', ...props }) => { return ( ); }; ``` ### Input 컴포넌트 IME 처리 (중요) Input 컴포넌트는 한글 등 IME(Input Method Editor) 조합 입력을 올바르게 처리합니다. **핵심 동작**: - IME 조합 중(`compositionStart` ~ `compositionEnd`)에는 외부 `onChange`를 호출하지 않음 - 조합 완료 후 `compositionEnd` 이벤트에서 최종 값으로 `onChange` 호출 - IME 조합 중에는 `onKeyPress` 이벤트도 발생하지 않음 (Enter 키 등) - 내부 로컬 상태로 화면 표시를 유지하여 조합 중에도 입력이 보임 **사용 시 주의사항**: ``` ✅ keypress 이벤트: IME 조합 완료 후에만 발생 ✅ change 이벤트: IME 조합 완료 후에만 외부에 전달 한글 입력 후 Enter 검색: keypress + key: "Enter" 조합 사용 권장 ``` **검색 입력 필드 예시** (keypress 사용): ```json { "id": "search_input", "type": "basic", "name": "Input", "props": { "type": "text", "placeholder": "검색어 입력..." }, "actions": [ { "type": "change", "handler": "setState", "params": { "target": "global", "searchQuery": "{{$event.target.value}}" } }, { "type": "keypress", "key": "Enter", "handler": "navigate", "params": { "path": "/search?q={{_global.searchQuery}}" } } ] } ``` --- ## 2. 집합 컴포넌트 (Composite Component) ⭐ 핵심 **정의**: 기본 컴포넌트를 조합하여 특정 UI 패턴을 캡슐화한 복합 컴포넌트 **타입**: `composite` **예시**: - **UI 컴포넌트**: Card, DataGrid, Modal, Dropdown, Pagination, SearchBar, StatusBadge, Toast, TagInput - **관리자 컴포넌트**: AdminSidebar, AdminHeader, AdminFooter, PageHeader, TemplateCard ### 핵심 원칙 (필수 준수) ``` 필수: 기본 컴포넌트 사용 (HTML 태그 직접 사용 금지) 필수: 기본 컴포넌트만 사용 (Div, Button, H2 등) 필수: 집합 컴포넌트 재사용 우선 필수: props 기본값은 undefined로 설정 (배열/객체 리터럴 금지) 필수: 모듈 레벨 상수 사용 (const EMPTY: T[] = []) ``` 1. **UI 패턴 캡슐화**: 특정 UI 패턴(카드, 테이블, 모달 등)을 완성된 형태로 제공 2. **간단한 Props 전달**: 레이아웃 JSON에서 최소한의 props만 전달 3. **기본 컴포넌트 조합**: 내부적으로 Div, Button, Table 등 기본 컴포넌트만 사용 4. **HTML 태그 직접 사용 금지**: `
`, ` ))}
); // ✅ 올바른 예: ActionMenu 컴포넌트 재사용 import { ActionMenu } from './ActionMenu'; import { IconName } from '../basic/Icon'; export const TemplateCard = () => (
); ``` **Breadcrumb 재사용 예시**: ```tsx // ❌ 잘못된 예: 커스텀 브레드크럼 구현 export const PageHeader = ({ breadcrumbs }) => (
{breadcrumbs.map((item, index) => ( {item.url ? {item.label} : {item.label}} {index < breadcrumbs.length - 1 && } ))}
); // ✅ 올바른 예: Breadcrumb 컴포넌트 재사용 import { Breadcrumb, BreadcrumbItem } from './Breadcrumb'; export const PageHeader = ({ breadcrumbItems }) => (
); ``` --- ## 4. 레이아웃 컴포넌트 (Layout Component) **정의**: 자식 요소를 배치하는 컨테이너 컴포넌트 **타입**: `layout` **예시**: Container (flex, grid, stack), Grid, Flex, SectionLayout **특징**: - 자식 요소의 배치 및 정렬을 담당 - 레이아웃 관련 props 제공 (layout, direction, gap, justify, align 등) - UI 로직 없이 순수하게 구조화만 담당 **주의**: `Section`은 basic 컴포넌트이며, `SectionLayout`은 layout 컴포넌트입니다. - `Section` (basic): HTML `
` 태그를 래핑하는 최소 컴포넌트 - `SectionLayout` (layout): Section 컴포넌트를 활용하여 제목, 패딩, 배경색 등을 제공하는 레이아웃 컴포넌트 ### 핵심 원칙 (필수 준수) 1. **기본 컴포넌트 재사용**: 내부적으로 Div, Section 등 기본 컴포넌트만 사용 2. **HTML 태그 직접 사용 금지**: `
`, `
` 등 HTML 태그 직접 사용 불가 3. **집합 컴포넌트와 동일한 패턴**: 집합 컴포넌트와 동일한 개발 패턴 적용 ### 개발 가이드라인 ```tsx // ❌ 잘못된 예: HTML 태그 직접 사용 export const Container: React.FC = ({ children }) => (
{children}
); // ✅ 올바른 예: 기본 컴포넌트 재사용 import { Div } from '../basic/Div'; export const Container: React.FC = ({ children }) => (
{children}
); ``` ### 재사용 시 얻는 이점 - 집합 컴포넌트와 동일한 아키텍처 패턴 유지 - 일관된 코드베이스 구조 - 기본 컴포넌트 수정 시 자동 반영 --- ## 관련 문서 - [컴포넌트 개발 규칙 인덱스](components.md) - [컴포넌트 패턴](components-patterns.md) - [컴포넌트 고급 기능](components-advanced.md) - [sirsoft-admin_basic 컴포넌트](../../templates/_bundled/sirsoft-admin_basic/docs/components.md) - [sirsoft-basic 컴포넌트](../../templates/_bundled/sirsoft-basic/docs/components.md)