fix(board): 게시판 URL tenant_id 처리 및 게시글 수 표시 추가

- Route Model Binding → 수동 조회로 변경 (board_code + tenant_id)
- PostController: resolveBoard() 헬퍼 추가
  - t 파라미터 → 시스템 게시판 → 로그인 회원 tenant 순서
- 사이드바 메뉴 리다이렉트: tenant_id ?? 1 fallback 추가
  - SidebarMenuService와 동일한 로직으로 일관성 확보
- 게시판 목록 테이블에 게시글 수 컬럼 추가
- 모든 posts View에 tenant_id 파라미터 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-28 01:30:50 +09:00
parent 5b8ecf02ab
commit a30410643b
13 changed files with 192 additions and 70 deletions

View File

@@ -18,11 +18,48 @@ public function __construct(
private readonly PostService $postService
) {}
/**
* board_code + tenant_id로 게시판 조회
* - t 파라미터가 있으면 해당 테넌트의 게시판
* - 없으면 시스템 게시판 우선, 그 다음 로그인 회원의 테넌트 게시판
*/
private function resolveBoard(string $boardCode, Request $request): Board
{
$tenantId = $request->query('t');
// t 파라미터가 있으면 해당 테넌트의 게시판 조회
if ($tenantId) {
return Board::where('board_code', $boardCode)
->where('tenant_id', $tenantId)
->where('is_active', true)
->firstOrFail();
}
// t 파라미터가 없으면: 시스템 게시판 우선, 그 다음 로그인 회원의 테넌트 게시판
$board = Board::where('board_code', $boardCode)
->whereNull('tenant_id') // 시스템 게시판
->where('is_active', true)
->first();
if ($board) {
return $board;
}
// 시스템 게시판이 없으면 로그인 회원의 테넌트 게시판
$userTenantId = auth()->user()?->tenant_id;
return Board::where('board_code', $boardCode)
->where('tenant_id', $userTenantId)
->where('is_active', true)
->firstOrFail();
}
/**
* 게시글 목록
*/
public function index(Board $board, Request $request): View
public function index(string $boardCode, Request $request): View
{
$board = $this->resolveBoard($boardCode, $request);
$filters = $request->only(['search', 'is_notice']);
$posts = $this->postService->getPosts($board->id, $filters, 15);
$notices = $this->postService->getNotices($board->id, 5);
@@ -34,8 +71,9 @@ public function index(Board $board, Request $request): View
/**
* 게시글 작성 폼
*/
public function create(Board $board): View
public function create(string $boardCode, Request $request): View
{
$board = $this->resolveBoard($boardCode, $request);
$fields = $board->fields;
return view('posts.create', compact('board', 'fields'));
@@ -44,8 +82,9 @@ public function create(Board $board): View
/**
* 게시글 저장
*/
public function store(Board $board, Request $request): RedirectResponse
public function store(string $boardCode, Request $request): RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'nullable|string',
@@ -79,21 +118,23 @@ public function store(Board $board, Request $request): RedirectResponse
} catch (\Exception $e) {
// 파일 업로드 실패해도 게시글은 저장됨 - 경고 메시지만 표시
return redirect()
->route('boards.posts.show', [$board, $post])
->route('boards.posts.show', [$board->board_code, $post, 't' => $board->tenant_id])
->with('warning', '게시글이 작성되었으나 일부 파일 업로드에 실패했습니다: '.$e->getMessage());
}
}
return redirect()
->route('boards.posts.show', [$board, $post])
->route('boards.posts.show', [$board->board_code, $post, 't' => $board->tenant_id])
->with('success', '게시글이 작성되었습니다.');
}
/**
* 게시글 상세
*/
public function show(Board $board, Post $post): View|RedirectResponse
public function show(string $boardCode, Post $post, Request $request): View|RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -119,8 +160,10 @@ public function show(Board $board, Post $post): View|RedirectResponse
/**
* 게시글 수정 폼
*/
public function edit(Board $board, Post $post): View|RedirectResponse
public function edit(string $boardCode, Post $post, Request $request): View|RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -140,8 +183,10 @@ public function edit(Board $board, Post $post): View|RedirectResponse
/**
* 게시글 수정 저장
*/
public function update(Board $board, Post $post, Request $request): RedirectResponse
public function update(string $boardCode, Post $post, Request $request): RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -184,21 +229,23 @@ public function update(Board $board, Post $post, Request $request): RedirectResp
$this->postService->uploadFiles($post, $request->file('files'));
} catch (\Exception $e) {
return redirect()
->route('boards.posts.show', [$board, $post])
->route('boards.posts.show', [$board->board_code, $post, 't' => $board->tenant_id])
->with('warning', '게시글이 수정되었으나 일부 파일 업로드에 실패했습니다: '.$e->getMessage());
}
}
return redirect()
->route('boards.posts.show', [$board, $post])
->route('boards.posts.show', [$board->board_code, $post, 't' => $board->tenant_id])
->with('success', '게시글이 수정되었습니다.');
}
/**
* 게시글 삭제
*/
public function destroy(Board $board, Post $post): RedirectResponse
public function destroy(string $boardCode, Post $post, Request $request): RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -215,7 +262,7 @@ public function destroy(Board $board, Post $post): RedirectResponse
$this->postService->deletePost($post);
return redirect()
->route('boards.posts.index', $board)
->route('boards.posts.index', [$board->board_code, 't' => $board->tenant_id])
->with('success', '게시글이 삭제되었습니다.');
}
@@ -226,8 +273,10 @@ public function destroy(Board $board, Post $post): RedirectResponse
/**
* 파일 업로드 (AJAX)
*/
public function uploadFiles(Board $board, Post $post, Request $request): JsonResponse
public function uploadFiles(string $boardCode, Post $post, Request $request): JsonResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
return response()->json(['success' => false, 'message' => '게시글을 찾을 수 없습니다.'], 404);
@@ -269,8 +318,10 @@ public function uploadFiles(Board $board, Post $post, Request $request): JsonRes
/**
* 파일 다운로드
*/
public function downloadFile(Board $board, Post $post, int $fileId): BinaryFileResponse|StreamedResponse|RedirectResponse
public function downloadFile(string $boardCode, Post $post, int $fileId, Request $request): BinaryFileResponse|StreamedResponse|RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -287,8 +338,10 @@ public function downloadFile(Board $board, Post $post, int $fileId): BinaryFileR
/**
* 파일 미리보기 (이미지 인라인 표시)
*/
public function previewFile(Board $board, Post $post, int $fileId): BinaryFileResponse|StreamedResponse|RedirectResponse
public function previewFile(string $boardCode, Post $post, int $fileId, Request $request): BinaryFileResponse|StreamedResponse|RedirectResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
abort(404);
@@ -305,8 +358,10 @@ public function previewFile(Board $board, Post $post, int $fileId): BinaryFileRe
/**
* 파일 삭제 (AJAX)
*/
public function deleteFile(Board $board, Post $post, int $fileId): JsonResponse
public function deleteFile(string $boardCode, Post $post, int $fileId, Request $request): JsonResponse
{
$board = $this->resolveBoard($boardCode, $request);
// 게시판 일치 확인
if ($post->board_id !== $board->id) {
return response()->json(['success' => false, 'message' => '게시글을 찾을 수 없습니다.'], 404);