# SAM API 개발 규칙 **업데이트**: 2026-01-28 --- ## 1. Architecture Philosophy - **Service First**: All business logic must be written in Service classes (public functions) - **Controller**: Only responsible for DI injection of Services and calling them. Use ApiResponse::handle() for responses - **Exception Flow**: Errors are thrown → converted to JSON by global handler - **Context Enforcement**: Base Service requires tenantId(), apiUserId(). Throws exception if not set --- ## 2. Multi-tenancy & Models - BelongsToTenant global scope applied - ModelTrait for is_active, date handling - SoftDeletes by default - Common columns: tenant_id, created_by, updated_by, deleted_by (COMMENT required) - FK constraints: Created during design, minimal in production ### 2.1 ModelTrait 사용 가이드 `ModelTrait`는 모든 모델에서 공통으로 사용하는 기능을 제공합니다. **위치**: `app/Traits/ModelTrait.php` **제공 기능**: ```php // 1. 날짜 직렬화 포맷 (Y-m-d H:i:s) protected function serializeDate(DateTimeInterface $date) // 2. Active 상태 조회 Scope public function scopeActive($query) // 사용: Model::active()->get() // SQL: WHERE is_active = 1 ``` **⚠️ 필수 요구사항**: `scopeActive()` 메서드 사용 시 테이블에 `is_active` 컬럼이 **반드시 존재해야 함** ```sql -- 마이그레이션 예시 $table->boolean('is_active') ->default(true) ->comment('활성화 여부 (ModelTrait::scopeActive() 사용)'); ``` **모델 설정**: ```php class YourModel extends Model { use BelongsToTenant, ModelTrait, SoftDeletes; protected $fillable = [ // ... 'is_active', // 반드시 추가 ]; protected $casts = [ 'is_active' => 'boolean', // 반드시 추가 ]; } ``` **is_active 컬럼 적용 테이블** (2025-12-05 기준): | 테이블 | is_active | 비고 | |--------|-----------|------| | materials | ✅ 있음 | | | products | ✅ 있음 | | | item_pages | ✅ 있음 | | | item_fields | ✅ 있음 | 2025-12-05 추가 | | item_sections | ❌ 없음 | 필요시 마이그레이션 추가 | --- ## 3. Middleware Stack - ApiKeyMiddleware, CheckSwaggerAuth, CorsMiddleware, CheckPermission, PermMapper - **ApiVersionMiddleware** - API 버전 선택 및 폴백 처리 (v2 없으면 v1 사용) - Default route group: auth.apikey (some with auth:sanctum) --- ## 4. Routing ### 4.1 라우트 파일 구조 API 라우트는 도메인별로 분리되어 있습니다: ``` routes/api/ ├── v1/ # v1 API 라우트 │ ├── auth.php # 인증 (login, logout, signup, token) │ ├── admin.php # 관리자 (users, global-menus, FCM) │ ├── users.php # 사용자 (me, profiles, invitations, roles) │ ├── tenants.php # 테넌트 (CRUD, settings, stat-fields) │ ├── hr.php # HR (departments, positions, employees, attendances) │ ├── finance.php # 재무 (cards, deposits, withdrawals, payrolls) │ ├── sales.php # 영업 (clients, quotes, orders, pricing) │ ├── inventory.php # 재고 (items, BOM, stocks, shipments) │ ├── production.php # 생산 (processes, work-orders, inspections) │ ├── design.php # 설계 (models, versions, BOM templates) │ ├── files.php # 파일 (upload, download, folders) │ ├── boards.php # 게시판 (boards, posts, comments) │ └── common.php # 공통 (menus, roles, permissions, settings) ├── v2/ # v2 API 라우트 (필요시 생성) └── api.php # 라우트 로더 ``` ### 4.2 API 버전 폴백 시스템 **버전 선택 방법 (우선순위 순):** 1. `Accept-Version` 헤더: `Accept-Version: v2` 2. `X-API-Version` 헤더: `X-API-Version: v2` 3. `api_version` 쿼리 파라미터: `?api_version=v2` 4. 기본값: `v1` **폴백 동작:** - v2 요청 시 해당 라우트가 v2에 없으면 자동으로 v1 라우트 사용 - 응답 헤더 `X-API-Version`에 실제 사용된 버전 표시 **사용 예시:** ```bash # v1 명시적 요청 curl -H "Accept-Version: v1" https://api.sam.kr/api/v1/users # v2 요청 (v2 없으면 v1으로 폴백) curl -H "Accept-Version: v2" https://api.sam.kr/api/v1/users # 쿼리 파라미터로 버전 지정 curl "https://api.sam.kr/api/v1/users?api_version=v2" # 버전 미지정 (기본 v1) curl https://api.sam.kr/api/v1/users ``` ### 4.3 REST 컨벤션 - 기본 CRUD: `index`, `show`, `store`, `update`, `destroy` - 확장 메서드: `toggle`, `bulkUpsert`, `reorder`, `stats`, `options` --- ## 5. Controller/Service Rules - **Controller**: FormRequest type-hint → only pass $request->validated() to Service - **Service**: extends Service, tenantId()/apiUserId() required - **Lists**: Pagination, explicit search parameters - **Responses**: {success, message, data}, message must be i18n key only --- ## 6. i18n & Response Messages - Messages: lang/{locale}/message.php - Errors: lang/{locale}/error.php - **Rule**: No direct strings, must use __('message.xxx'), __('error.xxx') - Common keys: message.fetched/created/updated/deleted/bulk_upsert/reordered - Resource-specific keys allowed: message.product.created, message.bom.bulk_upsert --- ## 7. Validation (FormRequest) - **No direct validate() calls**. Separate all into FormRequest classes - **Reuse common Requests**: PaginateRequest, BomItemsRequest, DateRangeRequest - **Boundary validation** (effective_from ≤ effective_to) in Request --- ## 8. Audit Logging - **Table**: audit_logs (tenant_id, target_type, target_id, action, before/after(json), actor_id, ip, ua, created_at) - **Actions**: created, updated, deleted, released, cloned, items_replaced, diff_viewed - **Retention**: 13 months default, audit:prune scheduler cleanup - **Failure tolerance**: Log failures don't block business operations --- ## 9. Domain Rules - **Status**: Strings (DRAFT/RELEASED/ARCHIVED) - **Unique/Indexes**: models(code), model_versions(version_no), bom_items(parent, order) - **BOM**: Supports summary/validate/bulkUpsert/reorder --- ## 개발 시 필수 체크 ``` ✓ Service-First 패턴 (Controller는 단순) ✓ BelongsToTenant scope 적용 ✓ ModelTrait 사용 ✓ SoftDeletes 적용 ✓ FormRequest 검증 ✓ i18n 키 사용 (__('message.xxx')) ✓ 감사 로그 고려 ✓ tenant_id 필터링 확인 ```