## 역할
재사용 가능한 React 컴포넌트 라이브러리. 기존 JSP의 UI 요소(Tabulator, Select2, jQuery 플러그인)를 React + Tailwind로 전환한 것.

---

## ui/ — 기본 폼/입력 컴포넌트

### 주요 파일
- **button.tsx** — forwardRef 지원, `variant`(primary/secondary/danger/ghost), `size`(sm/md/lg)
- **input.tsx** — forwardRef 지원, Tailwind 스타일 기본 입력
- **select.tsx** — forwardRef 지원, 배열 기반 options 전달
- **code-select.tsx** — 공통코드 선택박스. `codeId` 필수 prop으로 `/api/common/code-list` 호출, `CODE_ID`/`CODE_NAME` 필드 매핑 (native select)
- **searchable-code-select.tsx** — 검색 가능한 공통코드 선택박스. CodeSelect와 동일 데이터를 쓰지만 typeahead 검색 + `×`로 선택 해제. **긴 코드 목록은 이걸 우선 사용**
- **searchable-select.tsx** — 일반 options 배열용 검색 가능 셀렉트. `options: { value, label }[]` 전달. 동적 로드 데이터(프로젝트/유닛/고객사 등)에 사용
- **folder-cell.tsx** — 첨부파일 개수 표시용 폴더 아이콘 셀. `count > 0`이면 파란 채움 폴더, 아니면 투명. `onClick` 지원. DataGrid formatter 또는 직접 렌더 모두 가능. 3D/2D/PDF/E-BOM 등 모든 count 컬럼 통일용
- **file-upload.tsx** — 파일 업로드. `targetObjId`, `docType`, `docTypeName` 필수. `/api/common/file-upload` POST, formData 키: `"file"`, `"targetObjId"`, `"docType"`, `"docTypeName"`
- **pagination.tsx** — 페이지네이션. 좌: `[input]/총 N 페이지 [go]`, 중앙: 1-10 번호, 우: `[pageSize ▼] N 건`. `maxPage` prop으로 서버사이드 모드도 지원. DataGrid에 기본 내장됨
- **loading.tsx** — 전체 화면 덮는 로딩 모달 (fixed z-50). 그리드 영역만 덮고 싶으면 DataGrid의 `loading` prop 사용 권장

### 숨겨진 스펙
- CodeSelect placeholder 기본값 "선택"
- 모든 폼 컴포넌트는 `disabled:opacity-50` 적용
- FileUpload 성공 시 `onUploadComplete()` 콜백 호출 + 입력값 리셋
- **드롭다운 선택 가이드**:
  - 짧은 고정 목록(≤6 항목: Revision, 상태, LEVEL, 년도 등) → native `<select>` 유지 OK
  - 공통코드(comm_code) 기반 긴 목록 → `SearchableCodeSelect` 사용
  - 동적 API 데이터(프로젝트/유닛/고객사/사용자 등) → `SearchableSelect` + 별도 API 호출로 `{ value, label }[]` 구성
- **Pagination**: DataGrid에 내장되어 있으므로 별도 배치 불필요. 서버사이드 페이징을 쓸 때만 외부에서 `<Pagination maxPage={...}>` 로 직접 사용

---

## layout/ — 페이지 레이아웃

### 주요 파일
- **header.tsx** — 상단 메뉴 탭 + 유저 정보 + 테마 토글 + 승인함 배지(`/api/approval/count` 60초 폴링). 관리자 메뉴: `MENU_NAME_KOR === "관리자"` 필터링. 관리자 패널: `window.open` 고정 사이즈(1630x950).
- **sidebar.tsx** — 좌측 트리 메뉴. `mapMenuUrl()`로 경로 변환, `MENU_ICON_MAP`으로 아이콘 매핑. 축소: `w-[52px]` / 확장: `w-[240px]`.
- **search-form.tsx** — 검색 폼 래퍼 + SearchField(label + 폼 요소). `onSearch(FormData)` 콜백.

### 연결 고리
- Header → useAuthStore, useMenuStore, useThemeStore (3개 store 모두)
- Sidebar → `mapMenuUrl()` from `@/lib/menu-url-map`, `MENU_ICON_MAP` from `@/lib/constants`
- Header의 activeTopMenu 변경 시 Sidebar 자동 갱신(fetchSideMenus)

---

## grid/ — 데이터 테이블

### 주요 파일
- **data-grid.tsx** — TanStack React Table 기반 커스텀 그리드. 정렬, 체크박스 선택, 포매팅(money), 셀 클릭 콜백 지원.

### 숨겨진 스펙
- 데이터 소스: `data` prop(배열) 또는 `dataUrl`(POST 요청)
- 원격 데이터 응답: `json.RESULTLIST` → `json.data` → json 전체(배열) 순서로 탐색
- 총 건수: `json.TOTAL_CNT` 또는 배열 length
- 기본 높이: `calc(100vh - 300px)`
- 정렬: 문자열 기반 localeCompare (숫자 자동 변환 안 함)
- 체크박스 선택: `selectedRows: Set<number>` (인덱스 기반)
- **formatter**:
  - `"money"` → `numberWithCommas()` 적용
  - `(cell, row) => ReactNode` 함수 형태로 커스텀 렌더링 가능 (예: `<FolderCell count={cell} />`)
- 빈 데이터 메시지: "데이터가 없습니다."
- **페이지네이션 내장** (`pagination` prop 기본 true): 하단에 Pagination 컴포넌트 자동 렌더. `pageSize` prop으로 기본값 변경. 데이터 갱신 시 1페이지로 리셋
- **로딩 오버레이** (`loading` prop): 외부에서 true 전달 시 그리드 위에 반투명 오버레이 + spinner 표시. 각 페이지 fetchData 시작/종료 시점에 setLoading(true/false) 호출로 연동

---

## procurement-std/ — 구매품표준관리 템플릿

### 주요 파일
- **StandardCodePage.tsx** — 표준코드 CRUD 템플릿. 필수 props: `title`, `apiUrl`, `codeGroupId`.

### 숨겨진 스펙
- API 요청 필드: snake_case (`code_group`, `large_cd`, `middle_cd`, `maker`, `part_name`, `spec`)
- API 응답: `json.RESULTLIST` 또는 null → 빈 배열 폴백
- CodeSelect codeId 패턴: `{codeGroupId}_LARGE`, `{codeGroupId}_MIDDLE`
- 그리드 열 필드: 대문자 (`LARGE_CD_NAME`, `MIDDLE_CD_NAME`, `UNIT_PRICE` 등)
- 삭제/등록/Excel 기능은 TODO (Swal 안내만 표시)

@MISTAKES.md
