Files
sam-docs/frontend/api-specs/vehicle-api.md
김보곤 0e6d4e6adf docs: [vehicle] 차량 사진 API 문서 추가
- vehicle-api.md에 사진 API 3개 엔드포인트 추가 (섹션 7)
- corporate-vehicles.md에 사진 이관 현황 및 엔드포인트 추가
2026-03-12 22:26:40 +09:00

975 lines
25 KiB
Markdown

# 차량관리 API 명세
> **작성일**: 2026-03-12
> **상태**: API 이관 예정 (MNG에서 운영중, API 엔드포인트 미구현)
> **Base URL**: `api.codebridge-x.com` (운영) / `api.dev.codebridge-x.com` (개발)
---
## 1. 개요
법인/렌트/리스 차량을 등록하고, 운행일지와 정비이력을 관리하는 API이다.
멀티테넌트 구조로 테넌트별 데이터 격리가 적용된다.
### 1.1 인증
모든 요청에 다음 헤더가 필요하다:
```
X-API-KEY: {api_key}
Authorization: Bearer {token}
```
### 1.2 공통 응답 형식
```json
{
"success": true,
"message": "조회 성공",
"data": { ... }
}
```
에러 시:
```json
{
"success": false,
"message": "에러 메시지"
}
```
### 1.3 메뉴 구성
| 메뉴 | 설명 | 섹션 |
|------|------|------|
| 차량목록 | 법인/렌트/리스 차량 등록 관리 | [3. 차량목록 API](#3-차량목록-api) |
| 차량일지 | 운행기록 관리 및 월별 통계 | [4. 차량일지 API](#4-차량일지-api) |
| 정비이력 | 정비/주유/보험 등 비용 관리 | [5. 정비이력 API](#5-정비이력-api) |
### 1.4 엔드포인트 요약
| # | Method | Path | 설명 |
|---|--------|------|------|
| 1 | GET | `/api/v1/corporate-vehicles` | 차량 목록 |
| 2 | POST | `/api/v1/corporate-vehicles` | 차량 등록 |
| 3 | GET | `/api/v1/corporate-vehicles/{id}` | 차량 상세 |
| 4 | PUT | `/api/v1/corporate-vehicles/{id}` | 차량 수정 |
| 5 | DELETE | `/api/v1/corporate-vehicles/{id}` | 차량 삭제 |
| 6 | GET | `/api/v1/corporate-vehicles/dropdown` | 차량 드롭다운 목록 |
| 7 | GET | `/api/v1/vehicle-logs` | 운행기록 목록 |
| 8 | POST | `/api/v1/vehicle-logs` | 운행기록 등록 |
| 9 | GET | `/api/v1/vehicle-logs/{id}` | 운행기록 상세 |
| 10 | PUT | `/api/v1/vehicle-logs/{id}` | 운행기록 수정 |
| 11 | DELETE | `/api/v1/vehicle-logs/{id}` | 운행기록 삭제 |
| 12 | GET | `/api/v1/vehicle-logs/summary` | 월별 용도별 통계 |
| 13 | GET | `/api/v1/vehicle-maintenances` | 정비이력 목록 |
| 14 | POST | `/api/v1/vehicle-maintenances` | 정비이력 등록 |
| 15 | GET | `/api/v1/vehicle-maintenances/{id}` | 정비이력 상세 |
| 16 | PUT | `/api/v1/vehicle-maintenances/{id}` | 정비이력 수정 |
| 17 | DELETE | `/api/v1/vehicle-maintenances/{id}` | 정비이력 삭제 |
| 18 | GET | `/api/v1/corporate-vehicles/{id}/photos` | 차량 사진 목록 |
| 19 | POST | `/api/v1/corporate-vehicles/{id}/photos` | 차량 사진 업로드 (최대 10장) |
| 20 | DELETE | `/api/v1/corporate-vehicles/{id}/photos/{fileId}` | 차량 사진 삭제 |
---
## 2. 데이터 모델
### 2.1 CorporateVehicle (차량)
```json
{
"id": 1,
"plate_number": "12가 3456",
"model": "에쿠스",
"vehicle_type": "승용차",
"ownership_type": "corporate",
"year": 2024,
"driver": "홍길동",
"status": "active",
"mileage": 15000,
"memo": "대표이사 전용",
"purchase_date": "2024-01-15",
"purchase_price": 85000000,
"contract_date": null,
"rent_company": null,
"rent_company_tel": null,
"rent_period": null,
"agreed_mileage": null,
"vehicle_price": 0,
"residual_value": 0,
"deposit": 0,
"monthly_rent": 0,
"monthly_rent_tax": 0,
"insurance_company": null,
"insurance_company_tel": null,
"log_distance": 2350,
"total_mileage": 17350,
"created_at": "2026-01-15T09:00:00.000000Z",
"updated_at": "2026-03-10T14:30:00.000000Z"
}
```
#### 필드 설명 — 공통
| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `plate_number` | string(20) | ✅ | 차량번호 |
| `model` | string(100) | ✅ | 차량 모델명 |
| `vehicle_type` | string(20) | ✅ | 차종 |
| `ownership_type` | enum | ✅ | 소유형태 |
| `year` | int | | 연식 |
| `driver` | string(50) | | 주 운전자 |
| `status` | enum | | 상태 (기본: `active`) |
| `mileage` | int | | 기준 주행거리 (정비 시 갱신, 기본: 0) |
| `memo` | text | | 메모 |
#### 필드 설명 — 법인 전용 (`ownership_type = "corporate"`)
| 필드 | 타입 | 설명 |
|------|------|------|
| `purchase_date` | date | 취득일자 |
| `purchase_price` | int | 취득가액 (원) |
#### 필드 설명 — 렌트/리스 전용 (`ownership_type = "rent" or "lease"`)
| 필드 | 타입 | 설명 |
|------|------|------|
| `contract_date` | date | 계약일자 |
| `rent_company` | string(100) | 렌트/리스 회사명 |
| `rent_company_tel` | string(20) | 회사 연락처 |
| `rent_period` | string(20) | 계약기간 |
| `agreed_mileage` | string(20) | 약정주행거리 |
| `vehicle_price` | int | 차량가액 (원) |
| `residual_value` | int | 잔존가치 (원) |
| `deposit` | int | 보증금 (원) |
| `monthly_rent` | int | 월 렌트료 공급가 (원) |
| `monthly_rent_tax` | int | 월 렌트료 부가세 (원) |
| `insurance_company` | string(100) | 보험사명 |
| `insurance_company_tel` | string(20) | 보험사 연락처 |
#### 계산 필드 (API 응답에만 포함, 저장 안 함)
| 필드 | 계산식 | 설명 |
|------|--------|------|
| `log_distance` | `SUM(vehicle_logs.distance_km)` | 운행일지 거리 합계 |
| `total_mileage` | `mileage + log_distance` | 총 주행거리 |
#### Enum 값
**ownership_type (소유형태)**:
| 값 | 라벨 | UI 배지 색상 |
|----|------|:----------:|
| `corporate` | 법인 | 보라 (purple) |
| `rent` | 렌트 | 파랑 (blue) |
| `lease` | 리스 | 초록 (green) |
**vehicle_type (차종)**:
| 값 | 설명 |
|----|------|
| `승용차` | 일반 승용차 |
| `승합차` | 승합차 |
| `화물차` | 화물차 |
| `SUV` | SUV |
**status (상태)**:
| 값 | 라벨 | UI 배지 색상 |
|----|------|:----------:|
| `active` | 운행중 | 초록 (green) |
| `maintenance` | 정비중 | 노랑 (yellow) |
| `disposed` | 처분 | 빨강 (red) |
---
### 2.2 VehicleLog (운행기록)
```json
{
"id": 1,
"logDate": "2026-03-12",
"vehicleId": 1,
"plateNumber": "12가 3456",
"model": "에쿠스",
"department": "영업부",
"driverName": "홍길동",
"tripType": "business",
"departureType": "office",
"departureName": "본사",
"departureAddress": "서울시 강남구 테헤란로 123",
"arrivalType": "client",
"arrivalName": "거래처A",
"arrivalAddress": "경기도 성남시 분당구 판교로 456",
"distanceKm": 45,
"note": "거래처방문"
}
```
#### 필드 설명
| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `vehicleId` | int | ✅ | 차량 ID (FK → corporate_vehicles) |
| `logDate` | date | ✅ | 운행일 |
| `driverName` | string(50) | ✅ | 운전자명 |
| `tripType` | enum | ✅ | 운행 용도 |
| `distanceKm` | int | ✅ | 운행거리 (km, 0 이상) |
| `department` | string(50) | | 부서 |
| `departureType` | enum | | 출발지 유형 |
| `departureName` | string(100) | | 출발지명 |
| `departureAddress` | string(200) | | 출발지 주소 |
| `arrivalType` | enum | | 도착지 유형 |
| `arrivalName` | string(100) | | 도착지명 |
| `arrivalAddress` | string(200) | | 도착지 주소 |
| `note` | string(200) | | 비고 |
> **참고**: `plateNumber`, `model`은 차량 관계에서 자동 포함된 읽기 전용 필드이다.
#### Enum 값
**tripType (운행 용도)**:
| 값 | 라벨 | UI 배지 색상 |
|----|------|:----------:|
| `commute_to` | 출근용 | 초록 (green) |
| `commute_from` | 퇴근용 | 파랑 (blue) |
| `business` | 업무용 | 보라 (purple) |
| `personal` | 비업무 | 회색 (gray) |
| `commute_round` | 출퇴근 왕복 | 초록 (green) |
| `business_round` | 업무용 왕복 | 보라 (purple) |
| `personal_round` | 비업무 왕복 | 회색 (gray) |
**departureType / arrivalType (위치 유형)**:
| 값 | 라벨 |
|----|------|
| `home` | 자택 |
| `office` | 회사 |
| `client` | 거래처 |
| `other` | 기타 |
**비고 프리셋 옵션** (버튼으로 빠른 입력):
```json
["거래처방문", "제조시설등", "회의참석", "판촉활동", "교육등"]
```
---
### 2.3 VehicleMaintenance (정비이력)
```json
{
"id": 1,
"date": "2026-03-10",
"vehicleId": 1,
"plateNumber": "12가 3456",
"model": "에쿠스",
"category": "주유",
"description": "LPG 충전",
"amount": 85000,
"mileage": 17350,
"vendor": "SK에너지 강남점",
"memo": null
}
```
#### 필드 설명
| 필드 | 타입 | 필수 | 설명 |
|------|------|:----:|------|
| `vehicleId` | int | ✅ | 차량 ID (FK → corporate_vehicles) |
| `date` | date | ✅ | 정비일 |
| `category` | string(20) | ✅ | 카테고리 |
| `amount` | int | ✅ | 금액 (원, 0 이상) |
| `description` | string(200) | | 설명 |
| `mileage` | int | | 정비 시 주행거리 (입력 시 차량 mileage 자동 갱신) |
| `vendor` | string(100) | | 업체명 |
| `memo` | text | | 메모 |
#### 카테고리 목록
| 카테고리 | 아이콘 | UI 배지 색상 |
|---------|--------|:----------:|
| `주유` | Fuel | amber |
| `정비` | Wrench | blue |
| `보험` | Shield | emerald |
| `세차` | Droplets | cyan |
| `주차` | ParkingCircle | purple |
| `통행료` | Route | orange |
| `검사` | ClipboardCheck | indigo |
| `기타` | MoreHorizontal | gray |
---
## 3. 차량목록 API
### 3.1 차량 목록 조회
```
GET /api/v1/corporate-vehicles
```
**쿼리 파라미터**:
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|-------|------|
| `ownership_type` | string | `all` | `corporate` / `rent` / `lease` / `all` |
| `vehicle_type` | string | `all` | 승용차 / 승합차 / 화물차 / SUV / `all` |
| `status` | string | `all` | `active` / `maintenance` / `disposed` / `all` |
| `search` | string | | 차량번호, 모델, 운전자 검색 |
**응답**:
```json
{
"success": true,
"data": [
{
"id": 1,
"plate_number": "12가 3456",
"model": "에쿠스",
"vehicle_type": "승용차",
"ownership_type": "corporate",
"year": 2024,
"driver": "홍길동",
"status": "active",
"mileage": 15000,
"memo": null,
"purchase_date": "2024-01-15",
"purchase_price": 85000000,
"log_distance": 2350,
"total_mileage": 17350
}
]
}
```
### 3.2 차량 등록
```
POST /api/v1/corporate-vehicles
```
**요청 Body**:
```json
{
"plate_number": "12가 3456",
"model": "에쿠스",
"vehicle_type": "승용차",
"ownership_type": "corporate",
"year": 2024,
"driver": "홍길동",
"status": "active",
"mileage": 15000,
"memo": "대표이사 전용",
"purchase_date": "2024-01-15",
"purchase_price": 85000000
}
```
**유효성 검증**:
| 필드 | 규칙 |
|------|------|
| `plate_number` | required, string, max:20 |
| `model` | required, string, max:100 |
| `vehicle_type` | required, string, max:20 |
| `ownership_type` | required, in:corporate,rent,lease |
**응답** (201):
```json
{
"success": true,
"message": "차량이 등록되었습니다.",
"data": { ... }
}
```
### 3.3 차량 상세
```
GET /api/v1/corporate-vehicles/{id}
```
### 3.4 차량 수정
```
PUT /api/v1/corporate-vehicles/{id}
```
요청 Body는 등록과 동일. 유효성 검증도 동일.
### 3.5 차량 삭제
```
DELETE /api/v1/corporate-vehicles/{id}
```
SoftDelete 적용 (복구 가능).
**응답**:
```json
{
"success": true,
"message": "차량이 삭제되었습니다."
}
```
### 3.6 차량 드롭다운 목록
```
GET /api/v1/corporate-vehicles/dropdown
```
차량일지, 정비이력에서 차량 선택 드롭다운에 사용. 간소화된 필드만 반환.
**응답**:
```json
{
"success": true,
"data": [
{ "id": 1, "plate_number": "12가 3456", "model": "에쿠스" },
{ "id": 2, "plate_number": "56나 7890", "model": "스타리아" }
]
}
```
---
## 4. 차량일지 API
### 4.1 운행기록 목록
```
GET /api/v1/vehicle-logs
```
**쿼리 파라미터**:
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|-------|------|
| `vehicle_id` | int | `all` | 차량 ID |
| `year` | int | 현재년 | 연도 |
| `month` | int | 현재월 | 월 |
| `trip_type` | string | `all` | 운행 용도 |
| `search` | string | | 운전자, 부서, 출발지, 도착지, 비고 검색 |
**응답**:
```json
{
"success": true,
"data": [
{
"id": 1,
"logDate": "2026-03-12",
"vehicleId": 1,
"plateNumber": "12가 3456",
"model": "에쿠스",
"department": "영업부",
"driverName": "홍길동",
"tripType": "business",
"departureType": "office",
"departureName": "본사",
"departureAddress": "서울시 강남구 테헤란로 123",
"arrivalType": "client",
"arrivalName": "거래처A",
"arrivalAddress": "경기도 성남시 분당구 판교로 456",
"distanceKm": 45,
"note": "거래처방문"
}
]
}
```
> **참고**: 응답 필드명은 camelCase를 사용한다 (MNG 기존 패턴 유지).
### 4.2 운행기록 등록
```
POST /api/v1/vehicle-logs
```
**요청 Body**:
```json
{
"vehicle_id": 1,
"log_date": "2026-03-12",
"department": "영업부",
"driver_name": "홍길동",
"trip_type": "business",
"departure_type": "office",
"departure_name": "본사",
"departure_address": "서울시 강남구 테헤란로 123",
"arrival_type": "client",
"arrival_name": "거래처A",
"arrival_address": "경기도 성남시 분당구 판교로 456",
"distance_km": 45,
"note": "거래처방문"
}
```
**유효성 검증**:
| 필드 | 규칙 |
|------|------|
| `vehicle_id` | required, exists:corporate_vehicles,id |
| `log_date` | required, date |
| `driver_name` | required, string, max:50 |
| `trip_type` | required, in:commute_to,commute_from,business,personal,commute_round,business_round,personal_round |
| `distance_km` | required, integer, min:0 |
**응답** (201):
```json
{
"success": true,
"message": "운행기록이 등록되었습니다.",
"data": { ... }
}
```
### 4.3 운행기록 상세
```
GET /api/v1/vehicle-logs/{id}
```
### 4.4 운행기록 수정
```
PUT /api/v1/vehicle-logs/{id}
```
요청 Body/유효성 검증은 등록과 동일.
### 4.5 운행기록 삭제
```
DELETE /api/v1/vehicle-logs/{id}
```
### 4.6 월별 용도별 통계
```
GET /api/v1/vehicle-logs/summary
```
**쿼리 파라미터**:
| 파라미터 | 타입 | 설명 |
|---------|------|------|
| `vehicle_id` | int | 차량 ID (생략 시 전체) |
| `year` | int | 연도 |
| `month` | int | 월 |
**응답**:
```json
{
"success": true,
"data": {
"byType": {
"commute_to": { "label": "출근용", "count": 5, "distance": 125 },
"commute_from": { "label": "퇴근용", "count": 5, "distance": 130 },
"business": { "label": "업무용", "count": 10, "distance": 280 },
"personal": { "label": "비업무", "count": 2, "distance": 45 }
},
"total": {
"count": 22,
"distance": 580
}
}
}
```
---
## 5. 정비이력 API
### 5.1 정비이력 목록
```
GET /api/v1/vehicle-maintenances
```
**쿼리 파라미터**:
| 파라미터 | 타입 | 기본값 | 설명 |
|---------|------|-------|------|
| `vehicle_id` | int | `all` | 차량 ID |
| `category` | string | `all` | 카테고리 (주유, 정비, 보험 등) |
| `start_date` | date | 3개월 전 | 시작일 |
| `end_date` | date | 오늘 | 종료일 |
| `search` | string | | 설명, 업체명, 메모 검색 |
**응답**:
```json
{
"success": true,
"data": [
{
"id": 1,
"date": "2026-03-10",
"vehicleId": 1,
"plateNumber": "12가 3456",
"model": "에쿠스",
"category": "주유",
"description": "LPG 충전",
"amount": 85000,
"mileage": 17350,
"vendor": "SK에너지 강남점",
"memo": null
}
]
}
```
### 5.2 정비이력 등록
```
POST /api/v1/vehicle-maintenances
```
**요청 Body**:
```json
{
"vehicle_id": 1,
"date": "2026-03-10",
"category": "주유",
"description": "LPG 충전",
"amount": 85000,
"mileage": 17350,
"vendor": "SK에너지 강남점",
"memo": null
}
```
**유효성 검증**:
| 필드 | 규칙 |
|------|------|
| `vehicle_id` | required, exists:corporate_vehicles,id |
| `date` | required, date |
| `category` | required, string, max:20 |
| `amount` | required, numeric, min:0 |
**부수 효과**: `mileage` 값이 있으면 해당 차량의 `corporate_vehicles.mileage`를 자동 갱신한다.
**응답** (201):
```json
{
"success": true,
"message": "정비 이력이 등록되었습니다.",
"data": { ... }
}
```
### 5.3 정비이력 상세
```
GET /api/v1/vehicle-maintenances/{id}
```
### 5.4 정비이력 수정
```
PUT /api/v1/vehicle-maintenances/{id}
```
요청 Body/유효성 검증은 등록과 동일. `mileage` 갱신 부수 효과도 동일.
### 5.5 정비이력 삭제
```
DELETE /api/v1/vehicle-maintenances/{id}
```
---
## 6. UI 구현 가이드
### 6.1 차량목록 화면
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "차량목록"
│ 검색 | Excel 다운로드 | 차량 등록 버튼
├─ 요약 카드 (4열) ──────────────────
│ 총 차량 | 법인 취득가 | 월 렌트/리스비 | 총 주행거리
├─ 필터 바 ──────────────────────────
│ 소유형태: 전체 | 법인 | 렌트 | 리스
│ 상태: 전체 | 운행중 | 정비중 | 처분
├─ 차량 목록 테이블 ─────────────────
│ 차량번호 | 차종/모델 | 소유형태 | 운전자 | 주행거리 | 상태
├─ 등록/수정 모달 ───────────────────
│ 차량번호, 모델명, 차종, 소유형태
│ 연식, 주 운전자, 상태, 메모
│ ─────────────────────────
│ [법인] 취득일자, 취득가액
│ [렌트/리스] 계약일, 렌트사, 계약기간,
│ 약정주행, 차량가액, 잔존가치, 보증금,
│ 월렌트료, 부가세, 보험사
│ ─────────────────────────
│ [삭제] [취소] [등록/저장]
└────────────────────────────────────
```
**요약 카드 계산**:
| 카드 | 계산식 |
|------|--------|
| 총 차량 | 전체 차량 수 |
| 법인 취득가 | `ownership_type = 'corporate'`인 차량의 `purchase_price` 합계 |
| 월 렌트/리스비 | `ownership_type in ('rent','lease')`인 차량의 `monthly_rent + monthly_rent_tax` 합계 |
| 총 주행거리 | 전체 차량의 `total_mileage` 합계 |
**조건부 폼 필드**:
- `ownership_type = "corporate"` → 법인 전용 필드 표시
- `ownership_type = "rent" or "lease"` → 렌트/리스 전용 필드 표시
---
### 6.2 차량일지 화면
```
┌─ 페이지 헤더 ──────────────────────
│ 차량 선택 드롭다운 (현재 주행거리 표시)
│ 연/월 선택 | CSV 다운로드 | 기록 추가 버튼
├─ 용도별 통계 카드 ─────────────────
│ 출근 | 퇴근 | 업무 | 비업무
│ 건수 + 총 거리(km)
├─ 운행기록 테이블 ──────────────────
│ 날짜 | 차량 | 부서/이름 | 용도 | 출발지 | 도착지 | 거리 | 비고
├─ 등록/수정 모달 ───────────────────
│ 차량 선택, 날짜, 부서, 운전자, 용도
│ 출발지: 유형 + 이름 + 주소
│ 도착지: 유형 + 이름 + 주소
│ 운행거리(km)
│ 비고: 프리셋 버튼 + 자유 입력
│ [삭제] [취소] [등록/저장]
└────────────────────────────────────
```
**특수 기능 (프론트엔드에서 처리)**:
| 기능 | 설명 |
|------|------|
| 기록 복사 | 기존 기록을 복제하여 새 날짜로 등록 (API는 일반 POST) |
| 출발↔도착 교환 | 출발지/도착지 swap + tripType 자동 전환 (`commute_to``commute_from`) |
| 비고 프리셋 | 5개 버튼 클릭 시 비고 필드에 텍스트 입력 |
---
### 6.3 정비이력 화면
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "정비이력"
│ 새로고침 | CSV 다운로드 | 정비 등록 버튼
├─ 요약 카드 (4열) ──────────────────
│ 총 정비비용 | 주유비 | 정비비 | 기타비용
├─ 필터 영역 ────────────────────────
│ 기간: 시작일 ~ 종료일 (기본: 최근 3개월)
│ 카테고리: 전체 | 주유 | 정비 | 보험 | 세차 | 주차 | 통행료 | 검사 | 기타
│ 차량: 드롭다운 선택
│ 검색: 설명, 업체명 검색
├─ 정비이력 테이블 ──────────────────
│ 날짜 | 차량 | 카테고리 | 설명 | 금액 | 주행거리 | 작업
├─ 등록/수정 모달 ───────────────────
│ 차량 선택, 날짜, 카테고리
│ 설명, 금액, 주행거리, 업체명, 메모
│ [삭제] [취소] [등록/저장]
└────────────────────────────────────
```
**요약 카드 계산** (조회된 목록 데이터 기준):
| 카드 | 계산식 |
|------|--------|
| 총 정비비용 | 전체 `amount` 합계 |
| 주유비 | `category = '주유'``amount` 합계 |
| 정비비 | `category = '정비'``amount` 합계 |
| 기타비용 | 총 정비비용 - 주유비 - 정비비 |
---
## 7. 차량 사진 API
### 7.1 사진 목록 조회
```
GET /api/v1/corporate-vehicles/{id}/photos
```
**응답 예시**:
```json
{
"success": true,
"message": "조회 성공",
"data": [
{
"id": 42,
"file_name": "차량_전면.jpg",
"file_path": "1/corporate-vehicles/2026/03/a1b2c3d4e5f6.jpg",
"file_url": "/api/v1/files/42/download",
"file_size": 2048576,
"mime_type": "image/jpeg",
"created_at": "2026-03-12 15:30:00"
}
]
}
```
### 7.2 사진 업로드
```
POST /api/v1/corporate-vehicles/{id}/photos
Content-Type: multipart/form-data
```
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|:----:|------|
| `files[]` | file[] | ✅ | 이미지 파일 (다중 업로드) |
**제약조건**:
| 항목 | 값 |
|------|------|
| 최대 보유 수량 | 10장 / 차량 |
| 허용 확장자 | jpg, jpeg, png, gif, bmp, webp |
| 파일 크기 제한 | 10MB / 파일 |
| 동적 max 계산 | `max = 10 - 현재 사진 수` |
**응답 예시** (업로드된 파일 목록 반환):
```json
{
"success": true,
"message": "등록 성공",
"data": [
{
"id": 43,
"file_name": "차량_후면.jpg",
"file_path": "1/corporate-vehicles/2026/03/f6e5d4c3b2a1.jpg",
"file_url": "/api/v1/files/43/download",
"file_size": 1536000,
"mime_type": "image/jpeg",
"created_at": "2026-03-12 16:00:00"
}
]
}
```
**에러 응답** (10장 초과 시):
```json
{
"success": false,
"message": "사진은 최대 10장까지 등록할 수 있습니다."
}
```
### 7.3 사진 삭제
```
DELETE /api/v1/corporate-vehicles/{id}/photos/{fileId}
```
**응답 예시**:
```json
{
"success": true,
"message": "삭제 성공",
"data": {
"file_id": 42,
"deleted": true
}
}
```
### 7.4 UI 구현 가이드 — 사진
```
┌─ 차량 상세/수정 페이지 ───────────────
├─ 사진 영역 ──────────────────────────
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ │ 📷 │ │ 📷 │ │ 📷 │ │ + │
│ │ 사진1 │ │ 사진2 │ │ 사진3 │ │ 추가 │
│ └──────┘ └──────┘ └──────┘ └──────┘
│ * 최대 10장 (n/10 표시)
│ * 사진 클릭 → 확대 보기
│ * 삭제 버튼 (X) 각 사진에 표시
│ * 드래그앤드롭 또는 파일 선택으로 업로드
```
**특수 기능**:
- 다중 파일 선택 지원 (`multiple` attribute)
- 업로드 전 미리보기 (FileReader API)
- 남은 업로드 가능 수량 = 10 - 현재 사진 수
- 업로드 가능 수량 초과 시 파일 선택 차단
---
## 8. 데이터 관계도
```
corporate_vehicles (차량 마스터)
├── vehicle_logs (1:N)
│ 운행기록 → distance_km 합산하여 차량 total_mileage 계산
├── vehicle_maintenances (1:N)
│ 정비기록 → mileage 입력 시 차량 mileage 기준값 갱신
└── files (1:N, polymorphic)
사진 → document_type='corporate_vehicle', document_id=vehicle.id
최대 10장, R2 스토리지 저장, Soft Delete
```
```
총 주행거리 계산:
total_mileage = corporate_vehicles.mileage (정비 시 갱신되는 기준값)
+ SUM(vehicle_logs.distance_km) (운행일지 거리 합계)
```
---
## 관련 문서
- [차량목록 기능 상세](../../features/card-vehicle/corporate-vehicles.md)
- [차량일지 기능 상세](../../features/card-vehicle/vehicle-logs.md)
- [정비이력 기능 상세](../../features/card-vehicle/vehicle-maintenance.md)
- [카드/차량관리 모듈 README](../../features/card-vehicle/README.md)
---
**최종 업데이트**: 2026-03-12