docs: [equipment] API Phase 1 구현 현황 문서 업데이트

- 상태 변경: API+React 미구현 → API Phase 1 완료
- API 구현 현황 섹션 추가 (모델, 서비스, 컨트롤러, 라우트 26개)
- MNG→API 변환 규칙, DB 연결 분리 설명 추가
- Phase 로드맵 (Phase 1 완료, Phase 2-3 미착수)
This commit is contained in:
김보곤
2026-03-12 11:12:52 +09:00
parent 6b4eae5252
commit 5d2933c176

View File

@@ -1,7 +1,7 @@
# 설비관리 (Equipment Management)
> **작성일**: 2026-02-27
> **상태**: MNG R&D 완료 / API+React 미구현
> **상태**: MNG R&D 완료 / API Phase 1 완료 / React 미구현
> **최종 갱신**: 2026-03-12
---
@@ -10,21 +10,21 @@
### 1.1 목적
생산 설비의 등록, 점검, 수리이력을 체계적으로 관리한다. MNG 관리자 패널에서 Blade/HTMX 기반으로 R&D가 완료되었으며, 본 시스템(API + React)으로의 서비스 구축이 필요하다.
생산 설비의 등록, 점검, 수리이력을 체계적으로 관리한다. MNG 관리자 패널에서 Blade/HTMX 기반으로 R&D가 완료되었으며, API 백엔드 구현이 완료되어 React 프론트엔드 연동을 진행할 수 있다.
### 1.2 핵심 기능
| 기능 | 설명 | MNG 구현 |
|------|------|:--------:|
| 설비 대시보드 | 현황 카드(총/가동/유휴/폐기), 유형별 통계, 최근 수리 | ✅ |
| 설비 대장 (CRUD) | 등록/수정/삭제/복원, SoftDeletes | ✅ |
| 설비 점검 | 6주기(일일~반년), 그리드 셀 토글, 종합판정 | ✅ |
| 점검 템플릿 | 설비별 점검항목 정의, 주기간 복사 | ✅ |
| 수리이력 | 사내/외주, 비용, 시간, 업체 관리 | ✅ |
| 설비 사진 | GCS 업로드, 압축(1MB), 최대 10장 | ✅ |
| 엑셀 Import | 미리보기 + 일괄등록, 이미지 포함 | ✅ |
| 모바일 점검 | QR 스캔 → 모바일 점검 화면 | ✅ |
| 설비-공정 매핑 | N:N 관계, 주 설비 여부 | ✅ |
| 기능 | 설명 | MNG | API |
|------|------|:---:|:---:|
| 설비 대시보드 | 현황 카드(총/가동/유휴/폐기), 유형별 통계 | ✅ | ✅ |
| 설비 대장 (CRUD) | 등록/수정/삭제/복원, SoftDeletes | ✅ | ✅ |
| 설비 점검 | 6주기(일일~반년), 그리드 셀 토글, 종합판정 | ✅ | ✅ |
| 점검 템플릿 | 설비별 점검항목 정의, 주기간 복사 | ✅ | ✅ |
| 수리이력 | 사내/외주, 비용, 시간, 업체 관리 | ✅ | ✅ |
| 설비 사진 | 파일 업로드/삭제 | ✅ | ✅ |
| 엑셀 Import | 미리보기 + 일괄등록, 이미지 포함 | ✅ | — |
| 모바일 점검 | QR 스캔 → 모바일 점검 화면 | ✅ | — |
| 설비-공정 매핑 | N:N 관계, 주 설비 여부 | ✅ | ✅ |
---
@@ -43,6 +43,18 @@
| `equipment_repairs` | 수리이력 | `2026_02_25_100400` |
| `equipment_process` | 설비-공정 매핑 (N:N pivot) | `2026_02_25_100500` |
추가 마이그레이션:
| 마이그레이션 | 설명 |
|-------------|------|
| `2026_02_28_100000` | `sub_manager_id` 컬럼 추가 |
| `2026_02_28_100100` | templates에 `inspection_cycle` 추가 + 유니크 변경 |
| `2026_02_28_100200` | inspections에 `inspection_cycle` 추가 + 유니크 변경 |
| `2026_03_12_100000` | `equipments`, `equipment_repairs``options` JSON 추가 |
| `2026_03_12_100001` | 기본 DB에 테이블 존재 보장 (`hasTable` 체크) |
> **DB 연결 참고**: 기존 MNG 마이그레이션이 `codebridge` DB에 테이블을 생성했으나, API에서는 기본 DB(`sam`/`sam_prod`)에 테이블이 필요하다. `2026_03_12_100001` 마이그레이션이 기본 DB에 테이블이 없으면 최신 스키마로 생성한다.
### 2.2 ERD 개요
```
@@ -73,6 +85,7 @@ equipments (설비 마스터)
| `install_date` | date | 설치일 |
| `purchase_price` | decimal(15,2) | 구입가격 |
| `useful_life` | int | 내용연수 |
| `options` | json | 확장 속성 JSON |
| `sort_order` | int | 정렬순서 |
#### `equipment_inspection_templates`
@@ -119,23 +132,160 @@ equipments (설비 마스터)
| `cost` | decimal(15,2) | 수리비용 |
| `vendor` | varchar(100) | 외주업체 |
| `repaired_by` | FK → users | 수리자 |
| `options` | json | 확장 속성 JSON |
---
## 3. MNG 구현 현황
## 3. API 구현 현황 (Phase 1 완료)
### 3.1 모델 (`mng/app/Models/Equipment/`)
### 3.1 모델 (`api/app/Models/Equipment/`)
| 모델 | 파일 | 특이사항 |
|------|------|---------|
| `Equipment` | `Equipment.php` | BelongsToTenant, SoftDeletes, `connection='codebridge'` |
| `EquipmentInspection` | `EquipmentInspection.php` | BelongsToTenant |
| `EquipmentInspectionDetail` | `EquipmentInspectionDetail.php` | 결과 순환 로직 (`getNextResult`) |
| `EquipmentInspectionTemplate` | `EquipmentInspectionTemplate.php` | BelongsToTenant |
| `EquipmentRepair` | `EquipmentRepair.php` | BelongsToTenant, SoftDeletes |
| `EquipmentProcess` | `EquipmentProcess.php` | pivot 모델 |
| 모델 | Traits | 특이사항 |
|------|--------|---------|
| `Equipment` | Auditable, BelongsToTenant, ModelTrait, SoftDeletes | `options` cast, `getOption()`/`setOption()` |
| `EquipmentInspection` | Auditable, BelongsToTenant, ModelTrait | — |
| `EquipmentInspectionDetail` | ModelTrait | `getNextResult()`, `getResultSymbol()` static |
| `EquipmentInspectionTemplate` | Auditable, BelongsToTenant, ModelTrait | — |
| `EquipmentRepair` | Auditable, BelongsToTenant, ModelTrait, SoftDeletes | `options` cast, `getOption()`/`setOption()` |
| `EquipmentProcess` | | pivot 모델 |
### 3.2 서비스 (`mng/app/Services/`)
### 3.2 Enum (`api/app/Enums/InspectionCycle.php`)
6개 점검주기를 관리하는 헬퍼 클래스:
| 주기 | 코드 | period 형식 | 그리드 열 |
|------|------|------------|----------|
| 일일 | `daily` | `2026-03` (년-월) | 1~31일 |
| 주간 | `weekly` | `2026` (년) | 1~52주 |
| 월간 | `monthly` | `2026` (년) | 1~12월 |
| 2개월 | `bimonthly` | `2026` (년) | 1~6 |
| 분기 | `quarterly` | `2026` (년) | 1~4분기 |
| 반년 | `semiannual` | `2026` (년) | 상/하반기 |
주요 메서드: `columnLabels()`, `resolveCheckDate()`, `resolvePeriod()`, `isNonWorkingDay()`, `getHolidayDates(int $tenantId)`
> **MNG→API 변환**: `getHolidayDates()`가 `session('selected_tenant_id')` 대신 명시적 `$tenantId` 매개변수를 받는다.
### 3.3 서비스 (`api/app/Services/Equipment/`)
모든 서비스는 `extends Service` (base class)를 사용하며, 쓰기 작업은 `DB::transaction()`으로 감싼다.
| 서비스 | 핵심 메서드 |
|--------|-----------|
| `EquipmentService` | `index()`, `show()`, `store()`, `update()`, `destroy()`, `restore()`, `toggleActive()`, `stats()`, `options()` |
| `EquipmentInspectionService` | `getInspections()`, `toggleDetail()`, `setResult()`, `updateNotes()`, `resetInspection()`, `saveTemplate()`, `updateTemplate()`, `deleteTemplate()`, `copyTemplates()`, `getActiveCycles()` |
| `EquipmentRepairService` | `index()`, `store()`, `update()`, `destroy()` |
| `EquipmentPhotoService` | `index()`, `store()`, `destroy()` |
### 3.4 컨트롤러 (`api/app/Http/Controllers/V1/Equipment/`)
| 컨트롤러 | DI | 메서드 |
|---------|-----|-------|
| `EquipmentController` | `EquipmentService` | index, show, store, update, destroy, restore, toggleActive, stats, options |
| `EquipmentInspectionController` | `EquipmentInspectionService` | index, toggleDetail, setResult, updateNotes, resetInspection, templates, storeTemplate, updateTemplate, deleteTemplate, copyTemplates |
| `EquipmentRepairController` | `EquipmentRepairService` | index, store, update, destroy |
| `EquipmentPhotoController` | `EquipmentPhotoService` | index, store, destroy |
### 3.5 FormRequest (`api/app/Http/Requests/V1/Equipment/`)
| 클래스 | 용도 |
|--------|------|
| `StoreEquipmentRequest` | 설비 등록 (코드 unique per tenant) |
| `UpdateEquipmentRequest` | 설비 수정 (`sometimes` 규칙) |
| `StoreEquipmentRepairRequest` | 수리이력 등록/수정 |
| `StoreInspectionTemplateRequest` | 점검항목 등록 (cycle enum 검증) |
| `ToggleInspectionDetailRequest` | 점검 셀 토글 |
| `UpdateInspectionNotesRequest` | 점검 메모/판정 수정 |
### 3.6 API 라우트 (`api/routes/api/v1/equipment.php`)
26개 엔드포인트, prefix: `/v1/equipment`
```
설비 CRUD (9):
GET /v1/equipment → index (목록+검색+필터)
GET /v1/equipment/options → options (드롭다운 데이터)
GET /v1/equipment/stats → stats (대시보드 통계)
POST /v1/equipment → store
GET /v1/equipment/{id} → show
PUT /v1/equipment/{id} → update
DELETE /v1/equipment/{id} → destroy (soft delete)
POST /v1/equipment/{id}/restore → restore
PATCH /v1/equipment/{id}/toggle → toggleActive
점검 템플릿 (5):
GET /v1/equipment/{id}/templates → templates (활성 주기 목록)
POST /v1/equipment/{id}/templates → storeTemplate
PUT /v1/equipment/templates/{templateId} → updateTemplate
DELETE /v1/equipment/templates/{templateId} → deleteTemplate
POST /v1/equipment/{id}/templates/copy → copyTemplates
점검 (5):
GET /v1/equipment/inspections → index (그리드 데이터)
PATCH /v1/equipment/inspections/toggle → toggleDetail (셀 토글)
PATCH /v1/equipment/inspections/set-result → setResult (결과 직접 설정)
PATCH /v1/equipment/inspections/notes → updateNotes (메모/판정)
DELETE /v1/equipment/inspections/reset → resetInspection (초기화)
수리이력 (4):
GET /v1/equipment/repairs → index
POST /v1/equipment/repairs → store
PUT /v1/equipment/repairs/{id} → update
DELETE /v1/equipment/repairs/{id} → destroy
사진 (3):
GET /v1/equipment/{id}/photos → index
POST /v1/equipment/{id}/photos → store
DELETE /v1/equipment/{id}/photos/{fileId} → destroy
```
### 3.7 i18n 메시지
**성공 메시지** (`lang/ko/message.php``equipment.*`):
| 키 | 메시지 |
|----|--------|
| `created` | 설비가 등록되었습니다. |
| `updated` | 설비 정보가 수정되었습니다. |
| `deleted` | 설비가 삭제되었습니다. |
| `restored` | 설비가 복원되었습니다. |
| `inspection_saved` | 점검 정보가 저장되었습니다. |
| `inspection_reset` | 점검 데이터가 초기화되었습니다. |
| `template_created` | 점검항목이 추가되었습니다. |
| `template_copied` | 점검항목이 복사되었습니다. |
| `repair_created` | 수리이력이 등록되었습니다. |
| `photo_uploaded` | 사진이 업로드되었습니다. |
**에러 메시지** (`lang/ko/error.php``equipment.*`):
| 키 | 메시지 |
|----|--------|
| `not_found` | 설비 정보를 찾을 수 없습니다. |
| `template_not_found` | 점검항목을 찾을 수 없습니다. |
| `inspection_not_found` | 점검 데이터를 찾을 수 없습니다. |
| `repair_not_found` | 수리이력을 찾을 수 없습니다. |
| `photo_not_found` | 사진을 찾을 수 없습니다. |
| `invalid_cycle` | 유효하지 않은 점검주기입니다. |
| `no_source_templates` | 복사할 점검항목이 없습니다. |
### 3.8 MNG→API 변환 규칙 적용
| MNG 패턴 | API 변환 |
|----------|---------|
| `$connection = 'codebridge'` | 제거 (기본 connection 사용) |
| `session('selected_tenant_id')` | `$this->tenantId()` |
| `auth()->id()` | `$this->apiUserId()` |
| `DB::connection('codebridge')` | `DB::` (기본 connection) |
| MNG 직접 validate | FormRequest 분리 |
| 직접 문자열 에러 메시지 | `__('error.equipment.*')` i18n |
| — | `Auditable` trait 추가 |
| — | `DB::transaction()` 쓰기 래핑 |
---
## 4. MNG 구현 현황
### 4.1 서비스 (`mng/app/Services/`)
| 서비스 | 핵심 메서드 |
|--------|-----------|
@@ -145,99 +295,7 @@ equipments (설비 마스터)
| `EquipmentPhotoService` | `uploadPhotos()`, `uploadPhotoFromPath()`, `deletePhoto()`, `getPhotoUrls()`, `compressImage()` |
| `EquipmentImportService` | `preview()`, `import()` — 엑셀 헤더 매핑, 이미지 추출, 중복 처리 |
### 3.3 Enum (`mng/app/Enums/InspectionCycle.php`)
6개 점검주기를 관리하는 헬퍼 클래스:
| 주기 | 코드 | 라벨 | period 형식 | 그리드 열 |
|------|------|------|------------|----------|
| 일일 | `daily` | 일일 | `2026-03` (년-월) | 1~31일 |
| 주간 | `weekly` | 주간 | `2026` (년) | 1~52주 |
| 월간 | `monthly` | 월간 | `2026` (년) | 1~12월 |
| 2개월 | `bimonthly` | 2개월 | `2026` (년) | 1~6 (2개월 단위) |
| 분기 | `quarterly` | 분기 | `2026` (년) | 1~4분기 |
| 반년 | `semiannual` | 반년 | `2026` (년) | 상/하반기 |
주요 메서드: `columnLabels()`, `resolveCheckDate()`, `resolvePeriod()`, `isNonWorkingDay()`, `getHolidayDates()`
### 3.4 컨트롤러
| 컨트롤러 | 경로 | 역할 |
|---------|------|------|
| `EquipmentController` | `app/Http/Controllers/` | 웹 뷰 렌더링 (dashboard, index, create, show, edit, inspections, repairs, import, guide) |
| `Api\Admin\EquipmentController` | `app/Http/Controllers/Api/Admin/` | CRUD + 사진 + 템플릿 조회 + 엑셀 Import |
| `Api\Admin\EquipmentInspectionController` | 위 동일 | 점검 그리드 조회, 셀 토글, 결과 설정, 노트, 초기화, 템플릿 CRUD, 주기 복사 |
| `Api\Admin\EquipmentRepairController` | 위 동일 | 수리이력 CRUD |
### 3.5 Request 클래스 (`mng/app/Http/Requests/`)
| 클래스 | 용도 |
|--------|------|
| `StoreEquipmentRequest` | 설비 등록 (코드 unique per tenant) |
| `UpdateEquipmentRequest` | 설비 수정 (코드 unique ignore self) |
| `StoreEquipmentRepairRequest` | 수리이력 등록 |
| `StoreEquipmentInspectionRequest` | 점검 기록 |
### 3.6 라우트 구조
**웹 라우트** (`routes/web.php`):
```
/equipment → dashboard
/equipment/registry → index (목록)
/equipment/registry/create → create (등록)
/equipment/registry/{id} → show (상세)
/equipment/registry/{id}/edit → edit (수정)
/equipment/inspections → inspections (점검)
/equipment/repairs → repairs (수리이력)
/equipment/repairs/create → repairCreate
/equipment/import → import (엑셀)
/equipment/guide → guide (가이드)
```
**API 라우트** (`routes/api.php`, prefix: `/admin/equipment`):
```
설비 CRUD:
GET /admin/equipment → index
POST /admin/equipment → store
GET /admin/equipment/{id} → show
PUT /admin/equipment/{id} → update
DELETE /admin/equipment/{id} → destroy
POST /admin/equipment/{id}/restore → restore
점검 템플릿:
GET /admin/equipment/{id}/templates → templates
POST /admin/equipment/{id}/templates → storeTemplate
PUT /admin/equipment/templates/{templateId} → updateTemplate
DELETE /admin/equipment/templates/{templateId} → deleteTemplate
POST /admin/equipment/{id}/templates/copy → copyTemplates
점검:
GET /admin/equipment/inspections → index
PATCH /admin/equipment/inspections/detail → toggleDetail
PATCH /admin/equipment/inspections/notes → updateNotes
PATCH /admin/equipment/inspections/set-result → setResult
DELETE /admin/equipment/inspections/reset → resetInspection
DELETE /admin/equipment/inspections/reset-all → resetAllInspections
수리이력:
GET /admin/equipment/repairs → index
POST /admin/equipment/repairs → store
PUT /admin/equipment/repairs/{id} → update
DELETE /admin/equipment/repairs/{id} → destroy
사진:
POST /admin/equipment/{id}/photos → uploadPhotos
GET /admin/equipment/{id}/photos → photos
DELETE /admin/equipment/{id}/photos/{fileId} → deletePhoto
Import:
POST /admin/equipment/import/preview → importPreview
POST /admin/equipment/import → importExecute
```
### 3.7 뷰 파일 (`mng/resources/views/equipment/`)
### 4.2 뷰 파일 (`mng/resources/views/equipment/`)
```
equipment/
@@ -264,13 +322,11 @@ equipment/
└── repair-history.blade.php # 수리이력 탭
```
모바일: `mng/resources/views/mobile/inspection/show.blade.php`
---
## 4. 비즈니스 규칙
## 5. 비즈니스 규칙
### 4.1 설비 상태
### 5.1 설비 상태
| 상태 | 코드 | 설명 |
|------|------|------|
@@ -278,13 +334,13 @@ equipment/
| 유휴 | `idle` | 일시 정지 |
| 폐기 | `disposed` | 사용 중단 |
### 4.2 점검 권한
### 5.2 점검 권한
- 관리자(`isAdmin`): 모든 설비 점검 가능
- 일반 사용자: `manager_id` 또는 `sub_manager_id`와 일치하는 설비만 점검 가능
- 권한 없는 사용자가 점검 시도 시 403 응답
### 4.3 점검 결과 순환
### 5.3 점검 결과 순환
셀 클릭 시 결과가 순환한다:
@@ -292,38 +348,44 @@ equipment/
(빈칸) → good(○) → bad(X) → repaired(△) → (빈칸)
```
### 4.4 휴일/주말 점검 제한
### 5.4 휴일/주말 점검 제한
- 일일 점검(`daily`)에서 주말(토/일) 및 `holidays` 테이블 등록 휴일에는 점검 기록 불가
- 다른 주기(weekly/monthly 등)에는 제한 없음
### 4.5 설비 사진
### 5.5 설비 사진
- 최대 10장
- 업로드 시 자동 압축 (장축 2048px, JPEG 품질 85→40 점진 감소, 1MB 이하)
- PNG 투명도 있으면 PNG 유지, 없으면 JPEG 변환
- GCS(Google Cloud Storage)에 저장, Signed URL로 조회
- 업로드 시 자동 압축 (MNG에서 구현, API에서는 `File` 모델 연동)
- GCS(Google Cloud Storage)에 저장
### 4.6 엑셀 Import
### 5.6 엑셀 Import (MNG 전용)
- 한글/영문 헤더 자동 매핑 (설비코드↔equipment_code 등)
- 상태 한글 변환 (가동→active, 유휴→idle 등)
- 중복 처리: skip(건너뜀) 또는 overwrite(덮어쓰기) 선택
- 한글/영문 헤더 자동 매핑
- 중복 처리: skip 또는 overwrite 선택
- 엑셀 내 이미지(Drawing) 자동 추출 후 GCS 업로드
- API에서는 미구현 (Phase 2+ 검토)
---
## 5. 참고: MNG에서의 DB 연결
## 6. 향후 계획
MNG 모델은 `$connection = 'codebridge'`를 사용한다. 본 시스템(API)에서는 기본 connection을 사용하므로 모델 이관 시 이 속성을 제거해야 한다.
| 단계 | 내용 | 상태 |
|------|------|------|
| Phase 1 | API 백엔드 (모델, 서비스, 컨트롤러, 라우트) | ✅ 완료 |
| Phase 2 | React 프론트엔드 | 미착수 |
| Phase 3 | 테스트 + Swagger 문서 | 미착수 |
| 추가 | 엑셀 Import API | 검토 중 |
| 추가 | 모바일 점검 (React) | 검토 중 |
---
## 관련 문서
- [서비스 구축 계획](../../dev/dev_plans/equipment-service-build-plan.md)
- [MNG 구조](../../system/mng-structure.md)
- [DB 스키마 — 공통](../../system/database/commons.md)
- [options 컬럼 정책](../../dev/standards/options-column-policy.md) — 신규 테이블 생성 시 참고
- [options 컬럼 정책](../../dev/standards/options-column-policy.md)
---