2025-11-24 18:49:02 +09:00
|
|
|
# MNG 프로젝트 Critical Rules
|
|
|
|
|
|
|
|
|
|
> 🚨 **절대 위반하지 말아야 할 규칙들**
|
|
|
|
|
|
|
|
|
|
**작성일**: 2025-11-24
|
|
|
|
|
**목적**: 반복되는 실수 방지 및 프로젝트 정책 명확화
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🚫 절대 금지 사항
|
|
|
|
|
|
|
|
|
|
### 1. DB 마이그레이션 금지
|
|
|
|
|
|
|
|
|
|
**규칙:**
|
|
|
|
|
- ❌ mng/에서는 **절대로** 마이그레이션 파일 생성 금지
|
|
|
|
|
- ❌ 기존 테이블 구조 변경 금지
|
|
|
|
|
- ✅ 모델 관계 정의만 가능
|
|
|
|
|
- ✅ 필요 시 api/에 마이그레이션 요청
|
|
|
|
|
|
|
|
|
|
**이유:**
|
|
|
|
|
- mng/는 api/의 DB를 **읽기 전용**으로 사용
|
|
|
|
|
- DB 스키마는 **api/에서만** 관리
|
|
|
|
|
- 여러 저장소(api, admin, mng)가 동일 DB 공유
|
|
|
|
|
|
2025-11-30 21:04:19 +09:00
|
|
|
**mng 전용 테이블 생성 시:**
|
|
|
|
|
- ✅ mng에서만 사용하는 테이블은 `admin_*` 접두사 사용
|
|
|
|
|
- ✅ **마이그레이션은 무조건 api/에서 생성** (mng에서 생성 금지!)
|
|
|
|
|
- 예: `admin_pm_projects`, `admin_pm_tasks` 등
|
2025-11-24 18:49:02 +09:00
|
|
|
|
|
|
|
|
**실수 사례:**
|
|
|
|
|
```php
|
|
|
|
|
// ❌ 잘못된 예: mng/에서 users 테이블 수정 시도
|
|
|
|
|
Schema::table('users', function (Blueprint $table) {
|
|
|
|
|
$table->foreignId('tenant_id')->comment('테넌트 ID');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ✅ 올바른 예: User 모델에 관계만 정의
|
|
|
|
|
public function tenants(): BelongsToMany
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsToMany(Tenant::class, 'user_tenants');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 2. 기존 테이블은 재사용만
|
|
|
|
|
|
|
|
|
|
**재사용 테이블:**
|
|
|
|
|
- `users` - 사용자 계정
|
|
|
|
|
- `tenants` - 테넌트 (회사)
|
|
|
|
|
- `user_tenants` - 사용자-테넌트 관계 (pivot)
|
|
|
|
|
- `roles` - 역할
|
|
|
|
|
- `departments` - 부서
|
|
|
|
|
- `permissions` - 권한
|
|
|
|
|
- `products`, `materials`, `categories` 등 모든 비즈니스 테이블
|
|
|
|
|
|
|
|
|
|
**작업 방법:**
|
|
|
|
|
1. **모델 복사**: admin/app/Models → mng/app/Models
|
|
|
|
|
2. **Filament 코드 제거**: form(), table() 등 제거
|
|
|
|
|
3. **관계만 유지**: belongsTo, hasMany, belongsToMany
|
|
|
|
|
4. **Traits 적용**: BelongsToTenant (필요 시), SoftDeletes
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 3. Multi-tenant 아키텍처 이해
|
|
|
|
|
|
|
|
|
|
**user_tenants pivot 테이블 구조:**
|
|
|
|
|
```
|
|
|
|
|
user_tenants:
|
|
|
|
|
├── user_id (FK → users)
|
|
|
|
|
├── tenant_id (FK → tenants)
|
|
|
|
|
├── is_active (활성 여부)
|
|
|
|
|
├── is_default (기본 테넌트)
|
|
|
|
|
├── joined_at (가입일)
|
|
|
|
|
└── left_at (탈퇴일)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**관계 정의:**
|
|
|
|
|
```php
|
|
|
|
|
// User 모델
|
|
|
|
|
public function tenants(): BelongsToMany
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsToMany(Tenant::class, 'user_tenants')
|
|
|
|
|
->withTimestamps()
|
|
|
|
|
->withPivot(['is_active', 'is_default', 'joined_at', 'left_at']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tenant 모델
|
|
|
|
|
public function users(): BelongsToMany
|
|
|
|
|
{
|
|
|
|
|
return $this->belongsToMany(User::class, 'user_tenants');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**세션 기반 테넌트 선택:**
|
|
|
|
|
- `session('selected_tenant_id')` - 현재 선택된 테넌트
|
|
|
|
|
- `currentTenant()` 헬퍼 메서드 사용 권장
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 4. BelongsToTenant Trait 사용 주의
|
|
|
|
|
|
|
|
|
|
**언제 사용하지 말아야 하나:**
|
|
|
|
|
- ❌ `users` 테이블 - user_tenants pivot 사용
|
|
|
|
|
- ❌ `tenants` 테이블 - 테넌트 자체
|
|
|
|
|
- ❌ 다대다 관계 테이블
|
|
|
|
|
|
|
|
|
|
**언제 사용해야 하나:**
|
|
|
|
|
- ✅ `roles` - tenant_id 컬럼 있음
|
|
|
|
|
- ✅ `departments` - tenant_id 컬럼 있음
|
|
|
|
|
- ✅ `products` - tenant_id 컬럼 있음
|
|
|
|
|
|
|
|
|
|
**판단 기준:**
|
|
|
|
|
- 테이블에 직접 `tenant_id` 컬럼이 있으면 → BelongsToTenant 사용
|
|
|
|
|
- pivot 테이블로 관계가 맺어지면 → belongsToMany 관계만 정의
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 5. Service-First 패턴 엄수
|
|
|
|
|
|
|
|
|
|
**규칙:**
|
|
|
|
|
- ✅ 비즈니스 로직은 **Service 클래스**에만
|
|
|
|
|
- ✅ Controller는 FormRequest + Service 호출만
|
|
|
|
|
- ✅ Model은 관계와 accessor만
|
|
|
|
|
|
|
|
|
|
**잘못된 예:**
|
|
|
|
|
```php
|
|
|
|
|
// ❌ Controller에 로직
|
|
|
|
|
public function store(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$user = User::create($request->all());
|
|
|
|
|
$user->tenant_id = session('selected_tenant_id');
|
|
|
|
|
$user->save();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**올바른 예:**
|
|
|
|
|
```php
|
|
|
|
|
// ✅ Service에 로직
|
|
|
|
|
class UserService
|
|
|
|
|
{
|
|
|
|
|
public function createUser(array $data): User
|
|
|
|
|
{
|
|
|
|
|
// 비즈니스 로직
|
|
|
|
|
return User::create($data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Controller는 호출만
|
|
|
|
|
public function store(StoreUserRequest $request, UserService $service)
|
|
|
|
|
{
|
|
|
|
|
$user = $service->createUser($request->validated());
|
|
|
|
|
return response()->json(['success' => true]);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 6. FormRequest 필수 사용
|
|
|
|
|
|
|
|
|
|
**규칙:**
|
|
|
|
|
- ❌ Controller에서 `$request->validate()` 금지
|
|
|
|
|
- ✅ 모든 검증은 FormRequest 클래스로
|
|
|
|
|
|
|
|
|
|
**예:**
|
|
|
|
|
```php
|
|
|
|
|
// ✅ StoreUserRequest.php
|
|
|
|
|
class StoreUserRequest extends FormRequest
|
|
|
|
|
{
|
|
|
|
|
public function rules(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'name' => 'required|string|max:255',
|
|
|
|
|
'email' => 'required|email|unique:users',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ Controller
|
|
|
|
|
public function store(StoreUserRequest $request)
|
|
|
|
|
{
|
|
|
|
|
// $request->validated() 만 Service에 전달
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2025-11-30 21:04:19 +09:00
|
|
|
### 7. MNG 데이터 접근 아키텍처
|
|
|
|
|
|
|
|
|
|
**MNG는 자체 내부 API 사용 (외부 api/ 프로젝트 호출 안 함)**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
│ routes/web.php (Blade 화면만) │
|
|
|
|
|
│ └─ Controller → view('xxx.index') (데이터 없이 화면만) │
|
|
|
|
|
│ │
|
|
|
|
|
│ routes/api.php (HTMX 호출 + CRUD) │
|
|
|
|
|
│ └─ Api/Admin/Controller → Service → Model → DB │
|
|
|
|
|
│ │
|
|
|
|
|
│ Blade에서 HTMX로 /api/admin/* 호출 │
|
|
|
|
|
│ └─ hx-get="/api/admin/tenants" │
|
|
|
|
|
│ └─ hx-post="/api/admin/tenants" │
|
|
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**규칙:**
|
|
|
|
|
- ❌ 외부 api/ 프로젝트의 API 호출 금지
|
|
|
|
|
- ✅ mng 내부 API (`/api/admin/*`) 사용
|
|
|
|
|
- ✅ Service → Model → DB (직접 접근)
|
|
|
|
|
|
|
|
|
|
**Controller 구분:**
|
|
|
|
|
- `app/Http/Controllers/XxxController.php` → Blade 화면만 (GET)
|
|
|
|
|
- `app/Http/Controllers/Api/Admin/XxxController.php` → CRUD 처리 (HTMX)
|
|
|
|
|
|
|
|
|
|
**예시:**
|
|
|
|
|
```php
|
|
|
|
|
// ✅ Web Controller: 화면만 반환
|
|
|
|
|
class TenantController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function index(): View
|
|
|
|
|
{
|
|
|
|
|
return view('tenants.index'); // 데이터 없이 화면만
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ API Controller: CRUD 처리
|
|
|
|
|
class Api\Admin\TenantController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function index(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$tenants = $this->tenantService->getTenants($request->all());
|
|
|
|
|
return view('tenants.partials.list', compact('tenants')); // HTML partial
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2025-11-24 18:49:02 +09:00
|
|
|
## 📋 작업 전 체크리스트
|
|
|
|
|
|
|
|
|
|
### DB 작업 시:
|
|
|
|
|
```
|
|
|
|
|
□ mng/에서 작업 중인가? → 마이그레이션 금지!
|
|
|
|
|
□ 기존 테이블 수정인가? → api/에 요청!
|
2025-11-30 21:04:19 +09:00
|
|
|
□ mng 전용 새 테이블인가? → admin_* 접두사 + api/에 마이그레이션!
|
2025-11-24 18:49:02 +09:00
|
|
|
□ 관계만 추가인가? → OK, 모델만 수정
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 모델 작업 시:
|
|
|
|
|
```
|
|
|
|
|
□ tenant_id 컬럼 있는가? → BelongsToTenant trait
|
|
|
|
|
□ pivot 테이블 사용하는가? → belongsToMany 관계만
|
|
|
|
|
□ 비즈니스 로직 있는가? → Service로 이동!
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Controller 작업 시:
|
|
|
|
|
```
|
|
|
|
|
□ FormRequest 생성했는가?
|
|
|
|
|
□ Service 클래스 생성했는가?
|
|
|
|
|
□ Controller는 호출만 하는가?
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔗 관련 문서
|
|
|
|
|
|
|
|
|
|
- **[INDEX.md](./INDEX.md)** - MNG 프로젝트 개요
|
|
|
|
|
- **[MIGRATION_PLAN.md](./MIGRATION_PLAN.md)** - Admin → MNG 마이그레이션
|
|
|
|
|
- **[DEV_PROCESS.md](../../claudedocs/mng/DEV_PROCESS.md)** - 개발 프로세스
|
|
|
|
|
- **[database-schema.md](../../docs/specs/database-schema.md)** - DB 스키마
|
|
|
|
|
- **[api-rules.md](../../docs/reference/api-rules.md)** - API 개발 규칙
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📝 실수 사례 로그
|
|
|
|
|
|
|
|
|
|
### 2025-11-24: users 테이블 마이그레이션 시도
|
|
|
|
|
- **문제**: mng/에서 users 테이블에 tenant_id 추가 시도
|
|
|
|
|
- **원인**: user_tenants pivot 테이블 존재를 간과
|
|
|
|
|
- **교훈**: 다대다 관계는 belongsToMany로, 마이그레이션 금지
|
|
|
|
|
- **해결**: User 모델에 tenants() 관계만 추가
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2025-11-30 21:04:19 +09:00
|
|
|
**최종 업데이트**: 2025-11-27
|
2025-11-24 18:49:02 +09:00
|
|
|
**다음 리뷰**: 반복 실수 발생 시 업데이트
|