- 프론트엔드 요청 문서: 사진 업로드 API 상세 스펙 추가 - multipart/form-data files[] 배열 형식 명시 - 검증 규칙, Request/Response 예시 추가 - GCS 언급을 Cloudflare R2로 수정 - features/equipment README 동일 수정
25 KiB
설비관리 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 |
페이지 번호 |
응답:
{
"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 — 드롭다운 데이터
응답:
{
"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 — 대시보드 통계
응답:
{
"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 — 활성 점검 주기 조회
응답:
{
"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:
{
"source_cycle": "daily",
"target_cycles": ["weekly", "monthly"]
}
응답:
{
"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 | — | — | 특정 설비만 |
응답 구조 (핵심):
{
"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:
{
"equipment_id": 1,
"template_item_id": 10,
"check_date": "2026-03-12",
"cycle": "daily"
}
응답:
{
"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:
{
"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:
{
"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:
{
"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:
{
"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"
}
]
}
프론트엔드 구현 참고:
// 다중 파일 업로드 예시
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:
{
"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개 섹션:
- 기본정보: 설비코드*, 설비명*, 설비유형(select), 규격
- 제조사 정보: 제조사, 모델명, 제조번호
- 설치 정보: 위치, 생산라인(select), 구입일, 설치일, 구입가격, 내용연수
- 관리자/비고: 담당자(select), 부담당자(select), 상태(select), 비고(textarea)
드롭다운 고정값:
설비유형: 포밍기 / 미싱기 / 샤링기 / V컷팅기 / 절곡기 / 프레스 / 드릴 / 기타
생산라인: 스라트 / 스크린 / 절곡 / 기타
상태: 가동(active) / 유휴(idle) / 폐기(disposed)
담당자/부담당자 드롭다운은 사용자 API에서 동적 로드
사진 관리 (수정 화면):
- 설비 상세/수정 화면에서 사진 업로드/삭제 가능
- 최대 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 이상 △ 수리완료 ━ 비근무일
핵심 동작:
- 셀 클릭:
PATCH /inspections/toggle호출 → 결과 순환 (빈칸→○→X→△→빈칸) - 비근무일: 주말(토/일) + holidays 테이블 등록 휴일 → 클릭 불가, 음영 처리 (daily만 적용)
- 고정 열: 설비/점검항목 열은 가로 스크롤 시 고정 (sticky)
- 자동 판정: 비근무일 제외, 모든 점검항목이 good 또는 repaired면 "합격"
- 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 |
| 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 처리
6. 공통 응답 포맷
모든 API 응답은 다음 구조를 따릅니다:
{
"success": true,
"message": "성공/에러 메시지",
"data": { ... }
}
에러 시:
{
"success": false,
"message": "에러 메시지",
"errors": {
"equipment_code": ["설비코드는 필수입니다."]
}
}
7. 참고 사항
MNG R&D 화면 확인
개발서버 MNG에서 기존 구현을 확인할 수 있습니다:
- URL:
https://admin.codebridge-x.com/equipment - 대시보드, 등록대장, 점검 그리드, 수리이력 모두 동작하는 상태
개발서버 API 테스트
# 설비 목록 조회 예시
curl -H "X-Api-Key: <API_KEY>" \
-H "Authorization: Bearer <TOKEN>" \
"https://api.dev.codebridge-x.com/api/v1/equipment?per_page=5"
구현 순서 제안
- 1단계: 설비 CRUD (목록 → 등록 → 상세 → 수정) — 기본 DataTable + 폼
- 2단계: 수리이력 (목록 → 등록) — 1단계와 유사한 패턴
- 3단계: 점검 그리드 — 가장 복잡, 동적 그리드 + 셀 토글
- 4단계: 대시보드 — 통계 카드 + 간단한 차트
관련 문서
최종 업데이트: 2026-03-12