Files
sam-api/routes/api/v1/boards.php
권혁성 a96499a66d feat: API 라우터 분리 및 버전 폴백 시스템 구현
- api.php를 13개 도메인별 파일로 분리 (1,479줄 → 61줄)
- ApiVersionMiddleware 생성 (헤더/쿼리 기반 버전 선택)
- v2 요청 시 v2 없으면 v1으로 자동 폴백
- 지원 헤더: Accept-Version, X-API-Version, api_version 쿼리

분리된 도메인:
auth, admin, users, tenants, hr, finance, sales,
inventory, production, design, files, boards, common

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:30:19 +09:00

171 lines
12 KiB
PHP

<?php
/**
* 게시판 API 라우트 (v1)
*
* - 시스템 게시판
* - 테넌트 게시판
* - 게시글 관리
* - 댓글 관리
* - 품목기준관리
*/
use App\Http\Controllers\Api\V1\BoardController;
use App\Http\Controllers\Api\V1\ItemMaster\CustomTabController;
use App\Http\Controllers\Api\V1\ItemMaster\EntityRelationshipController;
use App\Http\Controllers\Api\V1\ItemMaster\ItemBomItemController;
use App\Http\Controllers\Api\V1\ItemMaster\ItemFieldController;
use App\Http\Controllers\Api\V1\ItemMaster\ItemMasterController;
use App\Http\Controllers\Api\V1\ItemMaster\ItemPageController;
use App\Http\Controllers\Api\V1\ItemMaster\ItemSectionController;
use App\Http\Controllers\Api\V1\ItemMaster\SectionTemplateController;
use App\Http\Controllers\Api\V1\ItemMaster\UnitOptionController;
use App\Http\Controllers\Api\V1\PostController;
use App\Http\Controllers\Api\V1\SystemBoardController;
use App\Http\Controllers\Api\V1\SystemPostController;
use Illuminate\Support\Facades\Route;
// 시스템 게시판 API (is_system=true, tenant_id=null)
Route::prefix('system-boards')->group(function () {
// 시스템 게시판 목록/상세
Route::get('/', [SystemBoardController::class, 'index'])->name('v1.system-boards.index');
Route::get('/{code}', [SystemBoardController::class, 'show'])->name('v1.system-boards.show');
Route::get('/{code}/fields', [SystemBoardController::class, 'fields'])->name('v1.system-boards.fields');
// 시스템 게시글 API
Route::get('/{code}/posts', [SystemPostController::class, 'index'])->name('v1.system-boards.posts.index');
Route::post('/{code}/posts', [SystemPostController::class, 'store'])->name('v1.system-boards.posts.store');
Route::get('/{code}/posts/{id}', [SystemPostController::class, 'show'])->name('v1.system-boards.posts.show');
Route::put('/{code}/posts/{id}', [SystemPostController::class, 'update'])->name('v1.system-boards.posts.update');
Route::delete('/{code}/posts/{id}', [SystemPostController::class, 'destroy'])->name('v1.system-boards.posts.destroy');
// 시스템 댓글 API
Route::get('/{code}/posts/{postId}/comments', [SystemPostController::class, 'comments'])->name('v1.system-boards.posts.comments.index');
Route::post('/{code}/posts/{postId}/comments', [SystemPostController::class, 'storeComment'])->name('v1.system-boards.posts.comments.store');
Route::put('/{code}/posts/{postId}/comments/{commentId}', [SystemPostController::class, 'updateComment'])->name('v1.system-boards.posts.comments.update');
Route::delete('/{code}/posts/{postId}/comments/{commentId}', [SystemPostController::class, 'destroyComment'])->name('v1.system-boards.posts.comments.destroy');
});
// 게시판 관리 API (테넌트용)
Route::prefix('boards')->group(function () {
// 게시판 목록/상세
Route::get('/', [BoardController::class, 'index'])->name('v1.boards.index'); // 접근 가능한 게시판 목록
Route::get('/tenant', [BoardController::class, 'tenantBoards'])->name('v1.boards.tenant'); // 테넌트 게시판만
Route::post('/', [BoardController::class, 'store'])->name('v1.boards.store'); // 테넌트 게시판 생성
Route::get('/{id}', [BoardController::class, 'show'])->whereNumber('id')->name('v1.boards.show'); // 게시판 상세 (ID 기반)
Route::put('/{id}', [BoardController::class, 'update'])->whereNumber('id')->name('v1.boards.update'); // 테넌트 게시판 수정
Route::delete('/{id}', [BoardController::class, 'destroy'])->whereNumber('id')->name('v1.boards.destroy'); // 테넌트 게시판 삭제
Route::get('/{code}/fields', [BoardController::class, 'fields'])->name('v1.boards.fields'); // 게시판 필드 목록
// 게시글 API
Route::get('/{code}/posts', [PostController::class, 'index'])->name('v1.boards.posts.index'); // 게시글 목록
Route::post('/{code}/posts', [PostController::class, 'store'])->name('v1.boards.posts.store'); // 게시글 작성
Route::get('/{code}/posts/{id}', [PostController::class, 'show'])->name('v1.boards.posts.show'); // 게시글 상세
Route::put('/{code}/posts/{id}', [PostController::class, 'update'])->name('v1.boards.posts.update'); // 게시글 수정
Route::delete('/{code}/posts/{id}', [PostController::class, 'destroy'])->name('v1.boards.posts.destroy'); // 게시글 삭제
// 댓글 API
Route::get('/{code}/posts/{postId}/comments', [PostController::class, 'comments'])->name('v1.boards.posts.comments.index'); // 댓글 목록
Route::post('/{code}/posts/{postId}/comments', [PostController::class, 'storeComment'])->name('v1.boards.posts.comments.store'); // 댓글 작성
Route::put('/{code}/posts/{postId}/comments/{commentId}', [PostController::class, 'updateComment'])->name('v1.boards.posts.comments.update'); // 댓글 수정
Route::delete('/{code}/posts/{postId}/comments/{commentId}', [PostController::class, 'destroyComment'])->name('v1.boards.posts.comments.destroy'); // 댓글 삭제
// 게시판 상세 (코드 기반) - 가장 마지막에 배치 (catch-all)
Route::get('/{code}', [BoardController::class, 'showByCode'])->name('v1.boards.show_by_code');
});
// 게시글 API (사용자 중심)
Route::prefix('posts')->group(function () {
Route::get('/my', [PostController::class, 'myPosts'])->name('v1.posts.my'); // 나의 게시글 목록
});
// 품목기준관리 (ItemMaster) API
Route::prefix('item-master')->group(function () {
// 초기화
Route::get('/init', [ItemMasterController::class, 'init'])->name('v1.item-master.init');
// 페이지 관리
Route::get('/pages', [ItemPageController::class, 'index'])->name('v1.item-master.pages.index');
Route::post('/pages', [ItemPageController::class, 'store'])->name('v1.item-master.pages.store');
Route::put('/pages/{id}', [ItemPageController::class, 'update'])->name('v1.item-master.pages.update');
Route::delete('/pages/{id}', [ItemPageController::class, 'destroy'])->name('v1.item-master.pages.destroy');
// 독립 섹션 관리
Route::get('/sections', [ItemSectionController::class, 'index'])->name('v1.item-master.sections.index');
Route::post('/sections', [ItemSectionController::class, 'storeIndependent'])->name('v1.item-master.sections.store-independent');
Route::post('/sections/{id}/clone', [ItemSectionController::class, 'clone'])->name('v1.item-master.sections.clone');
Route::get('/sections/{id}/usage', [ItemSectionController::class, 'getUsage'])->name('v1.item-master.sections.usage');
// 섹션 관리 (페이지 연결)
Route::post('/pages/{pageId}/sections', [ItemSectionController::class, 'store'])->name('v1.item-master.sections.store');
Route::put('/sections/{id}', [ItemSectionController::class, 'update'])->name('v1.item-master.sections.update');
Route::delete('/sections/{id}', [ItemSectionController::class, 'destroy'])->name('v1.item-master.sections.destroy');
Route::put('/pages/{pageId}/sections/reorder', [ItemSectionController::class, 'reorder'])->name('v1.item-master.sections.reorder');
// 독립 필드 관리
Route::get('/fields', [ItemFieldController::class, 'index'])->name('v1.item-master.fields.index');
Route::post('/fields', [ItemFieldController::class, 'storeIndependent'])->name('v1.item-master.fields.store-independent');
Route::post('/fields/{id}/clone', [ItemFieldController::class, 'clone'])->name('v1.item-master.fields.clone');
Route::get('/fields/{id}/usage', [ItemFieldController::class, 'getUsage'])->name('v1.item-master.fields.usage');
// 필드 관리 (섹션 연결)
Route::post('/sections/{sectionId}/fields', [ItemFieldController::class, 'store'])->name('v1.item-master.fields.store');
Route::put('/fields/{id}', [ItemFieldController::class, 'update'])->name('v1.item-master.fields.update');
Route::delete('/fields/{id}', [ItemFieldController::class, 'destroy'])->name('v1.item-master.fields.destroy');
Route::put('/sections/{sectionId}/fields/reorder', [ItemFieldController::class, 'reorder'])->name('v1.item-master.fields.reorder');
// 독립 BOM 항목 관리
Route::get('/bom-items', [ItemBomItemController::class, 'index'])->name('v1.item-master.bom-items.index');
Route::post('/bom-items', [ItemBomItemController::class, 'storeIndependent'])->name('v1.item-master.bom-items.store-independent');
// BOM 항목 관리 (섹션 연결)
Route::post('/sections/{sectionId}/bom-items', [ItemBomItemController::class, 'store'])->name('v1.item-master.bom-items.store');
Route::put('/bom-items/{id}', [ItemBomItemController::class, 'update'])->name('v1.item-master.bom-items.update');
Route::delete('/bom-items/{id}', [ItemBomItemController::class, 'destroy'])->name('v1.item-master.bom-items.destroy');
// 섹션 템플릿
Route::get('/section-templates', [SectionTemplateController::class, 'index'])->name('v1.item-master.section-templates.index');
Route::post('/section-templates', [SectionTemplateController::class, 'store'])->name('v1.item-master.section-templates.store');
Route::put('/section-templates/{id}', [SectionTemplateController::class, 'update'])->name('v1.item-master.section-templates.update');
Route::delete('/section-templates/{id}', [SectionTemplateController::class, 'destroy'])->name('v1.item-master.section-templates.destroy');
// 커스텀 탭
Route::get('/custom-tabs', [CustomTabController::class, 'index'])->name('v1.item-master.custom-tabs.index');
Route::post('/custom-tabs', [CustomTabController::class, 'store'])->name('v1.item-master.custom-tabs.store');
Route::put('/custom-tabs/reorder', [CustomTabController::class, 'reorder'])->name('v1.item-master.custom-tabs.reorder');
Route::put('/custom-tabs/{id}', [CustomTabController::class, 'update'])->name('v1.item-master.custom-tabs.update');
Route::delete('/custom-tabs/{id}', [CustomTabController::class, 'destroy'])->name('v1.item-master.custom-tabs.destroy');
// 단위 옵션
Route::get('/unit-options', [UnitOptionController::class, 'index'])->name('v1.item-master.unit-options.index');
Route::post('/unit-options', [UnitOptionController::class, 'store'])->name('v1.item-master.unit-options.store');
Route::delete('/unit-options/{id}', [UnitOptionController::class, 'destroy'])->name('v1.item-master.unit-options.destroy');
// 엔티티 관계 관리 (독립 엔티티 + 링크 테이블)
// 페이지-섹션 연결
Route::post('/pages/{pageId}/link-section', [EntityRelationshipController::class, 'linkSectionToPage'])->name('v1.item-master.pages.link-section');
Route::delete('/pages/{pageId}/unlink-section/{sectionId}', [EntityRelationshipController::class, 'unlinkSectionFromPage'])->name('v1.item-master.pages.unlink-section');
// 페이지-필드 직접 연결
Route::post('/pages/{pageId}/link-field', [EntityRelationshipController::class, 'linkFieldToPage'])->name('v1.item-master.pages.link-field');
Route::delete('/pages/{pageId}/unlink-field/{fieldId}', [EntityRelationshipController::class, 'unlinkFieldFromPage'])->name('v1.item-master.pages.unlink-field');
// 페이지 관계 조회
Route::get('/pages/{pageId}/relationships', [EntityRelationshipController::class, 'getPageRelationships'])->name('v1.item-master.pages.relationships');
Route::get('/pages/{pageId}/structure', [EntityRelationshipController::class, 'getPageStructure'])->name('v1.item-master.pages.structure');
// 섹션-필드 연결
Route::post('/sections/{sectionId}/link-field', [EntityRelationshipController::class, 'linkFieldToSection'])->name('v1.item-master.sections.link-field');
Route::delete('/sections/{sectionId}/unlink-field/{fieldId}', [EntityRelationshipController::class, 'unlinkFieldFromSection'])->name('v1.item-master.sections.unlink-field');
// 섹션-BOM 연결
Route::post('/sections/{sectionId}/link-bom', [EntityRelationshipController::class, 'linkBomToSection'])->name('v1.item-master.sections.link-bom');
Route::delete('/sections/{sectionId}/unlink-bom/{bomId}', [EntityRelationshipController::class, 'unlinkBomFromSection'])->name('v1.item-master.sections.unlink-bom');
// 섹션 관계 조회
Route::get('/sections/{sectionId}/relationships', [EntityRelationshipController::class, 'getSectionRelationships'])->name('v1.item-master.sections.relationships');
// 관계 순서 변경
Route::post('/relationships/reorder', [EntityRelationshipController::class, 'reorderRelationships'])->name('v1.item-master.relationships.reorder');
});