Files
sam-docs/dev/dev_plans/equipment-service-build-plan.md

19 KiB

설비관리 본 시스템 구축 계획

작성일: 2026-03-12 상태: 계획 수립 담당: R&D실 R&D 출처: MNG 설비관리 (R&D 완료)


1. 개요

1.1 목적

MNG에서 R&D 완료된 설비관리 기능을 본 시스템(API + React)에 서비스로 구축한다.

1.2 범위

구분 내용
API (Laravel) 모델, 서비스, 컨트롤러, Request, Swagger, 라우트
React (Next.js) 페이지 5개, 컴포넌트, Hook, 타입, API 연동
DB 테이블 이미 존재 (마이그레이션 완료) — options 컬럼 추가 필요

1.3 MNG → 본 시스템 차이점

항목 MNG (R&D) API + React (본 시스템)
아키텍처 Blade + HTMX REST API + Next.js SPA
인증 세션 기반 (auth()) Sanctum 토큰 (apiUserId())
테넌트 session('selected_tenant_id') tenantId() (Service 기본클래스)
DB 연결 $connection = 'codebridge' 기본 connection (제거)
응답 형식 Blade View / JsonResponse ApiResponse::handle()
감사 로그 없음 Auditable trait 적용
메시지 한글 직접 사용 i18n 키 (__('message.xxx'))
파일 업로드 GCS 직접 호출 API 파일 서비스 통합

2. 단계별 계획

Phase 1: API 백엔드 (1일차)

DB 테이블은 이미 존재하므로 options 컬럼 추가 마이그레이션 후 모델부터 시작한다.

2.1 마이그레이션 — options 컬럼 추가

정책: docs/standards/options-column-policy.md 준수

api/database/migrations/
└── YYYY_MM_DD_add_options_to_equipment_tables.php
    - equipments: options JSON nullable
    - equipment_repairs: options JSON nullable

2.2 모델 생성

api/app/Models/Equipment/
├── Equipment.php
├── EquipmentInspection.php
├── EquipmentInspectionDetail.php
├── EquipmentInspectionTemplate.php
├── EquipmentRepair.php
└── EquipmentProcess.php

MNG 대비 변경 사항:

항목 MNG API
$connection 'codebridge' 제거 (기본 connection)
Traits BelongsToTenant Auditable, BelongsToTenant, ModelTrait, SoftDeletes
$casts 기본 'options' => 'array' 추가
헬퍼 없음 getOption(), setOption() 추가

2.3 서비스 생성

api/app/Services/Equipment/
├── EquipmentService.php           # 설비 CRUD + 대시보드
├── EquipmentInspectionService.php # 점검 CRUD + 그리드
├── EquipmentRepairService.php     # 수리이력 CRUD
└── EquipmentPhotoService.php      # 사진 업로드/삭제

MNG 대비 변경 사항:

항목 MNG API
기본 클래스 없음 extends Service
테넌트 session('selected_tenant_id', 1) $this->tenantId()
사용자 auth()->id() $this->apiUserId()
에러 throw new \Exception(...) throw new HttpException(403, __('error.xxx'))
트랜잭션 없음 DB::transaction() 적용

엑셀 Import 서비스는 Phase 2에서 구현 (1차에서는 CRUD + 점검 + 수리 우선)

2.4 FormRequest 생성

api/app/Http/Requests/V1/Equipment/
├── StoreEquipmentRequest.php
├── UpdateEquipmentRequest.php
├── StoreEquipmentRepairRequest.php
├── StoreInspectionTemplateRequest.php
├── ToggleInspectionDetailRequest.php
└── UpdateInspectionNotesRequest.php

2.5 컨트롤러 생성

api/app/Http/Controllers/V1/Equipment/
├── EquipmentController.php           # 설비 CRUD + 통계
├── EquipmentInspectionController.php # 점검 그리드 + 셀 토글
├── EquipmentRepairController.php     # 수리이력 CRUD
└── EquipmentPhotoController.php      # 사진 관리

패턴 (기존 API 컨벤션):

class EquipmentController extends Controller
{
    public function __construct(private readonly EquipmentService $service) {}

    public function index(Request $request): JsonResponse
    {
        return ApiResponse::handle(
            fn () => $this->service->index($request->only([...])),
            'message.fetched'
        );
    }

    public function store(StoreEquipmentRequest $request): JsonResponse
    {
        return ApiResponse::handle(
            fn () => $this->service->store($request->validated()),
            'message.created'
        );
    }
}

2.6 라우트 등록

api/routes/api/v1/equipment.php (신규 파일)
Route::prefix('equipment')->group(function () {
    // 설비 CRUD
    Route::get('', [EquipmentController::class, 'index']);
    Route::get('/options', [EquipmentController::class, 'options']);
    Route::get('/stats', [EquipmentController::class, 'stats']);
    Route::post('', [EquipmentController::class, 'store']);
    Route::get('/{id}', [EquipmentController::class, 'show'])->whereNumber('id');
    Route::put('/{id}', [EquipmentController::class, 'update'])->whereNumber('id');
    Route::delete('/{id}', [EquipmentController::class, 'destroy'])->whereNumber('id');
    Route::post('/{id}/restore', [EquipmentController::class, 'restore'])->whereNumber('id');
    Route::patch('/{id}/toggle', [EquipmentController::class, 'toggleActive'])->whereNumber('id');

    // 점검 템플릿
    Route::get('/{id}/templates', [EquipmentInspectionController::class, 'templates'])->whereNumber('id');
    Route::post('/{id}/templates', [EquipmentInspectionController::class, 'storeTemplate'])->whereNumber('id');
    Route::put('/templates/{templateId}', [EquipmentInspectionController::class, 'updateTemplate']);
    Route::delete('/templates/{templateId}', [EquipmentInspectionController::class, 'deleteTemplate']);
    Route::post('/{id}/templates/copy', [EquipmentInspectionController::class, 'copyTemplates'])->whereNumber('id');

    // 점검
    Route::get('/inspections', [EquipmentInspectionController::class, 'index']);
    Route::patch('/inspections/toggle', [EquipmentInspectionController::class, 'toggleDetail']);
    Route::patch('/inspections/set-result', [EquipmentInspectionController::class, 'setResult']);
    Route::patch('/inspections/notes', [EquipmentInspectionController::class, 'updateNotes']);
    Route::delete('/inspections/reset', [EquipmentInspectionController::class, 'resetInspection']);

    // 수리이력
    Route::get('/repairs', [EquipmentRepairController::class, 'index']);
    Route::post('/repairs', [EquipmentRepairController::class, 'store']);
    Route::put('/repairs/{id}', [EquipmentRepairController::class, 'update'])->whereNumber('id');
    Route::delete('/repairs/{id}', [EquipmentRepairController::class, 'destroy'])->whereNumber('id');

    // 사진
    Route::get('/{id}/photos', [EquipmentPhotoController::class, 'index'])->whereNumber('id');
    Route::post('/{id}/photos', [EquipmentPhotoController::class, 'store'])->whereNumber('id');
    Route::delete('/{id}/photos/{fileId}', [EquipmentPhotoController::class, 'destroy'])->whereNumber('id');
});

2.7 Enum 이관

api/app/Enums/InspectionCycle.php

MNG의 InspectionCycle 그대로 이관. Holiday 모델 참조 경로만 API 쪽으로 변경.

2.8 Swagger 문서

api/app/Swagger/v1/EquipmentApi.php

스키마: Equipment, EquipmentRepair, EquipmentInspection, InspectionTemplate, InspectionDetail

2.9 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' => '설비를 찾을 수 없습니다.',
    'no_inspect_permission' => '점검 권한이 없습니다.',
    'non_working_day' => '휴일/주말에는 점검을 기록할 수 없습니다.',
],

Phase 2: React 프론트엔드 (2~3일차)

2.10 타입 정의

react/src/types/equipment.ts
interface Equipment {
  id: number;
  equipment_code: string;
  name: string;
  equipment_type: string | null;
  production_line: string | null;
  status: 'active' | 'idle' | 'disposed';
  manager?: { id: number; name: string };
  sub_manager?: { id: number; name: string };
  purchase_date: string | null;
  install_date: string | null;
  purchase_price: number | null;
  useful_life: number | null;
  memo: string | null;
  options: Record<string, any> | null;
}

interface EquipmentRepair { ... }
interface InspectionTemplate { ... }
interface InspectionData { ... }
interface InspectionDetail { ... }

interface EquipmentStats {
  total: number;
  active: number;
  idle: number;
  disposed: number;
}

2.11 API 서비스

react/src/lib/api/equipment.ts

buildApiUrl() + /api/proxy/equipment/* 패턴 사용.

2.12 페이지 구조

react/src/app/[locale]/(protected)/equipment/
├── page.tsx                    # 설비 대시보드
├── registry/
│   ├── page.tsx                # 설비 대장 목록 (UniversalListPage)
│   ├── create/page.tsx         # 설비 등록
│   └── [id]/
│       ├── page.tsx            # 설비 상세
│       └── edit/page.tsx       # 설비 수정
├── inspections/
│   └── page.tsx                # 점검 그리드
└── repairs/
    ├── page.tsx                # 수리이력 목록
    └── create/page.tsx         # 수리이력 등록

2.13 컴포넌트 구조

react/src/components/equipment/
├── EquipmentDashboard.tsx      # 대시보드 (통계 카드 + 차트)
├── EquipmentListClient.tsx     # 설비 목록 (UniversalListPage)
├── EquipmentDetailClient.tsx   # 설비 상세 (탭: 기본정보/점검항목/수리이력/사진)
├── EquipmentForm.tsx           # 등록/수정 폼 (Zod + React Hook Form)
├── InspectionGrid.tsx          # 점검 그리드 (6주기, 셀 토글)
├── InspectionCycleSelector.tsx # 주기/기간 선택
├── RepairListClient.tsx        # 수리이력 목록
├── RepairForm.tsx              # 수리이력 등록/수정 폼
└── EquipmentPhotoGallery.tsx   # 사진 갤러리 (업로드/삭제)

2.14 Hook

react/src/hooks/
├── useEquipmentList.ts         # 설비 목록 (검색, 필터, 페이징)
├── useEquipmentDetail.ts       # 설비 상세 로딩
├── useInspectionGrid.ts        # 점검 그리드 데이터 + 셀 토글
└── useEquipmentRepairs.ts      # 수리이력 목록

2.15 페이지별 구현 상세

설비 대시보드 (/equipment):

  • 통계 카드 4개 (총/가동/유휴/폐기) → StatCards 재사용
  • 유형별 분포 차트 (Recharts PieChart)
  • 최근 수리이력 테이블 (5건)
  • 이번 달 점검 현황

설비 대장 (/equipment/registry):

  • UniversalListPage<Equipment> 적용
  • 탭: 전체 / 가동 / 유휴 / 폐기
  • 검색: 코드, 설비명
  • 필터: 생산라인, 설비유형
  • 엑셀 다운로드

설비 상세 (/equipment/registry/[id]):

  • 탭 구조: 기본정보 | 점검항목 | 수리이력 | 사진
  • 기본정보: FormField 그리드 (읽기모드/수정모드 전환)
  • 점검항목: 템플릿 CRUD (주기별)
  • 수리이력: 해당 설비 필터 적용된 테이블
  • 사진: 갤러리 + 업로드 + 삭제

점검 그리드 (/equipment/inspections):

  • 주기 선택 탭 (일일~반년)
  • 기간 선택 (월/년)
  • 생산라인 필터
  • 그리드: 설비×점검항목 → 날짜/기간별 셀
  • 셀 클릭 → API 호출 → 결과 순환 (○→X→△→빈칸)
  • 종합판정, 수리내역, 이상내용 편집

수리이력 (/equipment/repairs):

  • UniversalListPage<EquipmentRepair> 적용
  • 필터: 설비, 보전구분, 날짜 범위
  • 등록/수정 모달 또는 별도 페이지

Phase 3: 통합 및 검증 (4일차)

2.16 API 테스트

# Swagger UI에서 검증
docker exec sam-api-1 php artisan l5-swagger:generate
# http://api.sam.kr/api-docs

2.17 React 연동 테스트

- 설비 CRUD (등록→조회→수정→삭제→복원)
- 점검 그리드 (주기 전환, 셀 토글, 노트 저장)
- 수리이력 (등록→수정→삭제)
- 사진 (업로드→조회→삭제)
- 권한 (담당자/비담당자 점검 제한)
- 모바일 반응형

2.18 Pint + 코드 품질

cd /home/aweso/sam/api && docker exec sam-api-1 ./vendor/bin/pint

3. 파일 목록 총정리

API (sam/api) — 신규 생성

# 파일 설명
1 database/migrations/YYYY_add_options_to_equipment_tables.php options 컬럼 추가
2 app/Models/Equipment/Equipment.php 설비 모델
3 app/Models/Equipment/EquipmentInspection.php 점검 모델
4 app/Models/Equipment/EquipmentInspectionDetail.php 점검 상세 모델
5 app/Models/Equipment/EquipmentInspectionTemplate.php 점검 템플릿 모델
6 app/Models/Equipment/EquipmentRepair.php 수리이력 모델
7 app/Models/Equipment/EquipmentProcess.php 설비-공정 pivot
8 app/Enums/InspectionCycle.php 점검주기 Enum
9 app/Services/Equipment/EquipmentService.php 설비 서비스
10 app/Services/Equipment/EquipmentInspectionService.php 점검 서비스
11 app/Services/Equipment/EquipmentRepairService.php 수리 서비스
12 app/Services/Equipment/EquipmentPhotoService.php 사진 서비스
13 app/Http/Controllers/V1/Equipment/EquipmentController.php 설비 컨트롤러
14 app/Http/Controllers/V1/Equipment/EquipmentInspectionController.php 점검 컨트롤러
15 app/Http/Controllers/V1/Equipment/EquipmentRepairController.php 수리 컨트롤러
16 app/Http/Controllers/V1/Equipment/EquipmentPhotoController.php 사진 컨트롤러
17 app/Http/Requests/V1/Equipment/StoreEquipmentRequest.php 설비 등록 검증
18 app/Http/Requests/V1/Equipment/UpdateEquipmentRequest.php 설비 수정 검증
19 app/Http/Requests/V1/Equipment/StoreEquipmentRepairRequest.php 수리 등록 검증
20 app/Http/Requests/V1/Equipment/StoreInspectionTemplateRequest.php 템플릿 등록 검증
21 app/Http/Requests/V1/Equipment/ToggleInspectionDetailRequest.php 점검 토글 검증
22 app/Http/Requests/V1/Equipment/UpdateInspectionNotesRequest.php 점검 노트 검증
23 app/Swagger/v1/EquipmentApi.php Swagger 문서
24 routes/api/v1/equipment.php 라우트 파일

수정 파일:

파일 변경
routes/api.php equipment.php include 추가
lang/ko/message.php equipment 메시지 추가
lang/ko/error.php equipment 에러 메시지 추가

React (sam/react) — 신규 생성

# 파일 설명
1 src/types/equipment.ts 타입 정의
2 src/lib/api/equipment.ts API 서비스
3 src/hooks/useEquipmentList.ts 목록 Hook
4 src/hooks/useEquipmentDetail.ts 상세 Hook
5 src/hooks/useInspectionGrid.ts 점검 그리드 Hook
6 src/hooks/useEquipmentRepairs.ts 수리이력 Hook
7 src/app/[locale]/(protected)/equipment/page.tsx 대시보드
8 src/app/[locale]/(protected)/equipment/registry/page.tsx 설비 목록
9 src/app/[locale]/(protected)/equipment/registry/create/page.tsx 설비 등록
10 src/app/[locale]/(protected)/equipment/registry/[id]/page.tsx 설비 상세
11 src/app/[locale]/(protected)/equipment/registry/[id]/edit/page.tsx 설비 수정
12 src/app/[locale]/(protected)/equipment/inspections/page.tsx 점검 그리드
13 src/app/[locale]/(protected)/equipment/repairs/page.tsx 수리이력
14 src/app/[locale]/(protected)/equipment/repairs/create/page.tsx 수리 등록
15 src/components/equipment/EquipmentDashboard.tsx 대시보드
16 src/components/equipment/EquipmentListClient.tsx 목록
17 src/components/equipment/EquipmentDetailClient.tsx 상세
18 src/components/equipment/EquipmentForm.tsx
19 src/components/equipment/InspectionGrid.tsx 점검 그리드
20 src/components/equipment/InspectionCycleSelector.tsx 주기 선택
21 src/components/equipment/RepairListClient.tsx 수리 목록
22 src/components/equipment/RepairForm.tsx 수리 폼
23 src/components/equipment/EquipmentPhotoGallery.tsx 사진 갤러리

4. 구현 우선순위

순위 기능 이유
🔴 1 설비 CRUD (API + React) 기본 데이터 관리
🔴 2 점검 그리드 (API + React) 핵심 업무 기능, 일일 사용
🔴 3 수리이력 (API + React) 설비 이력 추적
🟡 4 사진 관리 GCS 연동 필요
🟡 5 대시보드 통계 조회 전용
🟢 6 엑셀 Import Phase 2 (초기 데이터 입력용)
🟢 7 모바일 점검 Phase 2 (Capacitor 연동)

5. 주의사항

5.1 DB 관련

  • 마이그레이션은 API에서만 생성 (sam/api/database/migrations/)
  • options 컬럼 추가 시 기존 데이터 영향 없음 (nullable)
  • 테이블 이미 존재하므로 스키마 변경 최소화

5.2 MNG 코드 이관 시

  • $connection = 'codebridge' → 제거
  • session('selected_tenant_id')$this->tenantId()
  • auth()->id()$this->apiUserId()
  • throw new \Exception() → i18n 메시지 + HttpException
  • Auditable trait 추가 (감사 로그 자동화)
  • 한글 직접 문자열 → __('message.equipment.xxx') 변환

5.3 React 관련

  • 'use client' 필수 (모든 페이지 컴포넌트)
  • API 호출은 /api/proxy/equipment/* 프록시 경로
  • buildApiUrl() 사용 필수
  • UniversalListPage 템플릿 재사용 (목록 페이지)
  • Zod + React Hook Form (폼 검증)
  • 모바일 반응형 필수 (ListMobileCard)

관련 문서


최종 업데이트: 2026-03-12