# MNG HTMX + API 패턴 가이드
**작성일:** 2025-01-24
**목적:** MNG 프로젝트의 표준 HTMX + API 패턴 문서화 (Tenant 패턴 기반)
**관련 문서:**
- [LAYOUT_PATTERN.md](./LAYOUT_PATTERN.md) - 페이지 레이아웃 및 Tenant Selector 패턴
- [99_TECHNICAL_STANDARDS.md](./99_TECHNICAL_STANDARDS.md) - SAM API Rules 기반 기술 표준
---
## 📋 목차
1. [패턴 개요](#1-패턴-개요)
2. [아키텍처 구조](#2-아키텍처-구조)
3. [구현 가이드](#3-구현-가이드)
4. [파일 구조](#4-파일-구조)
5. [체크리스트](#5-체크리스트)
---
## 1. 패턴 개요
### 1.1 왜 HTMX + API 패턴인가?
**MNG 프로젝트의 표준 아키텍처 패턴입니다.**
- **일관성**: 모든 CRUD 기능이 동일한 패턴 사용
- **성능**: 페이지 전체 리로드 없이 동적 업데이트
- **유지보수성**: Blade 템플릿 + HTMX로 간단한 인터랙션
- **확장성**: API는 HTMX와 독립적으로 사용 가능
### 1.2 기본 원칙
1. **Blade View는 화면만 담당** - 데이터 처리 로직 없음
2. **API Controller는 HTMX와 JSON 모두 지원**
3. **HTMX 요청 시 HTML partial 반환**
4. **일반 요청 시 JSON 반환**
---
## 2. 아키텍처 구조
### 2.1 전체 흐름도
```
[Browser]
↓ (HTMX Request with HX-Request header)
[Route: web.php]
↓ (Blade View 반환)
[Controller: RoleController]
↓ (view('roles.index') - 화면만)
[Blade View: roles/index.blade.php]
↓ (hx-get="/api/admin/roles")
[API Route: api.php]
↓ (API 엔드포인트)
[Api\Admin\RoleController]
↓ (Service 호출)
[RoleService]
↓ (비즈니스 로직)
[Database]
↓
[RoleService]
↓ (데이터 반환)
[Api\Admin\RoleController]
↓ (HTMX 요청 감지: HX-Request header)
↓ (HTML partial 렌더링)
[Blade Partial: roles/partials/table.blade.php]
↓ (JSON with html)
[Browser - HTMX]
↓ (DOM 업데이트: #role-table)
[User sees updated table]
```
### 2.2 컨트롤러 분리
#### Blade Controller (화면 전용)
```php
// app/Http/Controllers/RoleController.php
class RoleController extends Controller
{
public function index(): View
{
return view('roles.index'); // 화면만 반환
}
public function create(): View
{
return view('roles.create');
}
public function edit(int $id): View
{
$role = $this->roleService->getRoleById($id);
return view('roles.edit', compact('role'));
}
}
```
#### API Controller (데이터 처리)
```php
// app/Http/Controllers/Api/Admin/RoleController.php
class RoleController extends Controller
{
public function index(Request $request): JsonResponse
{
$roles = $this->roleService->getRoles($request->all());
// HTMX 요청 감지
if ($request->header('HX-Request')) {
$html = view('roles.partials.table', compact('roles'))->render();
return response()->json(['html' => $html]);
}
// 일반 API 요청
return response()->json([
'success' => true,
'data' => $roles->items(),
'meta' => [/*...*/],
]);
}
public function store(StoreRoleRequest $request): JsonResponse
{
$role = $this->roleService->createRole($request->validated());
if ($request->header('HX-Request')) {
return response()->json([
'success' => true,
'message' => '역할이 생성되었습니다.',
'redirect' => route('roles.index'),
]);
}
return response()->json([
'success' => true,
'data' => $role,
], 201);
}
public function destroy(Request $request, int $id): JsonResponse
{
$this->roleService->deleteRole($id);
if ($request->header('HX-Request')) {
return response()->json([
'success' => true,
'message' => '역할이 삭제되었습니다.',
'action' => 'remove',
]);
}
return response()->json([
'success' => true,
'message' => '역할이 삭제되었습니다.',
]);
}
}
```
---
## 3. 구현 가이드
### 3.1 Blade View 구조
#### index.blade.php (메인 화면)
```blade
@extends('layouts.app')
@section('content')
@endsection
@push('scripts')
@endpush
```
#### partials/table.blade.php (HTMX 응답용 HTML partial)
```blade
| ID |
이름 |
설명 |
권한 수 |
액션 |
@forelse($roles as $role)
| {{ $role->id }} |
{{ $role->name }} |
{{ $role->description }} |
{{ $role->permissions_count }} |
수정
|
@empty
| 등록된 역할이 없습니다. |
@endforelse
@include('partials.pagination', [
'paginator' => $roles,
'target' => '#role-table',
'includeForm' => '#filterForm'
])
```
### 3.2 라우트 설정
#### web.php (Blade 화면 라우트)
```php
Route::middleware('auth')->group(function () {
Route::prefix('roles')->name('roles.')->group(function () {
Route::get('/', [RoleController::class, 'index'])->name('index');
Route::get('/create', [RoleController::class, 'create'])->name('create');
Route::get('/{id}/edit', [RoleController::class, 'edit'])->name('edit');
});
});
```
#### api.php (API 엔드포인트)
```php
Route::middleware(['web', 'auth'])->prefix('admin')->name('api.admin.')->group(function () {
Route::prefix('roles')->name('roles.')->group(function () {
Route::get('/', [RoleController::class, 'index'])->name('index');
Route::post('/', [RoleController::class, 'store'])->name('store');
Route::get('/{id}', [RoleController::class, 'show'])->name('show');
Route::put('/{id}', [RoleController::class, 'update'])->name('update');
Route::delete('/{id}', [RoleController::class, 'destroy'])->name('destroy');
});
});
```
### 3.3 HTMX 핵심 개념
#### hx-get, hx-post, hx-put, hx-delete
```html
로딩 중...
```
#### hx-trigger
```html
```
#### hx-include
```html
```
#### hx-headers
```html
```
#### hx-target, hx-swap
```html
기본값
엘리먼트 자체 교체
응답 무시
```
---
## 4. 파일 구조
### 4.1 표준 디렉토리 구조
```
mng/
├── app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── RoleController.php # Blade 화면만
│ │ │ └── Api/
│ │ │ └── Admin/
│ │ │ └── RoleController.php # API 로직
│ │ └── Requests/
│ │ ├── StoreRoleRequest.php
│ │ └── UpdateRoleRequest.php
│ ├── Services/
│ │ └── RoleService.php # 비즈니스 로직
│ └── Models/
│ └── Role.php
├── resources/
│ └── views/
│ └── roles/
│ ├── index.blade.php # 메인 화면
│ ├── create.blade.php # 생성 화면
│ ├── edit.blade.php # 수정 화면
│ └── partials/
│ ├── table.blade.php # HTMX 응답 HTML
│ └── detail.blade.php # (선택사항)
└── routes/
├── web.php # Blade 화면 라우트
└── api.php # API 엔드포인트
```
### 4.2 Tenant 패턴 참고 파일
**학습 및 복사 기준:**
- `app/Http/Controllers/TenantController.php` → Blade 컨트롤러 패턴
- `app/Http/Controllers/Api/Admin/TenantController.php` → API 컨트롤러 패턴
- `resources/views/tenants/index.blade.php` → HTMX 메인 화면 패턴
- `resources/views/tenants/partials/table.blade.php` → HTML partial 패턴
---
## 5. 체크리스트
### 5.1 구현 전 확인사항
- [ ] **Tenant 패턴 파일 확인**: `tenants/` 디렉토리 구조 참고
- [ ] **Service 작성 완료**: `RoleService.php` 비즈니스 로직 구현
- [ ] **FormRequest 작성 완료**: `StoreRoleRequest`, `UpdateRoleRequest`
- [ ] **Model 확인**: `Role.php` 관계 설정 확인
### 5.2 컨트롤러 체크리스트
**Blade Controller (`app/Http/Controllers/RoleController.php`)**
- [ ] `index()` → `view('roles.index')` 반환만
- [ ] `create()` → `view('roles.create')` 반환만
- [ ] `edit($id)` → Service로 데이터 조회 → `view('roles.edit', compact('role'))`
**API Controller (`app/Http/Controllers/Api/Admin/RoleController.php`)**
- [ ] `index()` → HTMX 요청 감지 (`$request->header('HX-Request')`)
- [ ] HTMX 요청 시 → `view('roles.partials.table')->render()` → JSON 반환
- [ ] 일반 요청 시 → JSON 데이터 반환
- [ ] `store()`, `update()`, `destroy()` → HTMX 지원
- [ ] HTMX 응답 시 `redirect` 또는 `action` 포함
### 5.3 Blade View 체크리스트
**index.blade.php**
- [ ] `@extends('layouts.app')` 상속
- [ ] 필터 폼 `