Skip to content

[FE] 20260303 #265 기능개선게시물 게시물 페이지 디테일 개선#285

Merged
discipline24 merged 7 commits intomainfrom
20260303_#265-기능개선게시물-게시물-페이지-디테일-개선
Mar 7, 2026

Hidden character warning

The head ref may contain hidden characters: "20260303_#265-\uae30\ub2a5\uac1c\uc120\uac8c\uc2dc\ubb3c-\uac8c\uc2dc\ubb3c-\ud398\uc774\uc9c0-\ub514\ud14c\uc77c-\uac1c\uc120"
Merged

[FE] 20260303 #265 기능개선게시물 게시물 페이지 디테일 개선#285
discipline24 merged 7 commits intomainfrom
20260303_#265-기능개선게시물-게시물-페이지-디테일-개선

Conversation

@sangkyu39
Copy link
Contributor

@sangkyu39 sangkyu39 commented Mar 7, 2026

사이드바 게시판 메뉴바 수정
게시판 api 연결
게시글 보기 및 댓글, 파일 저장 등

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 드래그 앤 드롭 파일 업로드 지원 추가
    • 게시판 선택 토글형 메뉴(드롭다운) 추가
    • 게시글 상세에서 게시판으로 이동하는 버튼 추가
    • 권한 기반 하위 게시판 생성 기능 추가
    • 게시판별 탭 필터(전체 탭 포함) 추가
  • 개선사항

    • 파일 첨부 UI·인터랙션 및 키보드 접근성 개선
    • 게시판 결과 개수 표시 추가
    • 첨부 파일 목록을 클릭 가능하게 변경 (다운로드/접근성 향상)

Copilot AI review requested due to automatic review settings March 7, 2026 06:59
@coderabbitai
Copy link

coderabbitai bot commented Mar 7, 2026

Walkthrough

게시판 관련 UI/라우팅을 재구성하고, 하위 게시판 생성/선택, 드래그앤드롭 파일 업로드 및 키보드 접근성, 게시판 간 이동 기능과 관련 유틸/API를 추가/수정했습니다.

Changes

Cohort / File(s) Summary
BoardActions 컴포넌트
frontend/src/components/Board/BoardActions.jsx, frontend/src/components/Board/BoardActions.module.css
검색 결과 수를 표시하는 resultCount prop 추가 및 컨테이너 정렬 방향(오른쪽→왼쪽) 변경.
CategoryTabs 컴포넌트
frontend/src/components/Board/CategoryTabs.jsx, frontend/src/components/Board/CategoryTabs.module.css
canCreateSubBoard prop 추가로 하위 게시판 생성 버튼을 조건부 렌더링하고 비활성화 스타일 추가.
Modal 컴포넌트
frontend/src/components/Board/Modal.jsx, frontend/src/components/Board/Modal.module.css
파일 드래그앤드롭, 숨김 파일 입력, 게시판 선택 UI, isSaving 플래그, 접근성 및 레이아웃 대대적 변경(핵심 로직·스타일 추가).
PostDetail / 편집 관련
frontend/src/components/Board/PostDetail/FileAttachmentList.jsx, frontend/src/components/Board/PostDetail/PostEditForm.jsx, frontend/src/pages/PostDetail.module.css
첨부파일 클릭/키보드 접근성, 드래그앤드롭 기반 파일 추가/관리 도입 및 편집 UI/스타일 개편.
PostView / PostItem / PostDetail 페이지
frontend/src/components/Board/PostDetail/PostView.jsx, frontend/src/components/Board/PostItem.jsx, frontend/src/pages/PostDetail.jsx
게시판 이동 버튼/플로우 추가(boardName, onMoveToBoard), 다운로드/이동 핸들러 강화 및 라우팅 개선.
게시판 페이지 로직
frontend/src/pages/Board.jsx
부모·하위 게시판 목록 로드, 탭 기반 필터링, 게시글 병합/페이징 재구성, 권한 기반 하위 게시판 생성 플래그 추가 및 관련 흐름 확장.
Sidebar 네비게이션
frontend/src/components/Sidebar.jsx, frontend/src/components/Sidebar.module.css
동적 게시판 목록 로드와 토글식 메뉴 도입, 관리자 표시 로직 변경, 새로운 메뉴/아이템 스타일 및 상태 처리 추가.
유틸리티 및 API 변경
frontend/src/utils/boardApi.js, frontend/src/utils/boardRoute.js, frontend/src/index.css
하위 게시판 생성 API 함수 추가(createSubBoard) 및 호환 래퍼, 한글 게시판명을 라우트 세그먼트로 변환하는 유틸 추가, 전역 배경색 강제 스타일 적용.

Sequence Diagram(s)

sequenceDiagram
    participant User as "사용자"
    participant UI as "프론트엔드 UI\n(Modal / Sidebar / Board)"
    participant Router as "라우터\n(navigation)"
    participant API as "API 클라이언트\n(boardApi)"
    participant Server as "서버"

    rect rgba(200,200,255,0.5)
    User->>UI: 게시글 작성 모달 열기\n(게시판 선택, 파일 드래그)
    UI->>UI: 파일 드래그/키보드 처리
    UI->>API: 업로드 요청 (선택된 boardId, 파일들)
    API->>Server: POST /api/... (파일 + 메타)
    Server-->>API: 업로드 응답 (boardId, postId)
    API-->>UI: 저장 결과 반환
    UI->>Router: 게시판/게시글로 이동 또는 목록 갱신
    Router-->>User: 업데이트된 화면 표시
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • DongEun02
  • gxuoo

Poem

🐇 새 길 위에 폴짝 폴짝,
파일은 드래그로 춤을 추고,
탭은 늘어나며 길을 나누네,
사이드바 문이 톡 열릴 때,
나는 토끼, 변화에 깡총 기뻐하네 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 게시물 페이지 디테일 개선이라는 주요 변경사항을 명확하게 설명하고 있으며, 변경 목록과 일관성이 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 20260303_#265-기능개선게시물-게시물-페이지-디테일-개선

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

게시판/게시글 상세 화면 UX를 개선하고(게시판 이동, 첨부파일 다운로드/드래그앤드롭), 사이드바의 게시판 목록을 하드코딩 대신 API 기반으로 동적으로 구성하며, 게시판/하위게시판/게시글 작성 흐름을 보완하는 FE 변경입니다.

Changes:

  • 게시판 이름 ↔ 라우트 세그먼트 변환 유틸(boardRoute) 도입 및 Sidebar/Board/PostItem에서 공통 사용
  • Board 페이지에서 하위 게시판 탭/검색/작성(세션 선택) 로직을 정리하고 권한 기반 하위게시판 생성 노출 추가
  • PostDetail에서 게시판 이동 링크 및 첨부파일 다운로드/편집 UI(드래그앤드롭 포함) 개선

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
frontend/src/utils/boardRoute.js 게시판명→URL 세그먼트/경로 변환 로직을 중앙화
frontend/src/utils/boardApi.js 하위 게시판 생성 엔드포인트를 /api/admin/board로 정리하고 호환 함수 추가
frontend/src/pages/Board.jsx 게시판/하위게시판 로딩, 다중 boardId 조회/검색, 글작성 세션 선택 및 권한 처리
frontend/src/components/Sidebar.jsx 게시판 목록을 API 기반으로 구성하고 메뉴 UI를 토글 리스트 형태로 변경
frontend/src/components/Board/PostItem.jsx 게시글 카드에서 작성자/시간/카운트 표시 및 상세 이동 시 origin 정보 전달
frontend/src/pages/PostDetail.jsx 게시판 이동 및 첨부파일 다운로드 URL 생성 방식 변경
frontend/src/components/Board/PostDetail/* 상세 보기/수정/첨부파일 리스트의 UI/인터랙션 개선(클릭 다운로드 등)
frontend/src/components/Board/Modal.jsx 글 작성 모달에 세션 선택 + 드래그앤드롭 업로드 UX 추가 및 저장중 상태 반영
*.module.css / frontend/src/index.css 신규 UI에 맞춘 스타일 추가/조정

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (6)
frontend/src/components/Board/Modal.module.css (1)

275-289: 파일 삭제 버튼에 focus-visible 상태도 추가해 주세요.

새로 추가된 인터랙션인데 hover만 있고 키보드 포커스 표시가 없습니다. 파일 제거 버튼은 반복 렌더링되는 컨트롤이라 포커스 링이 없으면 탐색성이 꽤 떨어집니다.

접근성 보완 예시
 .fileRemoveButton:hover {
   color: rgba(231, 76, 60, 1);
 }
+
+.fileRemoveButton:focus-visible {
+  outline: 2px solid rgba(29, 128, 244, 1);
+  outline-offset: 2px;
+  border-radius: 4px;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Board/Modal.module.css` around lines 275 - 289, Add
keyboard focus styling for the file removal control by updating the
.fileRemoveButton rule set to include a :focus-visible state (in
Modal.module.css) so keyboard users see a visible focus ring; specifically add a
.fileRemoveButton:focus-visible selector that applies a clear focus indicator
(e.g., outline or box-shadow with sufficient contrast and non-zero offset) and
ensure it does not conflict with the existing :hover styling or remove default
focus unintentionally for the .fileRemoveButton element.
frontend/src/pages/Board.jsx (1)

114-126: 역할 확인 useEffect에서 에러 무시

Sidebar.jsx와 동일하게 catch 블록에서 에러를 무시합니다. 일관성은 유지되지만, 최소한의 로깅을 추가하면 디버깅에 도움이 됩니다.

💡 에러 로깅 추가 제안
       } catch (error) {
+        console.warn('역할 확인 실패:', error);
         setCanCreateSubBoard(false);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/Board.jsx` around lines 114 - 126, The catch block in the
useEffect's fetchRole swallows errors silently; update the fetchRole async
function to log the caught error before setting setCanCreateSubBoard(false) so
failures from api.get('/api/user/details') are visible; reference the fetchRole
function, the catch block, setCanCreateSubBoard, SUB_BOARD_ADMIN_ROLES and the
api.get('/api/user/details') call and ensure the log provides the error
object/message (use existing logger or console.error if none).
frontend/src/components/Board/Modal.jsx (1)

29-57: 드래그 이벤트 핸들러 중복 호출 가능성

contentContainer와 내부 textarea에 동일한 드래그 이벤트 핸들러가 연결되어 있습니다 (Lines 137-140, 153-156). 이벤트 버블링으로 인해 textarea에서 발생한 이벤트가 contentContainer로 전파되면서 핸들러가 두 번 호출될 수 있습니다.

e.stopPropagation()이 각 핸들러에 있어 중복 호출은 방지되지만, textarea의 드래그 핸들러는 불필요할 수 있습니다. 이벤트 버블링을 활용하여 컨테이너에서만 처리하는 것이 더 깔끔합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Board/Modal.jsx` around lines 29 - 57, The container
and the inner textarea both register the same drag handlers (handleDragEnter,
handleDragOver, handleDragLeave, handleDrop), causing duplicate handling via
bubbling; remove the drag event props from the textarea and keep them only on
the contentContainer so all drag events are handled at the container level.
Ensure the container's handleDrop still builds droppedFiles and calls
onFileChange({ target: { files: droppedFiles } }) and that clearDragState is
used in handleDragLeave/handleDrop so visual state is preserved; no other
handler changes are needed.
frontend/src/components/Board/PostDetail/PostEditForm.jsx (1)

25-54: Modal.jsx와 드래그 로직 중복

handleDragEnter, handleDragOver, handleDragLeave, handleDrop 로직이 Modal.jsx와 거의 동일합니다. 커스텀 훅 또는 공통 컴포넌트로 추출하면 유지보수성이 향상됩니다.

♻️ 커스텀 훅 추출 예시
// hooks/useDragAndDrop.js
import { useState } from 'react';

export const useDragAndDrop = (onFileDrop) => {
  const [isDragOver, setIsDragOver] = useState(false);

  const handlers = {
    onDragEnter: (e) => {
      e.preventDefault();
      e.stopPropagation();
      setIsDragOver(true);
    },
    onDragOver: (e) => {
      e.preventDefault();
      e.stopPropagation();
      setIsDragOver(true);
    },
    onDragLeave: (e) => {
      e.preventDefault();
      e.stopPropagation();
      if (!e.currentTarget.contains(e.relatedTarget)) {
        setIsDragOver(false);
      }
    },
    onDrop: (e) => {
      e.preventDefault();
      e.stopPropagation();
      setIsDragOver(false);
      const files = Array.from(e.dataTransfer.files || []);
      if (files.length > 0) onFileDrop(files);
    },
  };

  return { isDragOver, handlers };
};

Also applies to: 107-116

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Board/PostDetail/PostEditForm.jsx` around lines 25 -
54, Extract the duplicated drag-and-drop logic into a reusable hook (e.g.,
useDragAndDrop) and replace the local handlers in PostEditForm.jsx
(handleDragEnter, handleDragOver, handleDragLeave, handleDrop) and the identical
handlers in Modal.jsx with the hook's returned handlers and isDragOver state;
the hook should accept a callback (onFileDrop) that receives an array of File
objects and internally implement the same prevention, setIsDragOver behavior,
relatedTarget check, and conversion of e.dataTransfer.files to an array before
calling onFileDrop so you can call onAddNewFile({ target: { files: filesArray }
}) or adapt callers to accept the files array.
frontend/src/components/Sidebar.jsx (1)

65-82: 관리자 권한 확인 시 에러 무시

catch 블록에서 에러를 무시하고 canSeeAdminMenu를 false로 설정합니다. 네트워크 오류와 권한 없음을 구분하지 않아, 일시적 네트워크 문제로 관리자 메뉴가 숨겨질 수 있습니다.

현재 동작이 의도된 것이라면 괜찮지만, 디버깅을 위해 최소한의 로깅을 추가하는 것을 권장합니다.

💡 에러 로깅 추가 제안
       } catch (error) {
+        console.warn('관리자 권한 확인 실패:', error);
         setCanSeeAdminMenu(false);
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Sidebar.jsx` around lines 65 - 82, checkAdminRole's
catch block currently swallows errors and forces setCanSeeAdminMenu(false);
update the catch in Sidebar.jsx (inside the checkAdminRole async function that
calls api.get('/api/user/details')) to log the caught error (include the error
object/message and context like "Failed to fetch user details for admin check")
before setting setCanSeeAdminMenu(false); use the app's logging utility if
available or console.error, and include references to ADMIN_VISIBLE_ROLES and
setCanSeeAdminMenu so the log helps debug network vs permission issues.
frontend/src/components/Sidebar.module.css (1)

168-188: 키보드 포커스 스타일도 같이 정의해 주세요.

새로 추가된 인터랙티브 요소들이 :hover 중심이라, 키보드 탐색 시 포커스가 배경에 묻혀 보일 수 있습니다. :focus-visible을 명시해 두면 접근성이 더 안정적입니다.

🎯 예시
+.menuTitleToggle:focus-visible,
+.boardMenuTrigger:focus-visible,
+.boardMenuItem:focus-visible {
+  outline: 2px solid `#4d8dff`;
+  outline-offset: 2px;
+}

Also applies to: 213-225, 263-279

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Sidebar.module.css` around lines 168 - 188, Add an
accessible keyboard focus style for the interactive class .menuTitleToggle (and
the other similar toggles mentioned) by defining a :focus-visible rule that
makes the element's focus state visually distinct from the background (e.g.,
visible outline or box-shadow and preserved background contrast), so keyboard
users can clearly see focus when tabbing; update the CSS by adding a
.menuTitleToggle:focus-visible selector (and the corresponding selectors for the
other toggle classes at the other ranges) that sets a clear outline/box-shadow,
ensures outline-offset or border-radius matches the element, and does not rely
solely on :hover styles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/Board/BoardActions.jsx`:
- Line 9: The label "검색결과" is shown unconditionally because the component uses
sortedPosts.length (resultCount) regardless of search state; update BoardActions
(styles.resultCount / resultCount) to branch on the actual search flag or query
prop (e.g., isSearching or searchQuery) and render "{resultCount}건의 검색결과" only
when a search is active, otherwise render a neutral label like "{resultCount}건"
(or "전체 {resultCount}건") so the text matches the current state.

In `@frontend/src/components/Board/PostDetail/FileAttachmentList.jsx`:
- Around line 43-48: The attachment container element (attachmentItem div) and
the inner img both call handleDownload(file), which can cause duplicate
downloads via event bubbling; remove the onClick from the img (leave the
container's onClick) or, if keeping the img handler, call e.stopPropagation()
inside the img click handler to prevent propagation. Locate the img element
rendering FolderIcon in FileAttachmentList.jsx and update its onClick
accordingly so only a single handleDownload(file) invocation occurs (references:
attachmentItem div, img, handleDownload, isEditMode).

In `@frontend/src/components/Board/PostDetail/PostView.jsx`:
- Around line 28-36: The button renders when boardName is truthy but calls
onMoveToBoard without guarding against it being undefined; update the click
handler used in the PostView component so it safely invokes onMoveToBoard (e.g.,
use optional chaining or a safe noop) in the onClick for the element with
className styles.boardLink so clicking cannot throw if onMoveToBoard is not
provided.

In `@frontend/src/components/Sidebar.module.css`:
- Around line 276-284: The active menu item's hover is being overridden by
.boardMenuItem:hover so the active highlight disappears; add a more specific CSS
rule to preserve the active styles on hover (e.g., a .boardMenuItemActive:hover
or a combined selector like .boardMenuItem.boardMenuItemActive:hover) that sets
color: `#ffffff` and background: transparent to ensure the active state remains
visually identical when hovered.

In `@frontend/src/index.css`:
- Line 13: Remove the global "!important" on the background-color declarations
in index.css (the body background entries at the two occurrences) so they no
longer override page-specific rules; replace them with a plain background-color
or move the forced rule to a narrower selector such as a layout container (e.g.,
.app-root or .layout-container) if you truly need to enforce a default, and
ensure page-specific selector body { background-color: `#f4f5f8`; } in
QuantTradingDashboard can take effect.

In `@frontend/src/pages/PostDetail.jsx`:
- Around line 12-15: FILE_DOWNLOAD_BASE_URL falls back to an empty string when
VITE_API_URL is unset, producing relative download paths; update the
PostDetail.jsx initialization of FILE_DOWNLOAD_BASE_URL to explicitly warn in
development when import.meta.env.VITE_API_URL is missing (e.g., use
import.meta.env.DEV or process.env.NODE_ENV check) and/or add a clear inline
comment documenting the intended Vite-proxy behavior; reference
FILE_DOWNLOAD_BASE_URL and VITE_API_URL so the change targets that constant and
ensures developers see a console.warn in dev rather than silently using a
relative path.

In `@frontend/src/utils/boardApi.js`:
- Around line 40-53: createSubBoard currently returns response.data but the
backend returns void, so Board.jsx reading created?.boardId always misses;
update createSubBoard to ensure it returns the new board's id: after the POST in
createSubBoard, if response.data.boardId is falsy, call the boards listing
endpoint (e.g., api.get('/api/admin/boards')), find the created board by
matching normalizedBoardName and parentBoardId, and return an object that
includes boardId (or throw if not found); keep the original POST path if the
backend starts returning { boardId } so Board.jsx can use created?.boardId as
expected.

In `@frontend/src/utils/boardRoute.js`:
- Around line 36-45: The helper currently returns arbitrary strings
(withoutSuffix/normalized) when BOARD_SEGMENT_MAP has no match, producing
invalid route segments; change the function in frontend/src/utils/boardRoute.js
so it only returns a valid mapped segment (BOARD_SEGMENT_MAP[withoutSuffix]) and
otherwise returns null (or undefined) to indicate "no routable value" instead of
returning withoutSuffix/normalized; keep references to BOARD_SEGMENT_MAP,
withoutSuffix and normalized so callers (e.g. Board.jsx's boardIdMap lookup) can
handle the null case and avoid creating unreachable routes.

---

Nitpick comments:
In `@frontend/src/components/Board/Modal.jsx`:
- Around line 29-57: The container and the inner textarea both register the same
drag handlers (handleDragEnter, handleDragOver, handleDragLeave, handleDrop),
causing duplicate handling via bubbling; remove the drag event props from the
textarea and keep them only on the contentContainer so all drag events are
handled at the container level. Ensure the container's handleDrop still builds
droppedFiles and calls onFileChange({ target: { files: droppedFiles } }) and
that clearDragState is used in handleDragLeave/handleDrop so visual state is
preserved; no other handler changes are needed.

In `@frontend/src/components/Board/Modal.module.css`:
- Around line 275-289: Add keyboard focus styling for the file removal control
by updating the .fileRemoveButton rule set to include a :focus-visible state (in
Modal.module.css) so keyboard users see a visible focus ring; specifically add a
.fileRemoveButton:focus-visible selector that applies a clear focus indicator
(e.g., outline or box-shadow with sufficient contrast and non-zero offset) and
ensure it does not conflict with the existing :hover styling or remove default
focus unintentionally for the .fileRemoveButton element.

In `@frontend/src/components/Board/PostDetail/PostEditForm.jsx`:
- Around line 25-54: Extract the duplicated drag-and-drop logic into a reusable
hook (e.g., useDragAndDrop) and replace the local handlers in PostEditForm.jsx
(handleDragEnter, handleDragOver, handleDragLeave, handleDrop) and the identical
handlers in Modal.jsx with the hook's returned handlers and isDragOver state;
the hook should accept a callback (onFileDrop) that receives an array of File
objects and internally implement the same prevention, setIsDragOver behavior,
relatedTarget check, and conversion of e.dataTransfer.files to an array before
calling onFileDrop so you can call onAddNewFile({ target: { files: filesArray }
}) or adapt callers to accept the files array.

In `@frontend/src/components/Sidebar.jsx`:
- Around line 65-82: checkAdminRole's catch block currently swallows errors and
forces setCanSeeAdminMenu(false); update the catch in Sidebar.jsx (inside the
checkAdminRole async function that calls api.get('/api/user/details')) to log
the caught error (include the error object/message and context like "Failed to
fetch user details for admin check") before setting setCanSeeAdminMenu(false);
use the app's logging utility if available or console.error, and include
references to ADMIN_VISIBLE_ROLES and setCanSeeAdminMenu so the log helps debug
network vs permission issues.

In `@frontend/src/components/Sidebar.module.css`:
- Around line 168-188: Add an accessible keyboard focus style for the
interactive class .menuTitleToggle (and the other similar toggles mentioned) by
defining a :focus-visible rule that makes the element's focus state visually
distinct from the background (e.g., visible outline or box-shadow and preserved
background contrast), so keyboard users can clearly see focus when tabbing;
update the CSS by adding a .menuTitleToggle:focus-visible selector (and the
corresponding selectors for the other toggle classes at the other ranges) that
sets a clear outline/box-shadow, ensures outline-offset or border-radius matches
the element, and does not rely solely on :hover styles.

In `@frontend/src/pages/Board.jsx`:
- Around line 114-126: The catch block in the useEffect's fetchRole swallows
errors silently; update the fetchRole async function to log the caught error
before setting setCanCreateSubBoard(false) so failures from
api.get('/api/user/details') are visible; reference the fetchRole function, the
catch block, setCanCreateSubBoard, SUB_BOARD_ADMIN_ROLES and the
api.get('/api/user/details') call and ensure the log provides the error
object/message (use existing logger or console.error if none).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 062daf3b-7ae7-497d-a9b4-82730e40ecec

📥 Commits

Reviewing files that changed from the base of the PR and between 28a826c and 60ea17a.

📒 Files selected for processing (18)
  • frontend/src/components/Board/BoardActions.jsx
  • frontend/src/components/Board/BoardActions.module.css
  • frontend/src/components/Board/CategoryTabs.jsx
  • frontend/src/components/Board/CategoryTabs.module.css
  • frontend/src/components/Board/Modal.jsx
  • frontend/src/components/Board/Modal.module.css
  • frontend/src/components/Board/PostDetail/FileAttachmentList.jsx
  • frontend/src/components/Board/PostDetail/PostEditForm.jsx
  • frontend/src/components/Board/PostDetail/PostView.jsx
  • frontend/src/components/Board/PostItem.jsx
  • frontend/src/components/Sidebar.jsx
  • frontend/src/components/Sidebar.module.css
  • frontend/src/index.css
  • frontend/src/pages/Board.jsx
  • frontend/src/pages/PostDetail.jsx
  • frontend/src/pages/PostDetail.module.css
  • frontend/src/utils/boardApi.js
  • frontend/src/utils/boardRoute.js

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/PostDetail.jsx (1)

223-230: ⚠️ Potential issue | 🟠 Major

기존 첨부파일 삭제가 저장 payload에 반영되지 않습니다.

편집 UI에서는 frontend/src/components/Board/PostDetail/PostEditForm.jsx에서 기존 파일을 지울 수 있게 했는데, 여기서 만드는 updateData에는 newFiles만 들어갑니다. 그래서 저장 후 refreshPostAndComments()가 돌면 방금 지운 기존 첨부파일이 다시 나타납니다. 유지할 첨부파일 목록이나 deletedAttachmentIds 같은 계약을 같이 보내도록 맞춰야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/PostDetail.jsx` around lines 223 - 230, The save payload
only includes newFiles, so deleted existing attachments are reappearing; modify
the updateData sent from PostDetail.jsx to include the attachment-tracking field
your edit form uses (e.g., deletedAttachmentIds or retainedAttachmentIds) from
PostEditForm.jsx state/props and pass that through to boardApi.updatePost along
with title/content/files so the backend can remove the deleted attachments
before refreshPostAndComments() runs.
🧹 Nitpick comments (1)
frontend/src/pages/PostDetail.jsx (1)

262-281: 게시판 복귀 경로 계산을 한 곳으로 모으는 편이 안전합니다.

여기서는 'root'/board로 정규화하지만, Line 255의 삭제 후 이동은 아직 raw team을 그대로 씁니다. 규칙이 두 군데로 갈라져 있어서 전체 게시판 URL 정책이 바뀌면 한쪽만 쉽게 어긋납니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/pages/PostDetail.jsx` around lines 262 - 281, The code in
handleMoveToBoard duplicates team/root normalization, risking divergence;
compute a single normalizedTeamSegment (using location.state?.originTeam ||
team, then pass through toBoardRouteSegment or normalize 'root' to undefined)
and use that single variable for the early navigate('/board') case and for the
final navigate call (with encodeURIComponent when non-root). Update references:
handleMoveToBoard, location.state?.originTeam, team, toBoardRouteSegment,
targetBoardId, and navigate so all routing logic relies on normalizedTeamSegment
rather than branching with raw team or separate 'root' checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/Board/PostDetail/PostEditForm.jsx`:
- Around line 22-55: The form remains interactive while a save is in progress;
add a saving guard to prevent user actions being applied during save. Update
PostEditForm to accept an isSaving prop (or derive from parent) and at the top
of interactive handlers—handleFileButtonClick, handleDragEnter, handleDragOver,
handleDragLeave, handleDrop and any file-delete handler that uses
onAddNewFile—early-return when isSaving is true; additionally bind isSaving to
disable the file input (fileInputRef), disable the add-file button and set
title/body inputs to readOnly/disabled so no edits or file ops can occur until
the save completes.
- Around line 86-91: The hidden file input (referenced by fileInputRef in
PostEditForm.jsx) isn't being cleared, so selecting the same file twice won't
fire onAddNewFile; update the component to reset the input's value (e.g., set
fileInputRef.current.value = '' or null) after handling the file in onAddNewFile
or immediately before programmatically opening the picker so the change event
will fire for the same filename.

In `@frontend/src/utils/boardRoute.js`:
- Around line 19-20: The board route mapping is missing the '트레이딩팀' alias so
toBoardRouteSegment('트레이딩팀') returns null and causes navigation in PostItem.jsx
and PostDetail.jsx to fallback to /board; update the mapping in boardRoute.js to
include the alias (map '트레이딩팀' to 'trading') so toBoardRouteSegment returns the
correct segment and existing navigation logic in PostItem.jsx and PostDetail.jsx
works as intended.

---

Outside diff comments:
In `@frontend/src/pages/PostDetail.jsx`:
- Around line 223-230: The save payload only includes newFiles, so deleted
existing attachments are reappearing; modify the updateData sent from
PostDetail.jsx to include the attachment-tracking field your edit form uses
(e.g., deletedAttachmentIds or retainedAttachmentIds) from PostEditForm.jsx
state/props and pass that through to boardApi.updatePost along with
title/content/files so the backend can remove the deleted attachments before
refreshPostAndComments() runs.

---

Nitpick comments:
In `@frontend/src/pages/PostDetail.jsx`:
- Around line 262-281: The code in handleMoveToBoard duplicates team/root
normalization, risking divergence; compute a single normalizedTeamSegment (using
location.state?.originTeam || team, then pass through toBoardRouteSegment or
normalize 'root' to undefined) and use that single variable for the early
navigate('/board') case and for the final navigate call (with encodeURIComponent
when non-root). Update references: handleMoveToBoard,
location.state?.originTeam, team, toBoardRouteSegment, targetBoardId, and
navigate so all routing logic relies on normalizedTeamSegment rather than
branching with raw team or separate 'root' checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 43bf6f34-6ede-468e-9770-3972e2ed9409

📥 Commits

Reviewing files that changed from the base of the PR and between 60ea17a and 3742e33.

📒 Files selected for processing (7)
  • frontend/src/components/Board/PostDetail/FileAttachmentList.jsx
  • frontend/src/components/Board/PostDetail/PostEditForm.jsx
  • frontend/src/components/Board/PostDetail/PostView.jsx
  • frontend/src/components/Sidebar.module.css
  • frontend/src/pages/PostDetail.jsx
  • frontend/src/utils/boardApi.js
  • frontend/src/utils/boardRoute.js

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
frontend/src/components/Board/PostDetail/PostEditForm.jsx (1)

118-127: textarea에 드래그 핸들러 중복 설정에 대한 고려사항

textarea와 부모 div 모두에 동일한 드래그 핸들러가 설정되어 있습니다. stopPropagation()으로 이벤트 중복 처리는 방지되지만, textarea에서 부모 div로 드래그 이동 시 handleDragLeaverelatedTarget 체크로 인해 일시적인 시각적 깜빡임이 발생할 수 있습니다. 실제 사용 환경에서 문제가 확인되면 개선을 고려해주세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Board/PostDetail/PostEditForm.jsx` around lines 118 -
127, The textarea and its parent div both attach the same drag handlers
(handleDragEnter, handleDragOver, handleDragLeave, handleDrop), which can cause
visual flicker when moving the drag between them; either remove the duplicate
handlers from the textarea and only handle drag on the parent div (keeping
onDrop on the textarea if you need file capture there), or update
handleDragLeave to ignore transitions between the textarea and its parent by
checking event.relatedTarget (or using contains on event.currentTarget) and
returning early when the related element is the parent/child, ensuring
stopPropagation is preserved; locate these handlers and the textarea element in
PostEditForm.jsx to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/components/Board/PostDetail/PostEditForm.jsx`:
- Around line 118-127: The textarea and its parent div both attach the same drag
handlers (handleDragEnter, handleDragOver, handleDragLeave, handleDrop), which
can cause visual flicker when moving the drag between them; either remove the
duplicate handlers from the textarea and only handle drag on the parent div
(keeping onDrop on the textarea if you need file capture there), or update
handleDragLeave to ignore transitions between the textarea and its parent by
checking event.relatedTarget (or using contains on event.currentTarget) and
returning early when the related element is the parent/child, ensuring
stopPropagation is preserved; locate these handlers and the textarea element in
PostEditForm.jsx to apply the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 15213a3c-1f30-4fc7-a315-227de5bca19b

📥 Commits

Reviewing files that changed from the base of the PR and between 3742e33 and e1b4f33.

📒 Files selected for processing (1)
  • frontend/src/components/Board/PostDetail/PostEditForm.jsx

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~

@discipline24 discipline24 merged commit 43dc932 into main Mar 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔥 [긴급] ⌛ [~3/9] 🚀 [기능개선][게시물] 게시물 페이지 디테일 개선

3 participants