# 설비관리 React 프론트엔드 구현 요청

> **작성일**: 2026-03-12
> **요청자**: R&D실
> **상태**: API 백엔드 완료, React 프론트엔드 구현 요청

---

## 1. 개요

설비관리(Equipment Management) 기능의 React 프론트엔드를 구현해 주세요.
API 백엔드(26개 엔드포인트)는 개발 완료되어 개발서버에 배포된 상태입니다.

- **API 문서**: Swagger 미작성 상태이므로 아래 엔드포인트 스펙을 참고
- **MNG 참고**: `mng/resources/views/equipment/` 에 Blade/HTMX로 구현된 R&D 버전이 있으므로 화면 구성 참고 가능
- **API Base URL**: `https://api.dev.codebridge-x.com/api/v1`

---

## 2. 구현 화면 목록

| # | 화면 | 경로 (제안) | 우선순위 | 비고 |
|---|------|------------|:--------:|------|
| 1 | 설비 목록 | `/equipment` | 1순위 | DataTable + 필터 |
| 2 | 설비 등록 | `/equipment/create` | 1순위 | 폼 |
| 3 | 설비 상세 | `/equipment/:id` | 1순위 | 탭 3개 (기본정보/점검항목/수리이력) |
| 4 | 설비 수정 | `/equipment/:id/edit` | 1순위 | 등록과 동일 폼 + 기존 사진 관리 |
| 5 | 점검 그리드 | `/equipment/inspections` | 2순위 | 동적 그리드, 셀 토글 (가장 복잡) |
| 6 | 수리이력 목록 | `/equipment/repairs` | 2순위 | DataTable + 필터 |
| 7 | 수리이력 등록 | `/equipment/repairs/create` | 2순위 | 폼 |
| 8 | 설비 대시보드 | `/equipment/dashboard` | 3순위 | 통계 카드 + 차트 |

---

## 3. API 엔드포인트 상세

### 3.1 설비 CRUD (9개)

#### `GET /v1/equipment` — 목록 조회 (페이지네이션)

**Query Parameters:**

| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|----------|------|:----:|--------|------|
| `search` | string | — | — | 설비코드, 설비명 LIKE 검색 |
| `status` | string | — | — | `active` / `idle` / `disposed` |
| `production_line` | string | — | — | 생산라인 필터 |
| `equipment_type` | string | — | — | 설비유형 필터 |
| `sort_by` | string | — | `sort_order` | 정렬 컬럼 |
| `sort_direction` | string | — | `asc` | `asc` / `desc` |
| `per_page` | integer | — | `20` | 페이지당 개수 |
| `page` | integer | — | `1` | 페이지 번호 |

**응답:**

```json
{
  "success": true,
  "message": "...",
  "data": {
    "current_page": 1,
    "data": [
      {
        "id": 1,
        "equipment_code": "KD-M-001",
        "name": "포밍기#1",
        "equipment_type": "포밍기",
        "specification": "1200x800mm",
        "manufacturer": "제조사",
        "model_name": "모델A",
        "serial_no": "SN-001",
        "location": "1공장-1F",
        "production_line": "스라트",
        "purchase_date": "2020-01-15",
        "install_date": "2020-02-01",
        "purchase_price": "15000000.00",
        "useful_life": 10,
        "status": "active",
        "disposed_date": null,
        "manager_id": 1,
        "sub_manager_id": 2,
        "photo_path": null,
        "memo": "비고",
        "options": {},
        "is_active": 1,
        "sort_order": 1,
        "manager": { "id": 1, "name": "홍길동" },
        "sub_manager": { "id": 2, "name": "김철수" }
      }
    ],
    "total": 12,
    "per_page": 20,
    "last_page": 1
  }
}
```

---

#### `GET /v1/equipment/options` — 드롭다운 데이터

**응답:**

```json
{
  "success": true,
  "data": {
    "equipment_types": ["포밍기", "미싱기", "샤링기", "V컷팅기", "절곡기", "프레스", "드릴", "기타"],
    "production_lines": ["스라트", "스크린", "절곡", "기타"],
    "statuses": ["active", "idle", "disposed"],
    "equipment_list": [
      { "id": 1, "equipment_code": "KD-M-001", "name": "포밍기#1" }
    ]
  }
}
```

> 설비 등록/수정 폼, 필터 드롭다운에 사용

---

#### `GET /v1/equipment/stats` — 대시보드 통계

**응답:**

```json
{
  "success": true,
  "data": {
    "total": 12,
    "active": 10,
    "idle": 2,
    "disposed": 0
  }
}
```

---

#### `POST /v1/equipment` — 설비 등록

**Request Body (JSON):**

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `equipment_code` | string | ✅ | 설비코드 (tenant 내 unique) |
| `name` | string | ✅ | 설비명 |
| `equipment_type` | string | — | 설비유형 |
| `specification` | string | — | 규격 |
| `manufacturer` | string | — | 제조사 |
| `model_name` | string | — | 모델명 |
| `serial_no` | string | — | 제조번호 |
| `location` | string | — | 위치 |
| `production_line` | string | — | 생산라인 |
| `purchase_date` | date | — | 구입일 (YYYY-MM-DD) |
| `install_date` | date | — | 설치일 (YYYY-MM-DD) |
| `purchase_price` | number | — | 구입가격 |
| `useful_life` | integer | — | 내용연수 |
| `status` | string | — | `active`(기본) / `idle` / `disposed` |
| `disposed_date` | date | — | 폐기일 |
| `manager_id` | integer | — | 담당자 ID (users FK) |
| `sub_manager_id` | integer | — | 부담당자 ID (users FK) |
| `memo` | string | — | 비고 |
| `options` | object | — | 확장 속성 JSON |
| `sort_order` | integer | — | 정렬순서 |

**응답:** 생성된 설비 객체 (관계 포함)

---

#### `GET /v1/equipment/{id}` — 설비 상세

**응답에 포함되는 관계 데이터:**
- `manager` — 담당자 (User)
- `subManager` — 부담당자 (User)
- `inspectionTemplates` — 점검항목 배열
- `repairs` — 수리이력 배열
- `processes` — 연결 공정 배열
- `photos` — 사진 파일 배열

---

#### `PUT /v1/equipment/{id}` — 설비 수정

Body는 등록과 동일 (변경할 필드만 전송 가능)

---

#### `DELETE /v1/equipment/{id}` — 설비 삭제 (Soft Delete)

---

#### `POST /v1/equipment/{id}/restore` — 삭제된 설비 복원

---

#### `PATCH /v1/equipment/{id}/toggle` — 활성/비활성 토글

`is_active` 값이 반전됨

---

### 3.2 점검 템플릿 (5개)

#### `GET /v1/equipment/{id}/templates` — 활성 점검 주기 조회

**응답:**

```json
{
  "data": ["daily", "weekly", "monthly"]
}
```

> 해당 설비에 점검항목이 등록된 주기 목록 반환

---

#### `POST /v1/equipment/{id}/templates` — 점검항목 등록

**Request Body:**

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `inspection_cycle` | string | ✅ | 주기: `daily`/`weekly`/`monthly`/`bimonthly`/`quarterly`/`semiannual` |
| `item_no` | string | ✅ | 항목번호 |
| `check_point` | string | ✅ | 점검개소 (예: 겉모양) |
| `check_item` | string | ✅ | 점검항목 (예: 청결상태) |
| `check_timing` | string | — | 시기: `operating` / `stopped` |
| `check_frequency` | string | — | 주기 (예: 1회/일) |
| `check_method` | string | — | 점검방법 |
| `sort_order` | integer | — | 정렬순서 |

---

#### `PUT /v1/equipment/templates/{templateId}` — 점검항목 수정

---

#### `DELETE /v1/equipment/templates/{templateId}` — 점검항목 삭제

---

#### `POST /v1/equipment/{id}/templates/copy` — 점검항목 다른 주기에 복사

**Request Body:**

```json
{
  "source_cycle": "daily",
  "target_cycles": ["weekly", "monthly"]
}
```

**응답:**

```json
{
  "data": {
    "copied": 5,
    "skipped": 2,
    "source_count": 7,
    "target_cycles": ["weekly", "monthly"]
  }
}
```

---

### 3.3 점검 (5개) — 가장 핵심 기능

#### `GET /v1/equipment/inspections` — 점검 그리드 데이터

**Query Parameters:**

| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|----------|------|:----:|--------|------|
| `cycle` | string | — | `daily` | 점검주기 |
| `period` | string | — | 현재월 | daily: `YYYY-MM`, 그 외: `YYYY` |
| `production_line` | string | — | — | 생산라인 필터 |
| `equipment_id` | integer | — | — | 특정 설비만 |

**응답 구조 (핵심):**

```json
{
  "data": [
    {
      "equipment": {
        "id": 1,
        "equipment_code": "KD-M-001",
        "name": "포밍기#1"
      },
      "templates": [
        {
          "id": 10,
          "item_no": "1",
          "check_point": "겉모양",
          "check_item": "청결상태",
          "check_timing": "operating",
          "check_method": "육안 확인"
        }
      ],
      "inspection": {
        "id": 5,
        "overall_judgment": "OK",
        "inspector_id": 1,
        "repair_note": "",
        "issue_note": ""
      },
      "details": {
        "10_2026-03-01": { "result": "good" },
        "10_2026-03-02": { "result": "bad" },
        "10_2026-03-03": { "result": null }
      },
      "labels": {
        "1": "1", "2": "2", "3": "3", ..., "31": "31"
      },
      "can_inspect": true
    }
  ]
}
```

> **details 키 형식**: `{template_item_id}_{check_date}`
> **labels**: 그리드 열 헤더 (주기별로 다름)
> **can_inspect**: 현재 사용자가 이 설비를 점검할 권한이 있는지

---

#### `PATCH /v1/equipment/inspections/toggle` — 셀 클릭 (결과 순환)

**Request Body:**

```json
{
  "equipment_id": 1,
  "template_item_id": 10,
  "check_date": "2026-03-12",
  "cycle": "daily"
}
```

**응답:**

```json
{
  "data": {
    "result": "good",
    "symbol": "○"
  }
}
```

**결과 순환 규칙:**

```
(빈칸) → good(○) → bad(X) → repaired(△) → (빈칸) → ...
```

---

#### `PATCH /v1/equipment/inspections/set-result` — 결과 직접 설정

toggle과 같은 Body + `result` 필드 추가 (`good` / `bad` / `repaired` / `null`)

---

#### `PATCH /v1/equipment/inspections/notes` — 메모/판정 업데이트

**Request Body:**

```json
{
  "equipment_id": 1,
  "year_month": "2026-03",
  "cycle": "daily",
  "overall_judgment": "OK",
  "inspector_id": 1,
  "repair_note": "수리 사항...",
  "issue_note": "이상 사항..."
}
```

---

#### `DELETE /v1/equipment/inspections/reset` — 점검 데이터 초기화

**Request Body:**

```json
{
  "equipment_id": 1,
  "cycle": "daily",
  "period": "2026-03"
}
```

---

### 3.4 수리이력 (4개)

#### `GET /v1/equipment/repairs` — 목록 (페이지네이션)

**Query Parameters:**

| 파라미터 | 타입 | 설명 |
|----------|------|------|
| `equipment_id` | integer | 설비 필터 |
| `repair_type` | string | `internal`(사내) / `external`(외주) |
| `date_from` | date | 수리일 시작 |
| `date_to` | date | 수리일 종료 |
| `search` | string | 설비명/수리내용 검색 |
| `per_page` | integer | 페이지당 개수 (기본 20) |

---

#### `POST /v1/equipment/repairs` — 수리이력 등록

**Request Body:**

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `equipment_id` | integer | ✅ | 설비 ID |
| `repair_date` | date | ✅ | 수리일 (YYYY-MM-DD) |
| `repair_type` | string | — | `internal`(사내) / `external`(외주) |
| `repair_hours` | number | — | 수리시간 (0.5 단위) |
| `description` | string | — | 수리내용 |
| `cost` | number | — | 수리비용 (원) |
| `vendor` | string | — | 외주업체 (repair_type=external 시) |
| `repaired_by` | integer | — | 수리자 ID |
| `memo` | string | — | 비고 |

---

#### `PUT /v1/equipment/repairs/{id}` — 수정

#### `DELETE /v1/equipment/repairs/{id}` — 삭제

---

### 3.5 사진 (3개)

#### `GET /v1/equipment/{id}/photos` — 사진 목록

**Response:**

```json
{
  "success": true,
  "message": "데이터를 불러왔습니다.",
  "data": [
    {
      "id": 1,
      "file_name": "IMG_001.jpg",
      "file_path": "1/equipment/2026/03/a1b2c3d4e5f6g7h8.jpg",
      "file_url": "/api/v1/files/1/download",
      "file_size": 524288,
      "mime_type": "image/jpeg",
      "created_at": "2026-03-12 10:30:00"
    }
  ]
}
```

#### `POST /v1/equipment/{id}/photos` — 사진 업로드

> **multipart/form-data** 형식으로 직접 업로드한다. 별도 파일 업로드 API를 거치지 않는다.

**Request (multipart/form-data):**

| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `files[]` | file[] | O | 이미지 파일 배열 (다중 업로드) |

**검증 규칙:**
- 허용 확장자: `jpg, jpeg, png, gif, bmp, webp`
- 파일당 최대 크기: 10MB
- 설비당 최대 사진 수: 10장 (현재 사진 수 + 업로드 수 ≤ 10)

**Response:**

```json
{
  "success": true,
  "message": "사진이 업로드되었습니다.",
  "data": [
    {
      "id": 15,
      "file_name": "IMG_001.jpg",
      "file_path": "1/equipment/2026/03/a1b2c3d4e5f6g7h8.jpg",
      "file_url": "/api/v1/files/15/download",
      "file_size": 524288,
      "mime_type": "image/jpeg",
      "created_at": "2026-03-12 10:30:00"
    }
  ]
}
```

**프론트엔드 구현 참고:**
```typescript
// 다중 파일 업로드 예시
const formData = new FormData();
selectedFiles.forEach(file => formData.append('files[]', file));
await api.post(`/v1/equipment/${id}/photos`, formData, {
  headers: { 'Content-Type': 'multipart/form-data' }
});
```

#### `DELETE /v1/equipment/{id}/photos/{fileId}` — 사진 삭제

**Response:**

```json
{
  "success": true,
  "message": "삭제되었습니다.",
  "data": { "file_id": 15, "deleted": true }
}
```

---

## 4. 화면별 구현 가이드

### 4.1 설비 목록

```
┌────────────────────────────────────────────────────┐
│ 설비 등록대장                        [+ 설비 등록]  │
├────────────────────────────────────────────────────┤
│ 필터: [검색] [상태 ▼] [라인 ▼] [유형 ▼] [검색]   │
├────────────────────────────────────────────────────┤
│ 설비번호 │ 설비명 │ 유형 │ 라인 │ 상태 │ 담당자 │…│
│ KD-M-001│ 포밍기 │포밍기│스라트│🟢가동│ 홍길동│…│
│ KD-M-002│ 미싱기 │미싱기│스크린│🟡유휴│ 김철수│…│
├────────────────────────────────────────────────────┤
│                    < 1 2 3 >                       │
└────────────────────────────────────────────────────┘
```

**테이블 컬럼:**

| 컬럼 | 필드 | 정렬 |
|------|------|------|
| 설비번호 | `equipment_code` | center |
| 설비명 | `name` | left |
| 유형 | `equipment_type` | center |
| 위치 | `location` | center |
| 생산라인 | `production_line` | center |
| 상태 | `status` | center, Badge |
| 담당자 정 | `manager.name` | center |
| 담당자 부 | `sub_manager.name` | center |
| 구입일 | `purchase_date` | center |
| 액션 | — | 상세보기/수정/삭제 |

**상태 Badge 색상:**

| 상태 | 라벨 | 색상 |
|------|------|------|
| `active` | 가동 | 초록 |
| `idle` | 유휴 | 노랑 |
| `disposed` | 폐기 | 회색 |

---

### 4.2 설비 등록/수정 폼

**4개 섹션:**

1. **기본정보**: 설비코드*, 설비명*, 설비유형(select), 규격
2. **제조사 정보**: 제조사, 모델명, 제조번호
3. **설치 정보**: 위치, 생산라인(select), 구입일, 설치일, 구입가격, 내용연수
4. **관리자/비고**: 담당자(select), 부담당자(select), 상태(select), 비고(textarea)

**드롭다운 고정값:**

```
설비유형: 포밍기 / 미싱기 / 샤링기 / V컷팅기 / 절곡기 / 프레스 / 드릴 / 기타
생산라인: 스라트 / 스크린 / 절곡 / 기타
상태:     가동(active) / 유휴(idle) / 폐기(disposed)
```

> 담당자/부담당자 드롭다운은 사용자 API에서 동적 로드

**사진 관리 (등록/수정 화면):**
- **등록 화면**: 사진을 미리 선택하면 대기열에 추가 + 미리보기 표시. 설비 저장 완료 후 자동 업로드. 드래그앤드롭, Ctrl+V 클립보드 붙여넣기 지원.
- **수정 화면**: 기존 사진 조회/삭제 + 새 사진 추가 가능
- 최대 10장 제한 (설비당)

---

### 4.3 설비 상세 (탭 3개)

```
← 뒤로   KD-M-001 포밍기#1   [🟢가동]   [수정]
─────────────────────────────────────────────
[기본정보] [점검항목] [수리이력]
─────────────────────────────────────────────
```

**탭 1: 기본정보**
- 설비 상세 정보 (읽기 전용)
- 사진 갤러리
- 연결된 공정 목록

**탭 2: 점검항목**
- 주기별 탭 (daily/weekly/monthly/bimonthly/quarterly/semiannual)
- 각 주기의 점검항목 테이블 (항목번호, 점검개소, 점검항목, 시기, 주기, 점검방법)
- 항목 추가/수정/삭제 기능
- "다른 주기에 복사" 기능

**탭 3: 수리이력**
- 해당 설비의 수리이력 테이블

---

### 4.4 점검 그리드 (가장 복잡한 화면)

```
설비 점검표
[일일] [주간] [월간] [2개월] [분기] [반기]    ← 주기 탭

필터: [년월/연도] [라인 ▼] [설비 ▼] [조회]

┌──────────┬──────────┬───┬───┬───┬───┬──────┐
│ 설비     │ 점검항목 │ 1 │ 2 │ 3 │...│ 판정 │
│(고정열)  │(고정열)  │(토)│(일)│(월)│   │      │
├──────────┼──────────┼───┼───┼───┼───┼──────┤
│ KD-M-001 │ 청결상태 │ — │ — │ ○ │   │ 합격 │
│ 포밍기#1 │ 윤활상태 │ — │ — │ X │   │      │
│(rowspan) │ 볼트이완 │ — │ — │ △ │   │      │
├──────────┼──────────┼───┼───┼───┼───┼──────┤
│ KD-M-002 │ 청결상태 │ — │ — │ ○ │   │ 합격 │
└──────────┴──────────┴───┴───┴───┴───┴──────┘
범례: ○ 양호  X 이상  △ 수리완료  ━ 비근무일
```

**핵심 동작:**

1. **셀 클릭**: `PATCH /inspections/toggle` 호출 → 결과 순환 (빈칸→○→X→△→빈칸)
2. **비근무일**: 주말(토/일) + holidays 테이블 등록 휴일 → 클릭 불가, 음영 처리 (daily만 적용)
3. **고정 열**: 설비/점검항목 열은 가로 스크롤 시 고정 (sticky)
4. **자동 판정**: 비근무일 제외, 모든 점검항목이 good 또는 repaired면 "합격"
5. **rowspan**: 동일 설비의 여러 점검항목은 설비명을 세로 병합

**주기별 그리드 열 구성:**

| 주기 | period 형식 | 열 개수 | 열 라벨 |
|------|------------|---------|---------|
| daily | `YYYY-MM` | 28~31 | 1, 2, 3, ..., 31 (요일 표시) |
| weekly | `YYYY` | 52 | 1주, 2주, ..., 52주 |
| monthly | `YYYY` | 12 | 1월, 2월, ..., 12월 |
| bimonthly | `YYYY` | 6 | 1~2월, 3~4월, ..., 11~12월 |
| quarterly | `YYYY` | 4 | 1분기, 2분기, 3분기, 4분기 |
| semiannual | `YYYY` | 2 | 상반기, 하반기 |

**점검 결과 심볼/색상:**

| result | 심볼 | 색상 |
|--------|------|------|
| `good` | ○ | 초록 (`text-green-600`) |
| `bad` | X | 빨강 (`text-red-600`) |
| `repaired` | △ | 노랑 (`text-yellow-600`) |
| `null` | (빈칸) | — |

---

### 4.5 수리이력

**목록 필터:**
- 검색 (설비명/수리내용)
- 설비 선택 (드롭다운)
- 보전구분 (`internal` 사내 / `external` 외주)
- 기간 (시작일 ~ 종료일)

**테이블 컬럼:**

| 컬럼 | 필드 | 형식 |
|------|------|------|
| 수리일 | `repair_date` | YYYY-MM-DD |
| 설비 | `equipment.name` | 텍스트 |
| 보전구분 | `repair_type` | Badge (사내: 파랑, 외주: 주황) |
| 수리시간 | `repair_hours` | "2h" 또는 "-" |
| 수리내용 | `description` | 텍스트 (40자 제한) |
| 비용 | `cost` | 통화 형식 (1,000,000원) |
| 외주업체 | `vendor` | 텍스트 또는 "-" |
| 액션 | — | 수정/삭제 |

**등록 폼 특이사항:**
- `repair_type`이 `external`(외주)일 때만 `vendor`(외주업체) 필드 표시
- `repair_hours`는 0.5 단위 입력 (step=0.5)

---

### 4.6 대시보드

```
┌────────────┬────────────┬────────────┬────────────┐
│ 총 설비    │ 가동 중     │ 유휴       │ 폐기       │
│   12대     │   10대      │   2대      │   0대      │
└────────────┴────────────┴────────────┴────────────┘

┌──────────────────────────┬──────────────────────────┐
│ 이번달 점검 현황         │ 설비 유형별 현황         │
│ 점검 대상: 12대          │ 포밍기 ████████ 5        │
│ 점검 완료: 8대           │ 절곡기 █████ 3           │
│ 완료율: 66% [████░░]    │ 미싱기 ███ 2             │
│ 이상 발견: 2건           │ 기타   ██ 2              │
└──────────────────────────┴──────────────────────────┘

┌────────────────────────────────────────────────────┐
│ 최근 수리이력                        [전체보기 →]  │
├─────────┬──────────┬──────────┬───────────┬────────┤
│ 수리일  │ 설비     │ 보전구분 │ 수리내용  │ 비용   │
│ 03-08   │ KD-M-01  │ 사내     │ 베어링 교체│ 50,000│
└─────────┴──────────┴──────────┴───────────┴────────┘
```

> `GET /v1/equipment/stats` 로 상단 4개 카드 데이터를 가져옴.
> 점검 현황과 유형별 차트는 stats 응답을 가공하여 표시.

---

## 5. 비즈니스 규칙

### 5.1 점검 권한

- 관리자(admin): 모든 설비 점검 가능
- 일반 사용자: 자신이 `manager_id` 또는 `sub_manager_id`인 설비만 점검 가능
- `can_inspect` 플래그로 API가 알려줌 → false면 셀 클릭 비활성화

### 5.2 비근무일 제한 (daily만)

- **일일 점검에서만** 주말(토/일) 및 등록된 휴일에 점검 기록 불가
- 해당 셀은 음영 처리 + 클릭 비활성화
- 그 외 주기(weekly/monthly 등)에는 제한 없음

### 5.3 점검 결과 순환

셀 클릭 시 결과가 순환:

```
(빈칸) → good(○) → bad(X) → repaired(△) → (빈칸)
```

### 5.4 자동 판정

- 도래한 날짜 중 비근무일 제외
- 모든 항목-날짜 조합이 `good` 또는 `repaired` → **합격(OK)**
- 하나라도 `bad` 또는 미점검 → **불합격(NG)**

### 5.5 설비 사진

- 최대 10장 (설비당)
- Cloudflare R2에 저장 (`Storage::disk('r2')`)
- `POST /v1/equipment/{id}/photos`로 직접 업로드 (`files[]` multipart)
- 허용 확장자: jpg, jpeg, png, gif, bmp, webp
- 파일당 최대 10MB
- 사진 URL: `/api/v1/files/{id}/download`
- 삭제 시 soft delete 처리
- **등록 시 사진 업로드 흐름**: 사진을 미리 선택(대기열) → 설비 저장 → 생성된 ID로 사진 자동 업로드
- **수정 시 사진 관리**: 페이지 로드 시 기존 사진 표시 → 추가/삭제 즉시 반영

---

## 6. 공통 응답 포맷

모든 API 응답은 다음 구조를 따릅니다:

```json
{
  "success": true,
  "message": "성공/에러 메시지",
  "data": { ... }
}
```

에러 시:

```json
{
  "success": false,
  "message": "에러 메시지",
  "errors": {
    "equipment_code": ["설비코드는 필수입니다."]
  }
}
```

---

## 7. 참고 사항

### MNG R&D 화면 확인

개발서버 MNG에서 기존 구현을 확인할 수 있습니다:
- **URL**: `https://admin.codebridge-x.com/equipment`
- 대시보드, 등록대장, 점검 그리드, 수리이력 모두 동작하는 상태

### 개발서버 API 테스트

```bash
# 설비 목록 조회 예시
curl -H "X-Api-Key: <API_KEY>" \
     -H "Authorization: Bearer <TOKEN>" \
     "https://api.dev.codebridge-x.com/api/v1/equipment?per_page=5"
```

### 구현 순서 제안

1. **1단계**: 설비 CRUD (목록 → 등록 → 상세 → 수정) — 기본 DataTable + 폼
2. **2단계**: 수리이력 (목록 → 등록) — 1단계와 유사한 패턴
3. **3단계**: 점검 그리드 — 가장 복잡, 동적 그리드 + 셀 토글
4. **4단계**: 대시보드 — 통계 카드 + 간단한 차트

---

## 관련 문서

- [설비관리 기능 문서](../features/equipment/README.md)
- [서비스 구축 계획](../dev/dev_plans/equipment-service-build-plan.md)

---

**최종 업데이트**: 2026-03-12 (설비 등록 시 사진 업로드 기능 반영)
