Skip to content

Conversation

@woneeeee
Copy link
Member

@woneeeee woneeeee commented Dec 10, 2025

1. 무슨 이유로 코드를 변경했나요?


2. 어떤 위험이나 장애를 발견했나요?


3. 관련 스크린샷을 첨부해주세요.

2025-12-10.9.55.06.mov

4. 완료 사항

  • 게시판 디자인 토큰 적용 완료
  • 게시판 브레드크럼 적용 완료
  • 게시, 수정 헤더 관련 layout 분리

5. 추가 사항

src/Layout/FixedLayout.tsx에서 Footer가 props를 안주고 있어서 오류가 뜨더라구요 그래서 추가해뒀습니닷!

board에서도 저장, 게시가 필요해서 저장일 경우 요렇게 할 수 있도록 수정해뒀습니닷 !!

      <EditGNB onClickButton={handleClickButton} save />

Summary by CodeRabbit

  • New Features

    • 게시판 전반에 브레드크럼 내비게이션 추가 및 게시물 작성용 플로팅(부동) 버튼 도입
    • 공지사항 상세 화면 개선 및 새 상세 뷰 컴포넌트 추가
  • Refactor

    • 게시판 레이아웃 및 편집 흐름 재구성(편집/작성 화면에 헤더·저장 버튼 통합)
    • 댓글·작성 폼과 게시물 리스트 구조 간결화 및 접근성 개선
  • Style

    • 전체 UI를 디자인 토큰(색상/간격/타이포) 기반으로 통일
    • 검색 입력 포커스 상태 시각 개선

✏️ Tip: You can customize this high-level summary in your review settings.

@woneeeee woneeeee requested review from JIN921 and dalzzy December 10, 2025 13:02
@woneeeee woneeeee self-assigned this Dec 10, 2025
@woneeeee woneeeee added 🐞 Fix Something isn't working 🎨 Design CSS 스타일링 labels Dec 10, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

PostLayout 컴포넌트 추가, 라우팅에서 board 관련 경로를 PostLayout 기반으로 재구성, 광범위한 디자인 토큰(colors/units/typography) 적용, Breadcrumb 컴포넌트 추가 및 보드 관련 페이지/스타일·컴포넌트 구조 변경이 다수 포함됨.

Changes

코호트 / 파일(들) 변경 요약
레이아웃: PostLayout 추가
src/Layout/PostLayout.tsx
새 PostLayout 컴포넌트 추가: window 크기 기반 isMobile 계산, Suspense+DelayedFallback, Wrapper > Content > Outlet, Footer에 isMobile 전달.
레이아웃 원시 노출
src/Layout/ResponsiveLayout.tsx
Wrapper, Content를 export로 변경; Content의 padding 규칙 제거.
라우팅 변경
src/routes/route.tsx
board 관련 경로를 PostLayout(PrivateRoute)으로 감싸도록 라우트 구조 재배치; 기존 BoardLayout 제거.
네비게이션: Breadcrumb 추가
src/components/common/Breadcrumb.tsx
src/styles/breadCrum/index.ts
새로운 Breadcrumb 컴포넌트·인터페이스 추가 및 스타일 컴포넌트(BreadCrumContainer, CrumbButton) 추가.
디자인 토큰 마이그레이션
여러 보드 컴포넌트 및 스타일
e.g., src/components/Board/*.tsx, src/styles/board/*.ts
theme 기반 색·타이포그래피 제거, colors, units, typography 토큰으로 대체. 여러 styled 컴포넌트 추가/이름 변경 및 공개 API 표면 일부 변경(추가/제거).
보드 상세/편집/작성 흐름 재구성
다수 페이지 변경
e.g., src/pages/board/**, src/components/Board/Notices/NoticesDetailMain.tsx, PostDetailMain.tsx
Header/모달 기반 편집·삭제 흐름 제거하고 Breadcrumb·EditGNB·MenuModal/SelectModal 등으로 재구성. 파일 다운로드, 삭제 흐름 구현 및 모달 UI 변경.
컴포넌트 구조·아이콘 업데이트
src/components/Board/Comment.tsx
CommentInput.tsx
PostFile.tsx
ReplyComment.tsx
Comment: 버튼을 CommentTop으로 이동. CommentInput: FileContainer 제거 및 토큰화. SVG 임포트들을 ?react로 변경해 React 컴포넌트로 사용.
ExpandableTagList / StudyWriteTemplate 변화
src/components/Board/ExpandableTagList.tsx
src/components/Board/StudyWriteTemplate.tsx
ExpandableTagList에서 onRefresh 제거(프로퍼티·상태 삭제). StudyWriteTemplate: headerTitle/onSave 제거, 일부 styled 컴포넌트 export로 노출.
네비게이션 버튼/헤더 변경
src/components/Navigation/*.tsx
DesktopGNB: path를 배열로 허용. EditGNB/HeaderButton: save prop 추가/이름 변경 및 버튼 텍스트 조건부 처리.
스타일 컴포넌트 리팩토링
src/styles/board/*.ts, src/styles/dues/Dues.styled.ts
다수 스타일 컴포넌트 토큰 기반으로 대체, 새로운 styled exports 추가/삭제(예: FloatingButton, BoardTitle, CommentTop 등). 일부 위치·패딩·간격 조정.

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant Browser as 브라우저
    participant Router as 라우터
    participant PostLayout as PostLayout
    participant OutletComp as Outlet(게시판 컴포넌트)
    participant API as API

    User->>Browser: /board/notices/:id 요청
    Browser->>Router: 라우팅 결정
    Router->>PostLayout: PostLayout 렌더링 (PrivateRoute)
    PostLayout->>PostLayout: window.width 비교 -> isMobile 결정
    PostLayout->>OutletComp: Outlet 렌더(Wrapper > Content)
    OutletComp->>API: 게시물 상세 조회 요청
    API-->>OutletComp: 게시물 데이터 응답
    OutletComp->>PostLayout: Footer 렌더 호출 (isMobile 전달)
    User->>OutletComp: 파일 다운로드 클릭
    OutletComp->>API: 파일 다운로드(Blob)
    API-->>OutletComp: Blob 응답
    OutletComp->>Browser: 프로그래밍 방식 파일 저장
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60분

Possibly related PRs

Suggested labels

✨ Feat, 🔨 Refactor

Suggested reviewers

  • dalzzy
  • JIN921

개요

새로운 PostLayout 컴포넌트를 도입하고, 광범위한 디자인 토큰 기반 스타일링으로 마이그레이션하며, Breadcrumb 네비게이션을 추가하고, 라우팅 구조를 재구성합니다. 또한 여러 보드 컴포넌트의 UI 구조를 개선하고, 공유 레이아웃 원시 요소(Wrapper, Content)를 내보냅니다.

변경 사항

코호트 / 파일(들) 변경 요약
새로운 레이아웃 컴포넌트
src/Layout/PostLayout.tsx
새로운 PostLayout 컴포넌트 추가. Suspense 래퍼, 윈도우 크기 기반 모바일 상태 감지(units.device.tablet), Wrapper > Content > Outlet 렌더링, isMobile props를 받는 Footer 포함.
레이아웃 원시 요소 내보내기
src/Layout/ResponsiveLayout.tsx
Wrapper와 Content 스타일 컴포넌트를 export로 변경. Content의 padding 규칙 제거(15px).
새로운 네비게이션 컴포넌트
src/components/common/Breadcrumb.tsx
src/styles/breadCrum/index.ts
BreadcrumbItem과 BreadcrumbProps 인터페이스를 포함한 새로운 Breadcrumb 컴포넌트. 홈 아이콘과 항목별 네비게이션 지원. BreadCrumContainer와 CrumbButton 스타일 컴포넌트 추가.
새로운 공지사항 상세 컴포넌트
src/components/Board/Notices/NoticesDetailMain.tsx
게시물 상세 조회, 파일 다운로드, 삭제 기능을 포함한 새로운 NoticesDetailMain 컴포넌트. ReactMarkdown 및 확인 모달 지원.
보드 컴포넌트 구조 개선
src/components/Board/Comment.tsx
src/components/Board/CommentInput.tsx
src/components/Board/ExpandableTagList.tsx
Comment: 응답/메뉴 버튼을 CommentTop 헤더로 이동. CommentInput: FileContainer 제거, 디자인 토큰 적용. ExpandableTagList: onRefresh 기능 제거, 디자인 토큰 마이그레이션.
디자인 토큰 마이그레이션 (보드 컴포넌트)
src/components/Board/ContentPost.tsx, src/components/Board/MarkdownTap.tsx, src/components/Board/StudyPostTitle.tsx, src/components/Board/StudyTag.tsx, src/components/Board/StudyBoardSearch.tsx, src/components/Board/WeekTag.tsx
theme 기반 스타일을 colors, units, typography 토큰으로 교체. 폰트, 색상, 간격을 토큰 기반 값으로 업데이트.
부분 게시판 UI 재구성
src/components/Board/PartBoard.tsx
src/components/Board/EduPartTap.tsx
src/components/Board/PartBoardTap.tsx
PartBoard: 드래그 가능 기능 제거, 반응형 레이아웃 추가, 데스크톱 아이콘 지원, 키보드 네비게이션 추가. TabContainer 단순화, isActive 속성 추가.
교육자료/공지사항 작성 컴포넌트
src/components/Board/EduWrite.tsx
src/components/Board/NoticeWrite.tsx
src/components/Board/StudyWriteTemplate.tsx
EduWrite/NoticeWrite: headerTitle과 onSave props 제거. StudyWriteTemplate: 스타일 컴포넌트 내보내기, 디자인 토큰 적용, Header 사용 제거.
파일 및 아이콘 컴포넌트 업데이트
src/components/Board/PostFile.tsx
src/components/Board/ReplyComment.tsx
src/components/Board/SlideNotice.tsx
src/components/Board/SlideEdu.tsx
SVG 임포트를 React 컴포넌트 형식으로 변경(?react). 아이콘 렌더링 방식 업데이트. SlideNotice: 게시자 정보와 파일 아이콘 추가. SlideEdu: 텍스트 구분자를 Divider 컴포넌트로 교체.
네비게이션 헤더 업데이트
src/components/Navigation/DesktopGNB.tsx
src/components/Navigation/EditGNB.tsx
src/components/Navigation/HeaderButton.tsx
DesktopGNB: 게시판 경로를 배열로 변경. EditGNB: save prop 추가, useLocation 제거. HeaderButton: isBoard를 save로 변경, '저장'/'게시' 텍스트 조건부 표시.
페이지 레이아웃 및 라우팅
src/pages/Layout.tsx (제거)
src/routes/route.tsx
BoardLayout 제거. PostLayout으로 보드 관련 라우트 감싸기. /board/education, /board/notices 관련 라우트를 새로운 PrivateRoute/PostLayout 구조로 이동.
보드 페이지 재구성
src/pages/board/Board.tsx
src/pages/board/EducationBoard.tsx
src/pages/board/notices/BoardNotice.tsx
src/pages/board/part/PartBoard.tsx
Header를 Breadcrumb로 교체. FloatingWritingIcon 추가(관리자 전용). useCustomBack 제거. 상단 네비게이션 영역에 Breadcrumb 추가.
상세 페이지 모달 제거 및 Breadcrumb 적용
src/pages/board/education/EduDetail.tsx
src/pages/board/notices/NoticeDetail.tsx
src/pages/board/part/PartDetail.tsx
모달 기반 편집/삭제 흐름 제거. Breadcrumb 네비게이션 추가. PostDetailMain을 NoticesDetailMain(공지)으로 교체.
작성/편집 페이지 재구성
src/pages/board/education/EduEdit.tsx, src/pages/board/education/EduPost.tsx
src/pages/board/notices/NoticeEdit.tsx, src/pages/board/notices/NoticePost.tsx
src/pages/board/part/PartEdit.tsx, src/pages/board/part/PartPost.tsx
EditGNB와 Breadcrumb으로 감싸기. onSave를 handleClickButton으로 변경. headerTitle props 제거. StudyWriteTemplate에서 onSave/headerTitle 제거.
스타일 컴포넌트 마이그레이션
src/styles/board/Board.styled.ts
src/styles/board/Comment.styled.ts
src/styles/board/Markdown.styled.ts
src/styles/board/PartBoard.styled.ts
src/styles/board/PostDetail.styled.ts
src/styles/board/PostFile.styled.ts
src/styles/board/StudyLogListItem.styled.ts
디자인 토큰 기반 스타일로 광범위하게 마이그레이션. 새로운 컴포넌트(BoardTitle, BreadCrumContainer, FloatingButton 등) 추가. theme 기반 값을 colors, units, typography로 교체. 여러 공개 내보내기 추가/제거.
기타 페이지 및 스타일
src/pages/attend/Attendance.tsx
src/styles/dues/Dues.styled.ts
src/components/Board/PostDetailMain.tsx
src/components/common/MenuModal.tsx
src/components/Board/StudyLogListItem.tsx
AttendContainer에 padding 추가. StyledDues에 padding 추가. PostDetailMain: 모달 기반 편집/삭제 흐름 재구성, MenuModal/SelectModal 추가, 파일 다운로드 개선. MenuModal: 위치 조정(top: 130px). StudyLogListItem: truncateText 제거.

시퀀스 다이어그램

sequenceDiagram
    participant User as 사용자
    participant Browser as 브라우저
    participant Router as 라우터
    participant PostLayout as PostLayout
    participant OutletComp as 게시판 컴포넌트
    participant API as API

    User->>Browser: /board/notices/:id 접근
    Browser->>Router: 라우팅 요청
    Router->>PostLayout: PostLayout 렌더 (PrivateRoute)
    PostLayout->>PostLayout: 윈도우 크기 판정 -> isMobile
    PostLayout->>OutletComp: Suspense로 Outlet(Wrapper>Content) 렌더
    OutletComp->>API: 게시물 상세 요청
    API-->>OutletComp: 게시물 데이터 반환
    OutletComp->>PostLayout: Footer 렌더(isMobile 전달)
    User->>OutletComp: 파일 다운로드 클릭
    OutletComp->>API: 파일 블롭 요청
    API-->>OutletComp: Blob 응답
    OutletComp->>Browser: 프로그래밍적 다운로드 실행
Loading

관련될 가능성이 있는 PR들

제안된 레이블

✨ Feat, 🔨 Refactor

제안된 리뷰어

  • dalzzy
  • JIN921

🐰 토큰 깃든 길 따라 달려가네
브레드크럼 빛나며 길을 열고
레이아웃 새 옷 입고 춤추네
파일은 내려오고 버튼은 반응해
당근 한 입, 배포의 축제! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive PR 설명이 저장소의 필수 템플릿을 부분적으로 준수합니다. 완료 사항과 추가 사항 섹션은 작성되었으나, 변경 이유, 위험 사항, 스크린샷 설명이 미흡합니다. 변경 이유(섹션 1), 위험이나 장애(섹션 2)를 자세히 작성하고, 스크린샷에 대한 설명을 추가하여 각 섹션을 충실히 완성하세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용과 정확히 일치합니다. '게시판 디자인 토큰 적용'은 다양한 파일에서 테마 기반 스타일을 디자인 토큰으로 대체하는 주요 변경 사항을 명확하게 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6017c9c and d6b0b56.

📒 Files selected for processing (3)
  • src/components/common/Breadcrumb.tsx
  • src/styles/board/Board.styled.ts
  • src/styles/board/BoardPost.styled.ts

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.

@woneeeee woneeeee changed the title Feat-WTH-54 디자인 토큰 적용 Feat-WTH-54 게시판 디자인 토큰 적용 Dec 10, 2025
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: 14

Caution

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

⚠️ Outside diff range comments (2)
src/pages/board/education/EduEdit.tsx (1)

84-90: 에러 메시지 로직이 현재 파일 컨텍스트와 불일치합니다.

이 파일은 EduEdit (교육자료 수정)인데, 에러 메시지에서 path === 'board' 조건으로 "게시글 작성" vs "공지사항 작성" 메시지를 분기하고 있습니다. 교육자료 수정 페이지이므로 해당 메시지가 적절하지 않습니다.

-      toastError(
-        path === 'board'
-          ? '게시글 작성 중 문제가 발생했습니다.'
-          : '공지사항 작성 중 문제가 발생했습니다.',
-      );
+      toastError('교육자료 수정 중 문제가 발생했습니다.');
src/pages/board/part/PartPost.tsx (1)

36-87: 파트 게시판 브레드크럼이 항상 study 경로로 이동하는 버그

PartPost 에서 실제 작성 완료 후에는 navigate(/board/${slug}/${part}); 로 현재 카테고리(study/article)에 맞는 파트 게시판으로 되돌아가지만, 브레드크럼의 “{part} 파트게시판” 버튼 클릭 시에는 항상 study 경로로만 이동하도록 고정되어 있습니다.

const handleClickPartBoard = () => {
  navigate(`/board/study/${part}`);
};

즉, slug === 'article' 인 상태에서 글쓰기 화면에 들어와 브레드크럼의 파트게시판을 클릭하면, 의도와 다르게 스터디 게시판(/board/study/${part})으로 이동하게 됩니다.

아래처럼 slug 를 사용하도록 수정하는 편이 맞을 것 같습니다.

-  const handleClickPartBoard = () => {
-    navigate(`/board/study/${part}`);
-  };
+  const handleClickPartBoard = () => {
+    navigate(`/board/${slug}/${part}`);
+  };

이렇게 하면 상단 브레드크럼과 작성 완료 후 리다이렉트 경로가 일관되게 동작합니다.

Also applies to: 88-96, 101-111

🧹 Nitpick comments (37)
src/pages/Receipt.tsx (1)

3-12: 디자인 토큰 적용 잘 완료되었습니다!

디자인 토큰을 사용한 패딩 적용이 올바르게 구현되었으며, 일관된 스타일링을 위한 좋은 변경입니다. 다만 다음 사항들을 검토해 주세요:

  1. 중복 속성: widthmax-width가 모두 375px로 동일하게 설정되어 있습니다. 하나만 사용해도 충분합니다.
  2. 반응형 디자인: 고정된 375px 너비로 인해 더 큰 화면에서도 동일한 너비를 유지합니다. 영수증 페이지의 특성상 의도된 것일 수 있지만, 확인이 필요합니다.

선택적 개선 제안:

 const Container = styled.div`
   display: flex;
   flex-direction: column;
-  width: 375px;
   max-width: 375px;
+  width: 100%;
   margin-bottom: 50px;
   padding: 0 ${units.padding['450']}px;
 `;

이렇게 하면 작은 화면에서는 100% 너비를 사용하고, 큰 화면에서는 375px로 제한됩니다.

src/styles/board/PostFile.styled.ts (1)

13-15: 하드코딩된 색상 값을 디자인 토큰으로 교체하는 것을 고려해보세요.

Line 13, 15는 디자인 토큰을 올바르게 사용하고 있으나, Line 14의 #ffffff0d는 하드코딩된 색상 값입니다. 디자인 토큰 마이그레이션의 일관성을 위해 반투명 배경색도 토큰으로 관리하는 것이 좋습니다.

designTokens.ts에 반투명 배경색 토큰이 있는지 확인하시고, 있다면 다음과 같이 적용하는 것을 고려해보세요:

-  background-color: #ffffff0d;
+  background-color: ${colors.semantic.background.translucent};

만약 해당 토큰이 없다면, 디자인 토큰에 추가하는 것을 권장합니다.

src/components/Board/StudyLogListItem.tsx (1)

60-60: 내용 전체 노출 변경, 레이아웃만 한 번 확인해 주세요

content를 그대로 렌더링하도록 바뀐 부분은 단순하고 이해하기 좋습니다. 다만 아주 긴 내용이 들어오는 경우 리스트 아이템 높이가 많이 커질 수 있으니, S.ContentText에서 line-clamp 등으로 이미 제어하고 있는지 UI에서 한 번만 확인해 보시면 좋겠습니다.

src/components/Navigation/DesktopGNB.tsx (1)

15-19: 여러 경로를 하나의 메뉴로 묶는 로직이 깔끔합니다

Array.isArray(menu.path)로 분기해 active 여부와 navigate 경로를 계산하는 구조가 명확하고 확장에도 유리해 보입니다.
다만 menus가 커질 경우를 대비해서,

type MenuPath = string | string[];
type Menu = { name: string; path: MenuPath };
const menus: Menu[] = [ ... ];

처럼 메뉴 타입을 명시해 두면 이후 유지보수 시 의도가 더 분명해질 것 같습니다.

Also applies to: 29-36

src/components/common/MenuModal.tsx (1)

19-19: 위치·여백 조정은 무난해 보입니다

상단 여백과 오른쪽 마진 조정은 기존 동작을 해치지 않는 단순 스타일 변경으로 보입니다.
전체적으로 디자인 토큰을 도입하는 PR이니, 추후에는 이 값들도 units.margin/units.padding 등 토큰으로 통일해 두면 테마 변경 시 관리가 더 쉬울 것 같습니다.

Also applies to: 42-42

src/components/Board/MarkdownTap.tsx (1)

2-3: 타이포·컬러를 토큰으로 정리한 방향이 좋습니다

typography.Sub2colors.semantic.text.strong을 사용해서 탭 텍스트/언더라인 스타일을 토큰 기반으로 맞춘 부분이 전체 디자인 시스템과 잘 어울립니다.
나중에는 border-bottom: 2px solid ${theme.color.gray[20]}; 같은 부분도 designTokens 기반 색상으로 교체하면 테마 소스가 더 일관될 것 같습니다.

Also applies to: 28-31, 37-37

src/components/Board/NoticeWrite.tsx (1)

6-18: StudyWriteTemplate의 공용 레이아웃 컴포넌트를 재사용하는 게 어떨까요?

여기서 Container/MarkdownContainer에 gap·padding 토큰을 직접 선언하고 있는데, StudyWriteTemplate.tsx 쪽에서 동일한 역할의 Container/MarkdownContainer를 export 하면서 width, max-width, pcResponsive까지 포함해 관리하는 것으로 보입니다.

공지 작성 화면도 동일한 레이아웃 스펙을 의도하신 거라면, 이 파일에서는 styled를 새로 정의하기보다 해당 export를 import 해서 재사용하면:

  • 스타일 중복을 줄이고
  • 이후 레이아웃 스펙 변경 시 한 곳만 수정해도 되어

유지보수가 더 쉬울 것 같습니다.

src/components/Board/StudyBoardSearch.tsx (1)

25-27: 검색 UI에 토큰·포커스 상태를 도입한 방향이 좋습니다

타이포/컬러/패딩을 전부 designTokens와 typography.Body1로 맞춰서 보드 검색 UI가 전체 디자인 시스템과 잘 정렬된 것 같습니다. isFocused 상태를 둬서 포커스 시 보더/배경을 바꾸는 것도 직관적입니다.

다만 이 부분은 React state 없이 CSS만으로도 구현이 가능해서, 다음처럼 :focus-within을 쓰면 코드가 조금 더 단순해질 수 있습니다:

-const Search = styled.div<{ isFocused: boolean }>`
+const Search = styled.div`
   display: flex;
   align-items: center;
   height: 40px;
   justify-content: space-between;
   border-radius: ${units.radius.sm}px;
-  border: ${(props) =>
-    props.isFocused
-      ? `1px solid ${colors.semantic.brand.secondary}`
-      : '1px solid transparent'};
+  border: 1px solid transparent;
   padding: ${units.padding['200']}px ${units.padding['200']}px
     ${units.padding['200']}px ${units.padding['400']}px;
   box-sizing: border-box;
   background-color: ${colors.semantic.container.neutral};
+
+  &:focus-within {
+    border-color: ${colors.semantic.brand.secondary};
+  }
 `;

이렇게 하면 isFocused state 및 onFocus/onBlur 핸들러가 필요 없어지고, 삭제/검색 아이콘을 클릭할 때도 컨테이너가 자연스럽게 포커스 스타일을 유지합니다.

Also applies to: 30-52, 59-72, 100-103, 145-175

src/pages/board/notices/BoardNotice.tsx (2)

124-130: 클릭 가능한 아이콘에 접근성 및 스타일 개선 필요

BreadcrumHomeIcon은 클릭 가능하지만, 관련 스니펫의 CrumbButton과 달리 cursor: pointer 스타일이 적용되지 않았을 수 있습니다. 또한 스크린 리더 사용자를 위한 접근성 속성이 누락되었습니다.

-        <BreadcrumHomeIcon onClick={handleClickHome} />
+        <BreadcrumHomeIcon 
+          onClick={handleClickHome} 
+          style={{ cursor: 'pointer' }}
+          role="button"
+          aria-label="홈으로 이동"
+        />

또는 BreadcrumHomeIcon을 감싸는 CrumbButton 컴포넌트를 사용하는 것을 고려해 주세요.


173-177: 관리자 전용 FAB 버튼 구현 확인

isAdmin 조건부 렌더링이 적절하게 적용되었습니다. 다만, 접근성을 위해 버튼에 aria-label을 추가하는 것을 권장합니다.

-          <FloatingWritingIcon onClick={handleWriting} />
+          <FloatingWritingIcon 
+            onClick={handleWriting} 
+            role="button"
+            aria-label="새 게시물 작성"
+          />
src/components/Board/CommentInput.tsx (1)

26-26: 시맨틱 토큰 사용 권장

colors.light.neutral[800]을 직접 사용하는 대신 시맨틱 토큰(예: colors.semantic.backGround 또는 colors.semantic.container.neutral)을 사용하면 다크/라이트 모드 전환 시 더 일관된 테마 적용이 가능합니다.

src/components/Board/Notices/NoticesDetailMain.tsx (2)

63-66: useLocation 훅 사용 권장

window.location.href를 직접 파싱하는 대신 React Router의 useLocation 훅을 사용하면 더 일관된 코드가 됩니다.

+import { useNavigate, useParams, useLocation } from 'react-router-dom';
 // ...
-  const url = new URL(window.location.href);
-  const pathArray = url.pathname.split('/');
-  const path = pathArray[2];
+  const location = useLocation();
+  const pathArray = location.pathname.split('/');
+  const path = pathArray[2];

102-122: 파일 다운로드 로직에서 변수명 섀도잉

line 106의 url2 변수가 line 63의 url 변수와 혼동될 수 있습니다. 더 명확한 변수명(예: blobUrl)을 사용하는 것을 권장합니다.

-        const url2 = window.URL.createObjectURL(blob);
+        const blobUrl = window.URL.createObjectURL(blob);
         const a = document.createElement('a');
-        a.href = url2;
+        a.href = blobUrl;
         a.download = fileName;
         document.body.appendChild(a);
         a.click();
         setTimeout(() => {
-          window.URL.revokeObjectURL(url2);
+          window.URL.revokeObjectURL(blobUrl);
           a.remove();
         }, 1000);
src/styles/board/Board.styled.ts (2)

159-159: 주석 처리된 코드 제거 권장

// justify-content: center; 주석은 불필요하므로 제거하는 것이 좋습니다.

-  // justify-content: center;

172-172: 하드코딩된 값을 디자인 토큰으로 통일

gap: 5px가 하드코딩되어 있습니다. 다른 속성들처럼 units.margin['100'] (4px) 또는 units.margin['200'] (8px) 토큰 사용을 권장합니다.

src/Layout/FixedLayout.tsx (1)

8-8: 모바일 판별 로직과 Footer prop 연동은 자연스럽습니다만, 네이밍만 한 번 점검해보시면 좋겠습니다.

isMobilewidth <= units.device.tablet 조건을 사용해서 실질적으로는 “태블릿 이하” 화면을 포함하고 있어서, Footer 쪽에서도 모바일+태블릿 공통 레이아웃을 의도하신 거라면 괜찮지만, 순수 모바일만 구분하려는 의도였다면 이름(예: isSmallScreen 등)이나 조건을 한 번 맞춰보는 것도 좋을 것 같습니다.

Also applies to: 21-23, 33-33

src/styles/breadCrum/index.ts (1)

1-14: BreadCrum 스타일 토큰 적용은 적절하지만, CrumbButton의 접근성은 한 번 점검해보시는 게 좋겠습니다.

CrumbButtonstyled.div + cursor: pointer라 키보드 포커스/Enter로는 동작하지 않을 수 있습니다. 실제 사용처에서 button/a 요소에 as={CrumbButton} 형태로 입히거나, 이쪽에서 role="button"/tabIndex={0} 등을 부여해서 키보드 접근성을 확보하는 방안을 한 번 고려해 보시면 좋겠습니다.

src/components/Board/ContentPost.tsx (1)

11-12: ContentPost에 디자인 토큰과 타이포그래피를 적용한 방향이 좋습니다.

여백·패딩·텍스트/배경색·타이포그래피를 모두 토큰(colors.semantic, units, typography.Body1)으로 맞춘 점이 일관성 측면에서 좋아 보입니다. 다만 동일 컴포넌트 내에서 스크롤바 색상 등은 아직 theme 기반을 쓰고 있어서, 전역적으로 디자인 토큰으로 이전할 계획이 있다면 후속 PR에서 이 부분도 colors.semantic 계열로 통일해 두면 더 관리하기 수월할 것 같습니다.

Also applies to: 17-17, 21-22, 26-27, 30-30, 40-40

src/styles/board/Dropdown.styled.ts (1)

2-3: Dropdown에 디자인 토큰을 적용한 방향은 좋지만, $hasValue prop 정리가 필요해 보입니다.

DropdownButton = styled.div<{ $hasValue: boolean }>로 타입은 받고 있지만, 스타일 내에서 $hasValue를 더 이상 참조하지 않아 dead prop 상태입니다. 실제로 값에 따라 색상/투명도 등을 나눌 계획이 없다면 제네릭 타입과 관련 로직을 제거해서 인터페이스를 단순화하고, 반대로 선택 상태에 따라 스타일을 다르게 줄 의도라면 $hasValue를 활용해 placeholder 색상 등을 구분해 주면 더 명확해질 것 같습니다.

또한 Button/List/Item은 토큰 기반으로 잘 옮겨졌는데, 상단의 Container/StudyDropdownList 등은 아직 theme 기반이라, 전역 디자인 토큰 전환이 끝나면 이쪽도 한 번에 맞춰 주시면 스타일 레이어 일관성이 더 좋아질 것 같습니다.

Also applies to: 104-117, 130-132, 135-143

src/pages/board/education/EduEdit.tsx (1)

106-116: 브레드크럼 접근성 개선을 권장합니다.

BreadcrumHomeIcon에 클릭 핸들러가 있지만 키보드 접근성을 위한 속성이 없습니다. <nav aria-label="breadcrumb">로 감싸고, 클릭 가능한 요소에 role="button"tabIndex={0}을 추가하는 것을 권장합니다.

-      <BreadCrumContainer>
-        <BreadcrumHomeIcon onClick={handleClickHome} />
+      <BreadCrumContainer as="nav" aria-label="breadcrumb">
+        <BreadcrumHomeIcon 
+          onClick={handleClickHome} 
+          role="button" 
+          tabIndex={0}
+          aria-label="홈으로 이동"
+        />
src/components/Navigation/HeaderButton.tsx (1)

6-19: LGTM! 스타일링 개선 승인, cursor 추가 권장

typography.Button1 적용과 border: none 추가가 디자인 토큰 시스템과 일관됩니다.

클릭 가능한 버튼임을 시각적으로 더 명확히 하기 위해 cursor: pointer 추가를 권장합니다.

 const ButtonContainer = styled.button`
   display: flex;
   align-items: center;
   background-color: ${colors.semantic.button.primary};
   ${typography.Button1};
   border: none;
+  cursor: pointer;
   padding: ${units.margin[200]}px ${units.padding[400]}px;
src/pages/board/part/PartBoard.tsx (1)

34-64: 글쓰기 버튼 권한 체크 제거 의도 및 주석 코드 정리 필요

useGetUserInfo / canWrite 로 파트별 글쓰기 권한을 제한하던 로직이 전부 주석 처리되고, 하단 플로팅 글쓰기 버튼이 항상 노출되는 구조로 바뀌었습니다. 백엔드에서 이미 권한을 강하게 검증한다면 큰 문제는 없을 수 있지만, 그렇지 않다면 모든 사용자가 글쓰기 화면까지 진입을 시도할 수 있어 UX/권한 측면에서 회귀가 될 수 있습니다.

  • 만약 권한 제한이 여전히 필요하다면, 기존 canWrite 계산 로직을復活시키고 <S.FloatingButton> 렌더링을 canWrite && ... 로 다시 감싸는 쪽이 안전해 보입니다.
  • 반대로 더 이상 권한 체크를 하지 않기로 한 의도라면, 현재 주석 처리된 useGetUserInfo / canWrite 관련 코드는 제거하거나 TODO 주석으로 의도를 명시해 두면 유지보수에 도움이 됩니다.

의도하신 방향이 어느 쪽인지 한 번만 확인 부탁드립니다.

Also applies to: 151-154, 244-246

src/pages/board/education/EduPost.tsx (1)

18-25: useParams 중복 호출 및 part 미존재 시 브레드크럼 경로 처리 개선 제안

현재 part 값을 위해 useParams 를 두 번 호출하고 있고, 두 번째 호출에서 얻은 partundefined 인 경우에도 그대로 문자열 템플릿에 들어가 /board/education/undefined 로 네비게이션될 여지가 있습니다.

  • 라우트 정의 상 part 파라미터가 항상 존재하지 않는 경우(예: 전체 교육자료에서 바로 글쓰기 진입)까지 고려한다면, partParam ?? 'ALL' 같은 fallback 을 두고 이를 브레드크럼/네비게이션에 재사용하는 편이 안전합니다.
  • 이미 위쪽에서 partParam 을 한 번 가져오고 있으므로, 아래쪽 useParams 는 제거하고 동일 값을 재사용해도 됩니다.

예시 리팩터링:

-  const { part: partParam } = useParams<{ part: PartTypes }>();
+  const { part: partParam } = useParams<{ part: PartTypes }>();
+  const breadcrumbPart = partParam ?? 'ALL';
   const initialSelectedParts: RealPart[] =
     partParam &&
@@
-  const { part } = useParams<{
-    part: string;
-  }>();
   const handleClickHome = () => {
     navigate('/home');
   };
   const handleClickBoard = () => {
     navigate('/board');
   };
   const handleClickPartBoard = () => {
-    navigate(`/board/education/${part}`);
+    navigate(`/board/education/${breadcrumbPart}`);
   };
@@
-          {part} 교육자료
+          {breadcrumbPart} 교육자료

라우트에서 part 가 항상 존재한다는 전제가 맞는지 한 번만 다시 확인해 보시면 좋겠습니다.

Also applies to: 74-85, 95-100

src/styles/board/StudyLogListItem.styled.ts (1)

4-12: 디자인 토큰/타이포그래피 적용은 일관적이며 무난합니다

Container 패딩/갭, 타이틀/본문/보조 텍스트의 타이포그래피와 색상을 units/colors.semantic/typography 기반으로 옮긴 부분이 전체 보드 스타일 개편 방향과 잘 맞습니다. 기능적인 이슈는 없어 보입니다.

다만 동일 파일 안에 여전히 gap: 5px; 같은 픽셀 상수가 일부 남아 있으니, 디자인 시스템에서 완전한 토큰화를 지향한다면 추후 여유 있을 때 margin/padding 토큰으로 치환하는 정도만 추가로 고려해 보셔도 좋겠습니다.

Also applies to: 29-30, 48-51, 65-78, 81-84

src/Layout/PostLayout.tsx (1)

1-27: PostLayout 구조와 isMobile 계산은 명확하며 동작 상 문제 없어 보입니다

useWindowSize + units.device.tablet 으로 모바일 여부를 판별해 Footer 에 전달하는 구조가 직관적이고, Wrapper/Content + Outlet 을 Suspense 로 감싼 것도 라우트 레이아웃 용도로 적절해 보입니다.

바깥쪽 <div> 의 inline style 정도는 기존 레이아웃 스타일 파일에 흡수해서 styled 컴포넌트로 분리하면 스타일 일관성 측면에서 조금 더 깔끔해질 수 있지만, 필수적인 변경은 아닙니다.

src/pages/board/notices/NoticePost.tsx (1)

15-18: 게시/공지 겸용 작성 핸들러의 postType·id 처리와 경로 파싱 로직 확인 제안

NoticePost 가 게시글/공지글 공용 작성 화면으로 보이는데, 몇 가지 확인 포인트가 있습니다.

  1. postType 결정 로직
const url = new URL(window.location.href);
const pathArray = url.pathname.split('/');
const path = pathArray[2];
const postType = path === 'board' ? 'postBoard' : 'postNotice';
  • 현재 라우트가 /board/notices/... / /board/board/... 처럼 구성되어 있다는 가정을 전제로 하고 있습니다. 라우트 구조가 바뀌면 잘못된 postType 으로 API 를 호출할 수 있으니, 가능하면 useLocation + 라우트 path, 혹은 useParams 기반으로 의도를 더 명시적으로 표현하는 쪽이 유지보수에 안전합니다.
  1. numericPostId 의 기본값 0 및 항상 id 전달
const { postId } = useParams();
const numericPostId = postId ? parseInt(postId, 10) : 0;
...
await postBoardNotice({
  ...,
  postType,
  id: numericPostId,
});
  • 생성 화면에서 postId 가 없는 경우에도 id: 0 이 항상 전달됩니다. 만약 postBoardNotice(및 백엔드)가 “id 존재 여부”로 생성/수정 여부를 판단한다면, 0 도 존재하는 값으로 인식해서 의도치 않은 코드 경로를 탈 수 있습니다.
  • 생성 시에는 id 를 아예 전달하지 않거나, postId 가 있을 때만 포함하도록 분기하는 편이 더 안전해 보입니다.
  1. window 기반 URL 파싱
  • 여러 파일에서 new URL(window.location.href) + split('/') 패턴을 쓰고 있는데, React Router 의 useLocation / useResolvedPath / generatePath 등을 활용하면 테스트/SSR 호환성과 라우트 구조 변경 내성을 모두 개선할 수 있습니다.

위 두 포인트(postType, id)가 현재 API 계약과 라우트 정의에 맞는지 한 번만 검증 부탁드립니다.

Also applies to: 27-61, 74-82, 87-95

src/pages/board/education/EduDetail.tsx (1)

15-25: 경로 파싱 및 타입 결정 로직을 라우터 정보 기반으로 단일화하는 것이 좋겠습니다

EduDetail 에서도 다른 보드 파일들과 마찬가지로 new URL(window.location.href) + split('/') 방식으로 path 를 구해 type 을 결정하고 있습니다.

const url = new URL(window.location.href);
const pathArray = url.pathname.split('/');
const path = pathArray[1];

const type = path === 'notices' ? 'notices' : 'board';
  • 이 로직은 현재 라우트가 /board/education/... / /notices/... 등 특정 형태라는 가정에 강하게 묶여 있어서, 라우트 구조가 조금만 변경되어도 디테일 API type 이 잘못 설정될 위험이 있습니다.
  • 이미 useParamspart, postId 를 얻고 있으니, 같은 맥락에서 useLocation 혹은 라우트 정의 상의 path 정보를 활용해 type 을 결정하는 유틸을 하나 두고(예: resolveBoardTypeFromPath(location.pathname)) 여러 컴포넌트에서 공통으로 사용하는 편이 유지보수에 유리해 보입니다.
  • 또한 window 직접 접근은 테스트/SSR 환경에서 문제가 될 수 있으므로, 가능한 한 React Router가 제공하는 정보에 의존하는 형태로 옮기는 것을 추천드립니다.

기능상 당장 깨지는 부분은 없어 보이지만, 이번 PR에서 유사한 URL 파싱 패턴이 여러 파일에 걸쳐 반복되고 있어 한 번에 정리해 두면 이후 라우트 개편 시 리스크를 줄일 수 있을 것 같습니다.

Also applies to: 76-84, 89-97

src/styles/board/Comment.styled.ts (1)

4-13: 댓글 스타일에 디자인 토큰 적용은 잘 되었고, 일부 상수 값은 추후 정리 여지 정도입니다

CommentContainer 패딩, CommentContentContainer/ReplyContentContainer 의 gap·padding·border-radius, NameText/ContentText/DateText 의 타이포그래피·색상 등을 units/colors.semantic/typography 로 옮긴 부분은 전체 보드 리뉴얼 방향과 일관되고, 기능적인 문제도 없어 보입니다.

선택된 댓글 배경('#508FFF1A'), ReplyCommentContainerpadding: 0 15px; 같이 몇몇 하드코딩된 색/px 값만 아직 남아 있는데, 디자인 팀에서 해당 값들을 토큰으로 올릴 계획이 있다면 나중에 한 번에 정리해 주시면 좋을 것 같습니다. 지금 상태로도 동작에는 문제가 없습니다.

Also applies to: 28-31, 49-52, 63-75, 83-86, 95-101, 115-124

src/pages/board/notices/NoticeDetail.tsx (1)

69-80: 공지 상세 브레드크럼/헤더 구조는 깔끔하지만 공통화 여지가 있습니다

브레드크럼 핸들러(handleClickHome/Board/Part)와 JSX 구조가 다른 게시판 페이지들과 거의 동일하게 반복되고 있습니다. 지금도 동작에는 문제 없지만, 나중에 경로 변경이나 텍스트 변경이 있을 때를 생각하면 Breadcrumb용 공용 컴포넌트(아이콘+CrumbButton 세트)를 하나 두고, 현재 파일에서는 “공지상세” 같은 라벨과 최종 경로만 주입하는 구조로 리팩터링하면 유지보수가 더 쉬울 것 같습니다.

또 여기서도 CrumbButton이 클릭 가능한 div라서 키보드 접근성이 떨어질 수 있다는 점(버튼/링크로 인식되지 않음)도 PartDetail과 동일하게 참고 부탁드립니다.

Also applies to: 85-93, 96-96

src/components/Board/PartBoard.tsx (1)

55-57: 아이콘 pointerEvents는 styled 컴포넌트로 옮기는 것이 더 깔끔해 보입니다

pressedKey 상태로 눌림 아이콘을 분기하는 구조는 명확합니다. 다만 81라인에서 <Icon style={{ pointerEvents: 'none' }} />와 같이 inline 스타일을 주입하고 있는데, 이런 속성은 S.PartIcon 또는 Icon을 감싸는 styled 컴포넌트 쪽으로 옮겨 두면 스타일 정의가 한 곳에 모여 유지보수하기 더 수월합니다. 동일 패턴을 다른 보드 컴포넌트에서도 쓸 수 있다면 공통 styled 래퍼로 빼는 것도 고려해 볼 만합니다.

Also applies to: 63-82, 88-89

src/components/Board/EduWrite.tsx (1)

7-14: originFile 타입 의존성 분리는 나중에 해두면 좋겠습니다

EduWrite에서 originFile 타입을 PartEdit 페이지 파일에서 직접 import 해 사용하고 있어, 교육/파트/공지 작성·수정 컴포넌트들이 모두 특정 페이지 구현에 의존하는 구조가 되었습니다. 이 타입이 실제로 공통으로 쓰이는 개념이라면 /types/models 같은 공용 위치로 분리한 뒤, PartEdit, NoticeEdit, EduWrite에서 모두 그 공통 타입만 참조하도록 바꾸면 의존성이 훨씬 깔끔해질 것 같습니다.

Also applies to: 29-31, 44-46, 67-75

src/pages/board/notices/NoticeEdit.tsx (1)

9-11: 공지 수정 화면 구조는 명확하지만, 타입/메시지/브레드크럼 몇 가지 정리 포인트가 있습니다

  • originFile 타입을 PartEdit에서 직접 import 해 사용하고 있어, 공지/파트/교육 작성·수정 컴포넌트가 특정 페이지 구현에 의존하는 구조가 되었습니다. 공통 타입으로 분리해 /types 같은 위치에서 가져오도록 정리하면 의존성이 훨씬 명확해질 것 같습니다.
  • handleClickButton의 catch 블록에서 토스트 메시지가 모두 ...작성 중 문제가 발생했습니다.로 되어 있는데, 이 컴포넌트는 수정(edit) 화면이라 사용자 입장에서는 “작성”보다 “수정”이라는 표현이 더 자연스러울 수 있습니다. 메시지를 분기하거나 텍스트만 수정해 주면 UX가 조금 더 정제될 것 같습니다.
  • 브레드크럼에서 CrumbButton이 클릭 가능한 div에 불과해 키보드 포커스/스크린리더 측면에서 버튼/링크로 인식되지 않습니다. 나중에 Breadcrumb 컴포넌트를 공통화할 때 <Link> 혹은 <button> 기반으로 래핑해 role/tabIndex/aria-label 등을 함께 처리해 주면 접근성 측면에서도 한 번에 개선될 것 같습니다.

Also applies to: 15-15, 42-86, 87-95, 98-118

src/pages/board/education/EducationBoard.tsx (3)

120-126: 주석 처리된 코드 제거 권장

더 이상 사용하지 않는 Header 컴포넌트 관련 주석 처리된 코드가 남아있습니다. 브레드크럼 UI로 전환이 완료되었다면, 이 주석 처리된 코드를 삭제하여 코드 가독성을 높이는 것이 좋습니다.

-      {/* <Header
-        isAccessible={isAdmin}
-        RightButtonType="WRITING"
-        onClickRightButton={handleRightButton}
-      >
-        교육자료
-      </Header> */}
       <BreadCrumContainer>

185-189: 플로팅 버튼 접근성 개선 권장

FloatingWritingIcononClick만 있고 키보드 접근성 속성이 없습니다. 스크린 리더 사용자와 키보드 사용자를 위해 role, tabIndex, aria-label 속성 추가를 권장합니다.

       {isAdmin && (
         <S.FloatingButton>
-          <FloatingWritingIcon onClick={handleWriting} />
+          <FloatingWritingIcon
+            onClick={handleWriting}
+            role="button"
+            tabIndex={0}
+            aria-label="교육자료 작성"
+            onKeyDown={(e) => e.key === 'Enter' && handleWriting()}
+          />
         </S.FloatingButton>
       )}

127-133: 브레드크럼 아이콘 접근성 속성 추가 권장

BreadcrumHomeIcononClick이 있지만 키보드 접근성 속성이 없습니다. 일관된 접근성을 위해 role="button", tabIndex={0}, aria-label 속성 추가를 고려해주세요.

src/components/Board/PostDetailMain.tsx (1)

93-105: 삭제 후 토스트 타이밍 검토 필요

navigate 호출 직후 setTimeout으로 토스트를 표시하고 있습니다. 컴포넌트가 언마운트된 후 토스트가 호출될 수 있어 예상치 못한 동작이 발생할 수 있습니다. 토스트를 먼저 표시하거나, 네비게이션 대상 페이지에서 삭제 성공 메시지를 처리하는 방식을 고려해보세요.

     try {
       await deletePost(numericPostId, type);
+      toastInfo('게시물이 삭제되었습니다');
       navigate(`/board/${category}/${part}`, { replace: true });
-      setTimeout(() => {
-        toastInfo('게시물이 삭제되었습니다');
-      }, 500);
     } catch (err) {
src/styles/board/PostDetail.styled.ts (1)

229-237: TextButton 디자인 토큰 적용 고려

TextButton에서 theme.color.gray[30]theme.color.negative를 사용하고 있습니다. 다른 컴포넌트들은 colors.semantic 토큰을 사용하고 있으므로, 일관성을 위해 디자인 토큰으로 통일하는 것을 고려해보세요.

 export const TextButton = styled.div<{ $isLast?: boolean }>`
   width: calc(100% - 8px);
   box-sizing: border-box;
   padding: 12px 0 12px 16px;
   margin: 0 4px;
   border-bottom: ${(props) =>
-    props.$isLast ? 'none' : `1px solid ${theme.color.gray[30]}`};
-  color: ${(props) => (props.$isLast ? theme.color.negative : 'white')};
+    props.$isLast ? 'none' : `1px solid ${colors.semantic.line}`};
+  color: ${(props) => (props.$isLast ? colors.semantic.state.error : colors.semantic.text.normal)};
 `;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e0b1de and 67b07df.

⛔ Files ignored due to path filters (24)
  • src/assets/images/ic_board_BE.svg is excluded by !**/*.svg
  • src/assets/images/ic_board_D.svg is excluded by !**/*.svg
  • src/assets/images/ic_board_FE.svg is excluded by !**/*.svg
  • src/assets/images/ic_board_PM.svg is excluded by !**/*.svg
  • src/assets/images/ic_board_detail_kebabButton.svg is excluded by !**/*.svg
  • src/assets/images/ic_breadcrum_arrow_right.svg is excluded by !**/*.svg
  • src/assets/images/ic_breadcrum_home.svg is excluded by !**/*.svg
  • src/assets/images/ic_circle_close.svg is excluded by !**/*.svg
  • src/assets/images/ic_comment_count.svg is excluded by !**/*.svg
  • src/assets/images/ic_comment_delete.svg is excluded by !**/*.svg
  • src/assets/images/ic_comment_file.svg is excluded by !**/*.svg
  • src/assets/images/ic_comment_send.svg is excluded by !**/*.svg
  • src/assets/images/ic_default_board_BE.svg is excluded by !**/*.svg
  • src/assets/images/ic_default_board_D.svg is excluded by !**/*.svg
  • src/assets/images/ic_default_board_FE.svg is excluded by !**/*.svg
  • src/assets/images/ic_default_board_PM.svg is excluded by !**/*.svg
  • src/assets/images/ic_download.svg is excluded by !**/*.svg
  • src/assets/images/ic_floating_writing.svg is excluded by !**/*.svg
  • src/assets/images/ic_folder.svg is excluded by !**/*.svg
  • src/assets/images/ic_header_check.svg is excluded by !**/*.svg
  • src/assets/images/ic_reply_comment.svg is excluded by !**/*.svg
  • src/assets/images/ic_study_close.svg is excluded by !**/*.svg
  • src/assets/images/ic_study_open.svg is excluded by !**/*.svg
  • src/assets/images/ic_study_refresh.svg is excluded by !**/*.svg
📒 Files selected for processing (57)
  • src/Layout/FixedLayout.tsx (3 hunks)
  • src/Layout/PostLayout.tsx (1 hunks)
  • src/Layout/ResponsiveLayout.tsx (1 hunks)
  • src/components/Board/Comment.tsx (1 hunks)
  • src/components/Board/CommentInput.tsx (4 hunks)
  • src/components/Board/ContentPost.tsx (2 hunks)
  • src/components/Board/EduMaterial/SlideEdu.tsx (1 hunks)
  • src/components/Board/EduPartTap.tsx (1 hunks)
  • src/components/Board/EduWrite.tsx (1 hunks)
  • src/components/Board/ExpandableTagList.tsx (4 hunks)
  • src/components/Board/MarkdownTap.tsx (2 hunks)
  • src/components/Board/NoticeWrite.tsx (1 hunks)
  • src/components/Board/Notices/NoticesDetailMain.tsx (1 hunks)
  • src/components/Board/PartBoard.tsx (4 hunks)
  • src/components/Board/PartBoardTap.tsx (1 hunks)
  • src/components/Board/PostDetailMain.tsx (3 hunks)
  • src/components/Board/PostFile.tsx (2 hunks)
  • src/components/Board/ReplyComment.tsx (3 hunks)
  • src/components/Board/SlideNotice.tsx (2 hunks)
  • src/components/Board/StudyBoardSearch.tsx (4 hunks)
  • src/components/Board/StudyLogListItem.tsx (1 hunks)
  • src/components/Board/StudyPostTitle.tsx (2 hunks)
  • src/components/Board/StudyTag.tsx (2 hunks)
  • src/components/Board/StudyWriteTemplate.tsx (1 hunks)
  • src/components/Board/WeekTag.tsx (2 hunks)
  • src/components/Navigation/DesktopGNB.tsx (2 hunks)
  • src/components/Navigation/EditGNB.tsx (2 hunks)
  • src/components/Navigation/HeaderButton.tsx (1 hunks)
  • src/components/common/MenuModal.tsx (2 hunks)
  • src/pages/Layout.tsx (0 hunks)
  • src/pages/Receipt.tsx (1 hunks)
  • src/pages/attend/Attendance.tsx (1 hunks)
  • src/pages/board/Board.tsx (3 hunks)
  • src/pages/board/education/EduDetail.tsx (2 hunks)
  • src/pages/board/education/EduEdit.tsx (3 hunks)
  • src/pages/board/education/EduPost.tsx (3 hunks)
  • src/pages/board/education/EducationBoard.tsx (3 hunks)
  • src/pages/board/notices/BoardNotice.tsx (5 hunks)
  • src/pages/board/notices/NoticeDetail.tsx (2 hunks)
  • src/pages/board/notices/NoticeEdit.tsx (3 hunks)
  • src/pages/board/notices/NoticePost.tsx (3 hunks)
  • src/pages/board/part/PartBoard.tsx (6 hunks)
  • src/pages/board/part/PartDetail.tsx (2 hunks)
  • src/pages/board/part/PartEdit.tsx (3 hunks)
  • src/pages/board/part/PartPost.tsx (3 hunks)
  • src/routes/route.tsx (2 hunks)
  • src/styles/board/Board.styled.ts (10 hunks)
  • src/styles/board/Comment.styled.ts (6 hunks)
  • src/styles/board/Dropdown.styled.ts (3 hunks)
  • src/styles/board/Markdown.styled.ts (3 hunks)
  • src/styles/board/PartBoard.styled.ts (3 hunks)
  • src/styles/board/PostDetail.styled.ts (4 hunks)
  • src/styles/board/PostFile.styled.ts (3 hunks)
  • src/styles/board/StudyLogListItem.styled.ts (4 hunks)
  • src/styles/breadCrum/index.ts (1 hunks)
  • src/styles/dues/Dues.styled.ts (1 hunks)
  • src/theme/typography.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • src/pages/Layout.tsx
🧰 Additional context used
🧬 Code graph analysis (38)
src/Layout/FixedLayout.tsx (1)
src/theme/designTokens.ts (1)
  • units (137-166)
src/styles/dues/Dues.styled.ts (1)
src/theme/designTokens.ts (1)
  • units (137-166)
src/styles/breadCrum/index.ts (1)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/pages/board/education/EduPost.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/pages/board/notices/NoticeDetail.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/styles/board/PostFile.styled.ts (1)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/components/Board/Notices/NoticesDetailMain.tsx (3)
src/api/useGetBoardDetail.tsx (1)
  • useGetBoardDetail (41-80)
src/components/common/ToastMessage.tsx (3)
  • toastInfo (76-76)
  • toastError (77-78)
  • toastSuccess (75-75)
src/components/Board/MarkdownLink.tsx (2)
  • MarkdownLink (28-28)
  • CustomCheckbox (28-28)
src/components/Board/CommentInput.tsx (1)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/styles/board/StudyLogListItem.styled.ts (1)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/pages/board/notices/NoticePost.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/StudyBoardSearch.tsx (2)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/styles/index.ts (1)
  • pcResponsive (6-10)
src/pages/board/part/PartDetail.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/StudyPostTitle.tsx (2)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/styles/board/BoardPost.styled.ts (1)
  • TitleInput (20-34)
src/pages/board/education/EduDetail.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/pages/board/education/EducationBoard.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/PartBoard.tsx (1)
src/styles/calendar/DatePicker.styled.ts (1)
  • Icon (20-22)
src/components/Board/StudyWriteTemplate.tsx (5)
src/styles/board/Board.styled.ts (1)
  • Container (7-15)
src/styles/board/Markdown.styled.ts (1)
  • Container (7-18)
src/styles/board/PartBoard.styled.ts (3)
  • Container (7-16)
  • InformationContainer (49-54)
  • DropdownContainer (56-59)
src/styles/index.ts (2)
  • MOBILE (4-4)
  • pcResponsive (6-10)
src/theme/designTokens.ts (1)
  • units (137-166)
src/Layout/PostLayout.tsx (1)
src/theme/designTokens.ts (1)
  • units (137-166)
src/components/Board/NoticeWrite.tsx (2)
src/components/Board/StudyWriteTemplate.tsx (2)
  • Container (14-22)
  • MarkdownContainer (43-52)
src/theme/designTokens.ts (1)
  • units (137-166)
src/pages/board/notices/NoticeEdit.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/styles/board/Comment.styled.ts (1)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/pages/attend/Attendance.tsx (1)
src/theme/designTokens.ts (1)
  • units (137-166)
src/styles/board/Markdown.styled.ts (1)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/components/Navigation/HeaderButton.tsx (2)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/styles/board/Comment.styled.ts (1)
  • ButtonContainer (42-47)
src/pages/board/part/PartPost.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/WeekTag.tsx (1)
src/theme/designTokens.ts (1)
  • colors (2-135)
src/pages/board/part/PartBoard.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/ExpandableTagList.tsx (1)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/pages/board/notices/BoardNotice.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/pages/Receipt.tsx (1)
src/theme/designTokens.ts (1)
  • units (137-166)
src/components/Navigation/EditGNB.tsx (1)
src/styles/navigation/Navigation.styled.ts (1)
  • Logo (174-178)
src/styles/board/PostDetail.styled.ts (4)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/styles/board/Comment.styled.ts (2)
  • DateText (83-86)
  • PositionIcon (57-61)
src/styles/board/Board.styled.ts (1)
  • PositionIcon (203-207)
src/styles/mypage/Mypage.styled.ts (1)
  • TextButton (11-21)
src/styles/board/Dropdown.styled.ts (1)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/pages/board/part/PartEdit.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/components/Board/ContentPost.tsx (1)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/components/Board/SlideNotice.tsx (2)
src/hooks/formatDate.tsx (1)
  • formatDate (10-12)
src/styles/board/PostListItem.styled.ts (1)
  • FileIcon (84-86)
src/pages/board/education/EduEdit.tsx (1)
src/styles/breadCrum/index.ts (1)
  • CrumbButton (12-14)
src/styles/board/PartBoard.styled.ts (3)
src/theme/designTokens.ts (2)
  • colors (2-135)
  • units (137-166)
src/components/Board/StudyWriteTemplate.tsx (1)
  • InformationContainer (24-28)
src/styles/board/Board.styled.ts (1)
  • FloatingButton (252-266)
🪛 Biome (2.1.2)
src/components/Board/Notices/NoticesDetailMain.tsx

[error] 74-74: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 76-78: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 102-102: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

src/components/Board/PostDetailMain.tsx

[error] 81-81: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 83-85: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 109-109: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

🔇 Additional comments (38)
src/pages/attend/Attendance.tsx (1)

15-15: 디자인 토큰 적용이 올바르게 되었습니다!

units.padding['450'] 토큰을 사용하여 일관된 스타일링을 적용한 점이 좋습니다. PR의 목표인 디자인 토큰 적용과 잘 부합합니다.

src/components/Board/EduMaterial/SlideEdu.tsx (1)

57-57: S.Divider component is properly defined and used correctly.

The S.Divider component is exported from @/styles/board/Board.styled.ts (line 217) as a styled span with appropriate styling (margin: 0 5px; width: 1px). The conditional rendering at line 57 is correctly implemented, using the same edu.hasFile condition as the adjacent FileIcon component, ensuring consistency. This change successfully applies design tokens to replace the text-based separator.

src/components/Board/PostFile.tsx (2)

1-3: LGTM! SVG를 React 컴포넌트로 올바르게 변환했습니다.

SVGR을 활용한 ?react 쿼리 파라미터를 통한 SVG 임포트는 vite-plugin-svgr의 표준 패턴이며, 디자인 토큰 마이그레이션과 잘 맞습니다.


32-38: LGTM! 아이콘 컴포넌트를 직접 렌더링하는 방식으로 깔끔하게 개선되었습니다.

styled-component 래퍼 대신 React 컴포넌트를 직접 사용하는 방식으로 변경되어 코드가 더 간결해졌습니다. 조건부 렌더링 로직도 올바르게 유지되고 있습니다.

src/styles/board/PostFile.styled.ts (2)

1-2: LGTM! 디자인 토큰과 타이포그래피 임포트가 올바르게 적용되었습니다.

기존 theme 임포트에서 디자인 토큰 기반 임포트로 전환이 잘 이루어졌습니다.


26-27: LGTM! 타이포그래피와 색상 토큰이 올바르게 적용되었습니다.

typography.Button2colors.semantic.text.normal을 사용하여 일관된 디자인 시스템을 따르고 있습니다.

src/components/Board/ReplyComment.tsx (1)

87-87: No changes needed. The SVG component (ic_reply.svg) has intrinsic dimensions defined (width="20" height="20"), which match the removed styled wrapper's dimensions exactly. Removing the S.ReplyArrow wrapper does not cause visual regression since the styling is already present in the SVG itself.

src/components/Board/StudyTag.tsx (1)

1-17: 디자인 토큰 마이그레이션이 올바르게 적용되었습니다.

theme 기반 스타일링을 design token으로 완전히 전환했으며, 모든 변경사항이 디자인 시스템과 일관성 있게 적용되었습니다. 로직 변경 없이 스타일링만 업데이트되어 안전합니다.

src/components/Board/ExpandableTagList.tsx (6)

10-11: 디자인 토큰 import가 올바르게 추가되었습니다.

필요한 design token 모듈들이 정확하게 import 되었습니다.


15-15: gap 값 변경을 확인해 주세요.

간격이 5px에서 units.margin['100']px (4px)로 변경되었습니다. 디자인 시스템의 표준 간격을 따르는 것은 좋지만, 이 1px 감소가 의도된 변경인지 확인이 필요합니다.

Also applies to: 22-22


33-48: 디자인 토큰 적용이 정확하게 완료되었습니다.

모든 스타일링이 design token 기반으로 올바르게 전환되었습니다:

  • 색상: semantic color tokens 사용
  • 간격: units.padding tokens 사용
  • 타이포그래피: typography.Button2 mixin 적용

$refresh prop이 제거되었는데, 이는 새로고침 기능 제거와 관련된 의도적인 변경으로 보입니다.


50-61: 버튼 스타일링이 디자인 토큰으로 올바르게 전환되었습니다.

배경색과 border radius가 semantic token과 units token을 적절히 사용하고 있습니다.


80-92: useEffect 의존성 배열이 올바르게 단순화되었습니다.

isRefreshing 상태 제거에 따라 의존성 배열이 [part]만 포함하도록 정리되었습니다. 이제 컴포넌트는 마운트 시와 part 변경 시에만 스터디 목록을 가져옵니다.


63-68: No action required. The onRefresh removal has been properly implemented. The component's only usage in PartBoard.tsx passes only selectedTag and onSelectTag props, which match the updated StudyTagProps interface exactly. No instances of onRefresh prop being passed to ExpandableTagList exist in the codebase.

src/styles/dues/Dues.styled.ts (1)

13-13: 디자인 토큰 기반 패딩 적용 좋습니다

기존 레이아웃을 유지하면서 좌우 여백만 units.padding['450']으로 맞춰서, 다른 보드 영역과 토큰 일관성이 잘 맞는 것 같습니다.

src/components/Board/WeekTag.tsx (1)

1-2: WeekTag의 스타일이 디자인 토큰에 잘 맞춰졌습니다

배경을 colors.semantic.container.neutral, 텍스트를 colors.semantic.text.alternative로 바꾸고 typography.Caption1을 적용해서, 다른 보드 컴포넌트들과 톤이 잘 맞는 것 같습니다. 별다른 기능적 리스크는 없어 보입니다.

Also applies to: 12-16

src/pages/board/notices/BoardNotice.tsx (2)

1-3: 아이콘 import 확인 완료

Vite의 ?react suffix를 사용한 SVG 컴포넌트 import가 적절합니다.


115-120: LGTM!

네비게이션 핸들러가 간결하고 명확하게 구현되었습니다.

src/components/Board/CommentInput.tsx (3)

38-42: 디자인 토큰 적용 확인 완료

border-radius, padding, background, color, typography가 모두 디자인 토큰으로 올바르게 마이그레이션되었습니다.


52-53: SendButton 크기 증가 확인

32x32에서 40x40으로 크기가 증가하여 터치 타겟이 개선되었습니다. WCAG 권장 최소 터치 타겟 크기(44x44)에 근접합니다.


144-153: Fragment 사용으로 간소화

FileContainer styled component 제거 후 React Fragment로 대체되었습니다. DOM 노드가 줄어들어 적절한 변경입니다.

src/components/Board/Notices/NoticesDetailMain.tsx (1)

168-178: ReactMarkdown 설정 적절함

rehypeRaw, remarkGfm, remarkBreaks 플러그인 조합이 적절하며, 커스텀 컴포넌트(MarkdownLink, CustomCheckbox)가 올바르게 연결되었습니다.

src/styles/board/Board.styled.ts (1)

252-266: FloatingButton 구현 확인

새로운 FloatingButton 컴포넌트가 적절하게 구현되었습니다. z-index: 1000은 모달 등과 충돌하지 않도록 z-index 관리 체계 확인이 필요할 수 있습니다.

src/components/Board/PartBoardTap.tsx (1)

10-19: 탭 구조 단순화와 isActive 기반 스타일링이 명확하게 잘 적용되었습니다.

DOM 깊이를 줄이고 TabTextisActive를 직접 넘겨 스타일을 분리한 구조라 유지보수에 유리해 보입니다. 별도 수정 없이 이대로 사용해도 좋겠습니다.

src/components/Board/Comment.tsx (1)

99-117: 댓글 상단으로 정보와 버튼을 묶어올린 구조 변경은 무리 없이 보입니다.

S.CommentTop 안에 NameText와 버튼 컨테이너를 함께 배치해서 헤더 영역을 명확히 분리한 점이 읽기 좋고, onClickReply/onClickMenu 등 기존 동작도 그대로 유지되고 있습니다. 실제 디자인 시안 기준으로 모바일에서 아이콘 간 간격과 터치 영역만 한 번 확인해 보시면 될 것 같습니다.

src/components/Board/StudyPostTitle.tsx (1)

2-3: 제목 영역의 디자인 토큰/타이포그래피 적용이 일관되게 잘 반영되었습니다.

TitleWrapper의 여백, Title의 Caption1 사용, TitleInput의 높이·라운드·배경/텍스트/플레이스홀더 색을 모두 semantic 토큰과 typography.Body1로 맞춰서, 기존 테마 의존성을 깔끔하게 줄인 것 같습니다. 현재 형태 그대로 사용하셔도 무방해 보입니다.

Also applies to: 9-10, 19-20, 24-24, 27-32, 36-36

src/Layout/ResponsiveLayout.tsx (1)

14-27: LGTM! Wrapper와 Content export 변경 승인

WrapperContent를 export하여 PostLayout 등 다른 레이아웃 컴포넌트에서 재사용할 수 있게 한 점이 좋습니다. 디자인 토큰 시스템과 일관된 반응형 레이아웃 구성에 적합한 구조입니다.

src/components/Navigation/EditGNB.tsx (1)

24-36: LGTM! location 의존성 제거 및 명시적 prop 사용

useLocation 기반의 isBoard 로직을 제거하고 명시적인 save prop으로 전환한 것이 좋습니다. 컴포넌트가 더 순수해지고, 부모 컴포넌트에서 동작을 명확하게 제어할 수 있습니다.

src/components/Board/EduPartTap.tsx (1)

14-21: LGTM! 깔끔한 탭 컴포넌트 리팩토링

isActive prop을 통한 상태 기반 스타일링으로 전환한 점이 좋습니다. 디자인 토큰 시스템과 일관된 구조이며, PartBoardTap.tsx의 패턴과도 일치합니다.

src/components/Navigation/HeaderButton.tsx (1)

21-31: LGTM! prop 이름 변경 승인

isBoard에서 save로 변경하여 prop의 의도가 더 명확해졌습니다. 저장 동작(save=true)과 게시 동작(save=false)을 직관적으로 구분할 수 있습니다.

src/components/Board/StudyWriteTemplate.tsx (1)

14-52: LGTM!

디자인 토큰 적용과 styled component 내보내기가 잘 구현되었습니다. unitstypography를 활용하여 일관된 스타일링을 적용한 점이 좋습니다.

src/routes/route.tsx (1)

123-138: EduEdit 라우트 경로 일관성 확인 필요

Line 136의 education/:part/:postId/edit 경로가 /board 하위가 아닌 루트 레벨의 상대 경로로 정의되어 있습니다. 반면 Line 119의 EduDetail/education/:part/:postId로 절대 경로를 사용합니다.

이 불일치로 인해 EduEdit 페이지 접근 시 예상치 못한 라우팅 문제가 발생할 수 있습니다.

의도한 경로가 맞는지 확인해주세요. 만약 /education/:part/:postId/edit이 의도한 경로라면:

-      { path: 'education/:part/:postId/edit', element: <EduEdit /> },
+      { path: '/education/:part/:postId/edit', element: <EduEdit /> },
src/styles/board/PartBoard.styled.ts (6)

4-5: 디자인 토큰 import가 올바르게 추가되었습니다.

색상, 간격, 타이포그래피를 위한 디자인 토큰 import가 적절하게 추가되어 토큰 기반 스타일링을 지원합니다.


23-25: 디자인 토큰 적용이 올바릅니다.

border, padding, gap에 디자인 토큰이 올바르게 적용되었습니다.


28-33: LGTM!

gap에 토큰 기반 간격이 올바르게 적용되었습니다.


35-41: LGTM!

isActive prop을 추가하여 조건부 스타일링을 구현하고 타이포그래피 및 색상 토큰을 올바르게 적용했습니다.


43-47: LGTM!

브랜드 primary 색상 토큰이 올바르게 적용되었고, 음수 마진을 사용한 border 오버랩 처리도 적절합니다.


49-54: LGTM!

padding과 gap에 디자인 토큰이 올바르게 적용되었습니다.

Comment on lines +2 to +3
import ReplyArrowImage from '@/assets/images/ic_reply.svg?react';
import MenuImage from '@/assets/images/ic_comment_delete.svg?react';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Verify the file exists and show the imports
if [ -f "src/components/Board/ReplyComment.tsx" ]; then
  echo "=== File exists: src/components/Board/ReplyComment.tsx ==="
  echo "=== Lines 1-10 (showing imports) ==="
  head -n 10 "src/components/Board/ReplyComment.tsx"
  echo ""
  echo "=== Lines 85-92 (showing line 87 usage) ==="
  sed -n '85,92p' "src/components/Board/ReplyComment.tsx"
  echo ""
  echo "=== Lines 97-105 (showing line 99 usage) ==="
  sed -n '97,105p' "src/components/Board/ReplyComment.tsx"
else
  echo "File not found"
fi

echo ""
echo "=== Checking for Vite configuration ==="
fd -type f \( -name "vite.config.*" -o -name "vitest.config.*" \) --max-results 5 -exec echo "Found: {}" \; -exec cat {} \;

Repository: Leets-Makers/Weeth-FE

Length of output: 1556


🏁 Script executed:

# Check for Vite configuration files with corrected command
echo "=== Searching for Vite configuration ==="
find . -maxdepth 2 -name "vite.config.*" -o -name "vitest.config.*" 2>/dev/null | head -10

# Also check package.json for Vite plugins
echo ""
echo "=== Checking package.json for Vite plugins ==="
if [ -f "package.json" ]; then
  cat package.json | jq '.devDependencies | select(. != null) | keys[] | select(contains("vite") or contains("svg"))' 2>/dev/null
  echo ""
  echo "=== Full devDependencies section ==="
  cat package.json | jq '.devDependencies' 2>/dev/null | head -30
fi

Repository: Leets-Makers/Weeth-FE

Length of output: 1218


🏁 Script executed:

# Check vite.config.js to verify svgr plugin is configured
echo "=== Content of vite.config.js ==="
cat vite.config.js

# Check the styled components file to understand styling
echo ""
echo "=== Checking Comment.styled.ts for ReplyArrow styling ==="
if [ -f "src/styles/board/Comment.styled.ts" ]; then
  cat "src/styles/board/Comment.styled.ts" | head -150
fi

Repository: Leets-Makers/Weeth-FE

Length of output: 3771


ReplyArrowImage component at line 87 loses critical styling

The SVG import change is properly supported by the vite.config.js which correctly configures vite-plugin-svgr, so the ?react imports will work. However, line 87 introduces a styling issue:

Previously: <S.ReplyArrow src={...} /> — applied width: 20px; height: 20px; styling
Now: <ReplyArrowImage /> — renders at natural SVG dimensions without size constraints

The S.ReplyArrow styled component defined in Comment.styled.ts sets explicit dimensions (20px × 20px) that are now lost. Verify that the SVG renders at the intended size, or apply inline styles / CSS classes to maintain the 20px × 20px dimensions. The MenuImage change at line 99 is safe since it remains wrapped in S.ReplyImageButton.

🤖 Prompt for AI Agents
In src/components/Board/ReplyComment.tsx around lines 2-3 and ~87, the SVG was
changed from a styled-image component to a raw React SVG component which loses
the S.ReplyArrow 20×20 sizing; restore the explicit 20px×20 dimensions by either
rendering the imported component with width and height props (e.g.
<ReplyArrowImage width={20} height={20} />) or by applying the existing
S.ReplyArrow styled wrapper/className to the SVG (e.g. <S.ReplyArrow
as={ReplyArrowImage} /> or <ReplyArrowImage className={S.ReplyArrowClass} />) so
the icon renders at the intended size; do not change the MenuImage usage at line
99.

Comment on lines +4 to 5
import FileIcon from '@/assets/images/ic_file.svg?react';
import { useRef, useEffect } from 'react';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "SlideNotice.tsx" | head -5

Repository: Leets-Makers/Weeth-FE

Length of output: 103


🏁 Script executed:

fd SlideNotice.tsx

Repository: Leets-Makers/Weeth-FE

Length of output: 101


🏁 Script executed:

cat -n src/components/Board/SlideNotice.tsx

Repository: Leets-Makers/Weeth-FE

Length of output: 3713


🏁 Script executed:

# Find Notice interface definition
rg -A 10 "interface Notice" --type ts --type tsx | head -30

Repository: Leets-Makers/Weeth-FE

Length of output: 92


🏁 Script executed:

# Find setPositionIcon and formatDate implementations
rg "setPositionIcon|formatDate" --type ts --type tsx -B 2 -A 5 | head -50

Repository: Leets-Makers/Weeth-FE

Length of output: 92


🏁 Script executed:

fd setPositionIcon -type f | head -10

Repository: Leets-Makers/Weeth-FE

Length of output: 235


🏁 Script executed:

fd formatDate -type f | head -10

Repository: Leets-Makers/Weeth-FE

Length of output: 235


🏁 Script executed:

# Search for setPositionIcon implementation
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -exec grep -l "setPositionIcon" {} \; | head -10

Repository: Leets-Makers/Weeth-FE

Length of output: 389


🏁 Script executed:

# Search for formatDate implementation
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" \) -exec grep -l "formatDate" {} \; | head -10

Repository: Leets-Makers/Weeth-FE

Length of output: 915


🏁 Script executed:

cat -n src/hooks/setPositionIcon.tsx

Repository: Leets-Makers/Weeth-FE

Length of output: 737


🏁 Script executed:

# Search for formatDate function definition
grep -r "export.*formatDate\|function formatDate\|const formatDate" --include="*.ts" --include="*.tsx" | head -20

Repository: Leets-Makers/Weeth-FE

Length of output: 833


🏁 Script executed:

cat -n src/hooks/formatDate.tsx

Repository: Leets-Makers/Weeth-FE

Length of output: 1280


formatDate 함수에 입력값 검증 추가 고려

SlideNotice.tsx의 하단 정보 행에서 작성자, 포지션, 시간, 파일 여부를 표시하는 UI는 좋으나, formatDate(notice.time) 호출 시 입력값 검증이 부족합니다.

src/hooks/formatDate.tsxformatDate 함수는 현재 빈 문자열이나 invalid 날짜 문자열에 대한 처리가 없어서, API 응답에서 time 필드가 비어있거나 잘못된 형식으로 내려올 경우 예상치 못한 출력(예: "Invalid date" 또는 "NaN/NaN")이 발생할 수 있습니다.

반면 setPositionIcon은 이미 폴백 로직(|| FEIcon)으로 undefined position을 처리하고 있으므로, formatDate에서도 유사하게:

  • 빈 문자열이나 null/undefined에 대한 기본값(예: "-") 반환
  • dayjs 파싱 실패 시 대체 텍스트 표시

등의 방어 로직을 추가하는 것을 권장합니다.

Comment on lines +4 to +5
import { colors, units } from '@/theme/designTokens';
import typography from '@/theme/typography';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

디자인 토큰 적용은 잘 되었지만, 남은 theme 기반 색상과 &table 오타를 정리하면 좋겠습니다

Container, MarkdownTapContainer, PreviewContainer, PreviewWrappercolors/units/typography를 적용한 부분은 일관성 측면에서 좋아 보입니다. 다만 같은 컴포넌트 안에서 여전히 theme.color.gray[30], theme.color.main 등을 병행 사용하고 있어(코드 블록, 표 헤더, 인용구 등), 나중에 테마를 조정할 때 색상 체계가 분리될 가능성이 있습니다. 급한 건 아니지만, 차후에 이 부분도 designTokens로 단계적으로 옮겨가는 걸 고려하시면 전체 보드 스타일이 더 정돈될 것 같습니다.

또 191~194라인의 &table 셀렉터는 & table의 오타처럼 보여 현재는 어떤 요소에도 매칭되지 않습니다. 필요 없는 코드라면 제거하고, 의도한 것이라면 공백을 추가해 실제로 동작하도록 수정하는 편이 좋겠습니다.

Also applies to: 11-12, 20-33, 64-83, 191-194

Comment on lines +90 to 104
export const FloatingButton = styled.div`
position: fixed;
display: flex;
bottom: 0.9375rem;
justify-content: end;
width: 100%;
max-width: 23.4375rem;
z-index: 10;
right: 16px;
bottom: 24px;
width: 44px;
height: 45px;
cursor: pointer;
border: none;
z-index: 1000;
svg {
width: 44px;
height: 45px;
}
`;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

FloatingButton 중복 정의를 제거하고 통합해야 합니다.

src/styles/board/Board.styled.ts (lines 251-265)에 동일한 이름과 스타일을 가진 FloatingButton이 이미 존재합니다. 두 파일에서 같은 컴포넌트를 export하면 다음과 같은 문제가 발생합니다:

  • import 시 naming conflict 발생 가능
  • 중복 코드 유지보수 부담
  • 향후 수정 시 두 곳 모두 동기화 필요

한 곳에서만 FloatingButton을 정의하고 필요한 곳에서 import하여 사용하도록 리팩토링하세요.

관련 코드 위치: src/styles/board/Board.styled.ts lines 251-265

🤖 Prompt for AI Agents
In src/styles/board/PartBoard.styled.ts around lines 90 to 104 there is a
duplicate FloatingButton styled export that is already defined in
src/styles/board/Board.styled.ts (lines 251-265); remove the FloatingButton
definition from PartBoard.styled.ts, add an import for FloatingButton from
src/styles/board/Board.styled.ts, update any local references to use the
imported component, and ensure Board.styled.ts exports FloatingButton (export if
missing) so only one canonical definition exists and all usages import that
single export.

Copy link
Collaborator

@JIN921 JIN921 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!
말씀드린 사항들은 급한 수정 사항이 아니니 천천히 고쳐도 좋을 거 같아용!!

import MenuModal from '@/components/common/MenuModal';
import SelectModal from '@/components/Modal/SelectModal';

interface Comment {
Copy link
Collaborator

Choose a reason for hiding this comment

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

게시판에서 쓰이는 타입들은 중복되는 게 많을 거 같은데 따로 게시판 타입에 정리해서 사용하면 좋을 거 같습니다! 공통 타입 하나 두고 상속이랑 유틸리티 타입 써서 정의 할 수 있도록이요!

Copy link
Member Author

Choose a reason for hiding this comment

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

오호 네엡 이렇게 수정해볼게요!!

Copy link
Collaborator

@dalzzy dalzzy left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~!! 정말 많은 작업을 하셨네요,,👍🏻

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: 6

Caution

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

⚠️ Outside diff range comments (1)
src/pages/board/education/EducationBoard.tsx (1)

85-99: useEffect 클린업에서 stale ref 참조 가능성이 있습니다.

observerRef.current가 클린업 함수 실행 시점에 변경되었을 수 있습니다. 클로저 문제를 방지하려면 ref 값을 클린업 전에 캡처해야 합니다.

🔎 제안된 수정 사항
   useEffect(() => {
+    const currentRef = observerRef.current;
     const observer = new IntersectionObserver(
       (entries) => {
         if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) {
           fetchNextPage();
         }
       },
       { threshold: 0.1 },
     );

-    if (observerRef.current) observer.observe(observerRef.current);
+    if (currentRef) observer.observe(currentRef);
     return () => {
-      if (observerRef.current) observer.unobserve(observerRef.current);
+      if (currentRef) observer.unobserve(currentRef);
     };
   }, [hasNextPage, isFetchingNextPage, fetchNextPage]);
🤖 Fix all issues with AI agents
In @src/components/common/Breadcrumb.tsx:
- Around line 35-51: The fragment returned inside items.map is missing a key,
causing React key warnings; update the map callback (where items.map is used and
elements like BreadcrumArrowRightIcon, CrumbButton and the span are returned and
handleItemClick is called) to provide a stable key per item—either replace the
shorthand <>...</> with <React.Fragment key={...}> using a unique identifier
from the item (e.g., item.key or item.path or index as fallback) or wrap the
returned nodes in a keyed element; ensure the chosen key is unique and stable
across renders.

In @src/pages/board/part/PartBoard.tsx:
- Line 15: The file contains commented-out authentication logic (the import
useGetUserInfo and related canWrite usage in PartBoard) which must be resolved:
either remove the commented import and all commented canWrite-related blocks
entirely to clean up dead code, or restore them by uncommenting the import of
useGetUserInfo, re-enabling the canWrite derived value and the conditional
rendering/guards that rely on it (e.g., any UI buttons or write flows inside
PartBoard that check canWrite); ensure the restored code compiles by importing
the hook correctly and handling its loading/error states, or if removing, also
remove any now-unused variables/props referencing canWrite so there are no
linter/TS errors.
- Around line 233-235: The floating write button is rendered for all users;
restore and use the existing canWrite logic to conditionally render
S.FloatingButton (containing FloatingWritingIcon and using handleWriting) only
when the current user has write permission. Reintroduce or compute the canWrite
boolean used previously, then wrap the S.FloatingButton block with a conditional
check (e.g., if (canWrite) { ... }) so only authorized users see and can invoke
handleWriting.

In @src/pages/board/part/PartDetail.tsx:
- Around line 81-87: The Breadcrumb items array in PartDetail.tsx currently
hardcodes the study path as `/board/study/${part}`; update that entry to use the
route built from the category prop/variable instead, i.e., change the second
item's path from `/board/study/${part}` to `/board/${category}/${part}` so
Breadcrumb receives the correct dynamic path (refer to the Breadcrumb component
and the items array, and the part and category variables).

In @src/styles/board/Board.styled.ts:
- Around line 36-40: Remove the unused BreadCrumContainer styled-component
definition from Board.styled.ts (the redundant export that only sets
display/typography/color). Delete that block and any related import/export tied
solely to it so there are no stale references; ensure the codebase uses the
single, full-featured BreadCrumContainer implementation from the breadCrum
module (which includes the $hasTitle prop).

In @src/styles/board/BoardPost.styled.ts:
- Around line 6-9: Fix the CSS typo and missing semicolon in the styled block
inside BoardPost.styled.ts: change the property name "ustify-content" to
"justify-content" and add the missing semicolon after that declaration so the
style block (the container with display: flex; flex-direction: column;)
correctly applies alignment.
🧹 Nitpick comments (4)
src/pages/board/education/EducationBoard.tsx (2)

111-117: 주석 처리된 코드를 제거해 주세요.

기존 Header 컴포넌트가 주석 처리된 채로 남아 있습니다. 더 이상 사용하지 않는 코드는 버전 관리 시스템에서 추적 가능하므로 완전히 제거하는 것이 좋습니다.

🔎 제안된 수정 사항
-      {/* <Header
-        isAccessible={isAdmin}
-        RightButtonType="WRITING"
-        onClickRightButton={handleRightButton}
-      >
-        교육자료
-      </Header> */}
       <Breadcrumb

175-179: FloatingWritingIcon 접근성 개선을 고려해 주세요.

SVG 아이콘에 직접 onClick이 적용되어 있어 키보드 접근성이 부족할 수 있습니다. S.FloatingButton이 버튼 역할을 하지 않는다면, button 요소로 감싸거나 role="button", tabIndex, aria-label 등을 추가하는 것이 좋습니다.

🔎 제안된 수정 사항
       {isAdmin && (
         <S.FloatingButton>
-          <FloatingWritingIcon onClick={handleWriting} />
+          <button
+            type="button"
+            onClick={handleWriting}
+            aria-label="교육자료 작성"
+          >
+            <FloatingWritingIcon />
+          </button>
         </S.FloatingButton>
       )}
src/components/Board/PostDetailMain.tsx (1)

65-68: 불필요한 삼항 연산자 (dead code)

numericPostId ? 'board' : 'board'는 조건과 관계없이 항상 'board'를 반환합니다. 의도한 로직인지 확인이 필요합니다. 만약 type 변수를 사용하려 했다면 수정이 필요합니다.

🔎 수정 제안
   const { boardDetailInfo } = useGetBoardDetail(
-    numericPostId ? 'board' : 'board',
+    'board',
     numericPostId ?? 0,
   );

또는 type 변수를 사용하려 했다면:

   const { boardDetailInfo } = useGetBoardDetail(
-    numericPostId ? 'board' : 'board',
+    type,
     numericPostId ?? 0,
   );
src/pages/board/notices/BoardNotice.tsx (1)

164-168: onClick을 컨테이너로 이동하면 클릭 영역이 넓어집니다

현재 onClickFloatingWritingIcon에 있어 아이콘 영역만 클릭 가능합니다. S.FloatingButtononClick을 적용하면 터치/클릭 영역이 넓어져 사용성이 향상됩니다.

🔎 수정 제안
       {isAdmin && (
-        <S.FloatingButton>
-          <FloatingWritingIcon onClick={handleWriting} />
+        <S.FloatingButton onClick={handleWriting}>
+          <FloatingWritingIcon />
         </S.FloatingButton>
       )}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67b07df and 6c66d54.

⛔ Files ignored due to path filters (4)
  • src/assets/images/ic_pc_avatar_be.svg is excluded by !**/*.svg
  • src/assets/images/ic_pc_avatar_d.svg is excluded by !**/*.svg
  • src/assets/images/ic_pc_avatar_fe.svg is excluded by !**/*.svg
  • src/assets/images/ic_pc_avatar_pm.svg is excluded by !**/*.svg
📒 Files selected for processing (23)
  • src/Layout/FixedLayout.tsx
  • src/components/Board/Notices/NoticesDetailMain.tsx
  • src/components/Board/PartBoard.tsx
  • src/components/Board/PostDetailMain.tsx
  • src/components/common/Breadcrumb.tsx
  • src/pages/board/Board.tsx
  • src/pages/board/education/EduDetail.tsx
  • src/pages/board/education/EduEdit.tsx
  • src/pages/board/education/EduPost.tsx
  • src/pages/board/education/EducationBoard.tsx
  • src/pages/board/notices/BoardNotice.tsx
  • src/pages/board/notices/NoticeDetail.tsx
  • src/pages/board/notices/NoticeEdit.tsx
  • src/pages/board/notices/NoticePost.tsx
  • src/pages/board/part/PartBoard.tsx
  • src/pages/board/part/PartDetail.tsx
  • src/pages/board/part/PartEdit.tsx
  • src/pages/board/part/PartPost.tsx
  • src/styles/board/Board.styled.ts
  • src/styles/board/BoardPost.styled.ts
  • src/styles/board/PartBoard.styled.ts
  • src/styles/board/PostDetail.styled.ts
  • src/styles/breadCrum/index.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/pages/board/part/PartEdit.tsx
  • src/styles/breadCrum/index.ts
  • src/Layout/FixedLayout.tsx
  • src/pages/board/part/PartPost.tsx
  • src/pages/board/notices/NoticePost.tsx
  • src/pages/board/education/EduPost.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
src/components/common/Breadcrumb.tsx (1)
src/styles/breadCrum/index.ts (2)
  • BreadCrumContainer (5-11)
  • CrumbButton (13-15)
src/components/Board/PostDetailMain.tsx (4)
src/api/useGetBoardDetail.tsx (1)
  • useGetBoardDetail (41-80)
src/hooks/formatDate.tsx (1)
  • formatDateTime (14-18)
src/components/common/ToastMessage.tsx (2)
  • toastInfo (76-76)
  • toastError (77-78)
src/components/Board/MarkdownLink.tsx (2)
  • MarkdownLink (28-28)
  • CustomCheckbox (28-28)
src/components/Board/Notices/NoticesDetailMain.tsx (4)
src/api/useGetBoardDetail.tsx (1)
  • useGetBoardDetail (41-80)
src/hooks/formatDate.tsx (1)
  • formatDateTime (14-18)
src/components/common/ToastMessage.tsx (3)
  • toastSuccess (75-75)
  • toastError (77-78)
  • toastInfo (76-76)
src/components/Board/MarkdownLink.tsx (2)
  • MarkdownLink (28-28)
  • CustomCheckbox (28-28)
src/styles/board/PostDetail.styled.ts (4)
src/theme/designTokens.ts (2)
  • units (137-166)
  • colors (2-135)
src/styles/board/Comment.styled.ts (2)
  • DateText (83-86)
  • PositionIcon (57-61)
src/styles/board/PostListItem.styled.ts (2)
  • DateText (66-69)
  • PositionIcon (88-91)
src/styles/board/BoardDetail.styled.ts (1)
  • TextButton (31-39)
🔇 Additional comments (13)
src/pages/board/education/EducationBoard.tsx (2)

118-123: Breadcrumb 구현이 적절합니다.

디자인 토큰 적용과 함께 브레드크럼 네비게이션이 잘 구현되었습니다. 경로와 레이블이 명확하게 설정되어 있습니다.


105-107: handleWriting 함수가 간결하게 구현되었습니다.

관리자 전용 글쓰기 기능으로의 네비게이션이 명확합니다.

src/pages/board/notices/NoticeEdit.tsx (1)

87-107: LGTM!

EditGNBBreadcrumb 컴포넌트 통합이 잘 구현되었습니다. 저장 로직의 유효성 검사 및 에러 처리도 적절합니다.

src/components/Board/PartBoard.tsx (1)

74-102: LGTM!

반응형 아이콘 전환 로직과 키보드 접근성(onKeyDown으로 Enter/Space 지원)이 잘 구현되었습니다. pointerEvents: 'none' 적용으로 아이콘 클릭 이벤트 전파 문제도 방지됩니다.

src/components/Board/PostDetailMain.tsx (1)

57-69: Hooks 규칙 준수 확인

이전 리뷰에서 지적된 조건부 훅 호출 문제가 해결되었습니다. 모든 훅이 컴포넌트 최상단에서 조기 반환 이전에 호출되고 있어 React Hooks 규칙을 준수합니다.

src/styles/board/PostDetail.styled.ts (2)

23-27: LGTM!

이전 리뷰에서 지적된 CSS 오타(wdithwidth, colorscolor)가 수정되었습니다. 디자인 토큰과 타이포그래피 적용이 일관성 있게 구현되었습니다.


29-52: LGTM!

TitleContainerKebabIcon 컴포넌트가 적절하게 추가되었습니다. flexbox 레이아웃과 cursor 스타일링이 올바르게 적용되었습니다.

src/pages/board/Board.tsx (1)

53-56: LGTM!

Breadcrumb 컴포넌트와 BoardTitle 섹션이 적절하게 통합되었습니다.

src/pages/board/notices/BoardNotice.tsx (1)

116-127: LGTM!

Breadcrumb 네비게이션과 검색 UI 분리가 잘 구현되었습니다. 관리자 전용 글쓰기 버튼 조건부 렌더링도 적절합니다.

src/pages/board/part/PartBoard.tsx (1)

161-166: 브레드크럼 구현이 적절합니다.

파트 게시판에 대한 브레드크럼 네비게이션이 잘 구현되었습니다. 경로 구조도 올바릅니다.

src/pages/board/notices/NoticeDetail.tsx (1)

73-82: 공지사항 상세 페이지 구조가 깔끔합니다.

브레드크럼을 통한 네비게이션 제공과 NoticesDetailMain을 사용한 공지사항 전용 컴포넌트 분리가 잘 되어 있습니다. 관심사의 분리 원칙을 잘 따르고 있습니다.

src/pages/board/education/EduEdit.tsx (1)

93-116: 레이아웃 구성이 깔끔합니다.

EditGNB, Breadcrumb, EduWrite를 조합한 구조가 PR 목표에 부합하며, 헤더, 네비게이션, 폼 컴포넌트의 책임이 명확히 분리되어 있습니다.

src/pages/board/education/EduDetail.tsx (1)

75-85: 교육자료 상세 페이지 구현이 일관성 있습니다.

브레드크럼 네비게이션과 PostDetailMain 컴포넌트 사용이 다른 상세 페이지들과 일관성 있게 구현되어 있습니다.

import Loading from '@/components/common/Loading';
import { SearchContent } from '@/types/search';
import useGetUserInfo from '@/api/useGetUserInfo';
// import useGetUserInfo from '@/api/useGetUserInfo';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

주석 처리된 인증 로직을 제거하거나 복원해야 합니다.

useGetUserInfocanWrite 로직이 주석 처리되어 있습니다. 이 코드가 더 이상 필요하지 않다면 제거하고, 임시로 주석 처리한 것이라면 복원해야 합니다. 주석 처리된 코드는 유지보수성을 저해합니다.

🔎 제안하는 조치

해당 코드가 더 이상 필요하지 않다면 완전히 제거:

-// import useGetUserInfo from '@/api/useGetUserInfo';
+import useGetUserInfo from '@/api/useGetUserInfo';
-  // const { userInfo } = useGetUserInfo();
+  const { userInfo } = useGetUserInfo();
-  // const canWrite = useMemo(() => {
-  //   if (!userInfo) return false;
-  //   if (userInfo.role === 'ADMIN') return true;
-
-  //   return part === 'ALL' || userInfo.position === part;
-  // }, [userInfo, part]);
+  const canWrite = useMemo(() => {
+    if (!userInfo) return false;
+    if (userInfo.role === 'ADMIN') return true;
+
+    return part === 'ALL' || userInfo.position === part;
+  }, [userInfo, part]);

Also applies to: 32-32, 56-61

🤖 Prompt for AI Agents
In @src/pages/board/part/PartBoard.tsx at line 15, The file contains
commented-out authentication logic (the import useGetUserInfo and related
canWrite usage in PartBoard) which must be resolved: either remove the commented
import and all commented canWrite-related blocks entirely to clean up dead code,
or restore them by uncommenting the import of useGetUserInfo, re-enabling the
canWrite derived value and the conditional rendering/guards that rely on it
(e.g., any UI buttons or write flows inside PartBoard that check canWrite);
ensure the restored code compiles by importing the hook correctly and handling
its loading/error states, or if removing, also remove any now-unused
variables/props referencing canWrite so there are no linter/TS errors.

Comment on lines +233 to +235
<S.FloatingButton>
<FloatingWritingIcon onClick={handleWriting} />
</S.FloatingButton>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

글쓰기 권한 체크가 누락되었습니다.

플로팅 버튼이 모든 사용자에게 표시되고 있습니다. 이전에 주석 처리된 canWrite 로직을 활용하여 권한이 있는 사용자에게만 버튼을 표시해야 합니다.

🔎 제안하는 수정사항

canWrite 로직을 복원한 후:

-      <S.FloatingButton>
-        <FloatingWritingIcon onClick={handleWriting} />
-      </S.FloatingButton>
+      {canWrite && (
+        <S.FloatingButton>
+          <FloatingWritingIcon onClick={handleWriting} />
+        </S.FloatingButton>
+      )}
🤖 Prompt for AI Agents
In @src/pages/board/part/PartBoard.tsx around lines 233 - 235, The floating
write button is rendered for all users; restore and use the existing canWrite
logic to conditionally render S.FloatingButton (containing FloatingWritingIcon
and using handleWriting) only when the current user has write permission.
Reintroduce or compute the canWrite boolean used previously, then wrap the
S.FloatingButton block with a conditional check (e.g., if (canWrite) { ... }) so
only authorized users see and can invoke handleWriting.

Comment on lines +81 to +87
<Breadcrumb
items={[
{ label: '게시판', path: '/board' },
{ label: `${part} 파트게시판`, path: `/board/study/${part}` },
{ label: '게시판상세' },
]}
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

브레드크럼 경로가 여전히 하드코딩되어 있습니다.

이전 리뷰에서 지적된 것처럼, 84번 라인에서 경로를 /board/study/${part}로 하드코딩하고 있습니다. category 파라미터를 사용하여 /board/${category}/${part}로 수정해야 합니다.

🔎 제안하는 수정사항
         <Breadcrumb
           items={[
             { label: '게시판', path: '/board' },
-            { label: `${part} 파트게시판`, path: `/board/study/${part}` },
+            { label: `${part} 파트게시판`, path: `/board/${category}/${part}` },
             { label: '게시판상세' },
           ]}
         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Breadcrumb
items={[
{ label: '게시판', path: '/board' },
{ label: `${part} 파트게시판`, path: `/board/study/${part}` },
{ label: '게시판상세' },
]}
/>
<Breadcrumb
items={[
{ label: '게시판', path: '/board' },
{ label: `${part} 파트게시판`, path: `/board/${category}/${part}` },
{ label: '게시판상세' },
]}
/>
🤖 Prompt for AI Agents
In @src/pages/board/part/PartDetail.tsx around lines 81 - 87, The Breadcrumb
items array in PartDetail.tsx currently hardcodes the study path as
`/board/study/${part}`; update that entry to use the route built from the
category prop/variable instead, i.e., change the second item's path from
`/board/study/${part}` to `/board/${category}/${part}` so Breadcrumb receives
the correct dynamic path (refer to the Breadcrumb component and the items array,
and the part and category variables).

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: 0

Caution

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

⚠️ Outside diff range comments (1)
src/components/Navigation/DesktopGNB.tsx (1)

30-31: 타입 불일치로 인한 런타임 오류 발생

16번 라인에서 menu.path가 배열 ['/board', '/education']로 변경되었지만, 30-31번 라인의 로직은 여전히 menu.path를 문자열로 취급하고 있습니다:

  • 30번 라인: startsWith()는 문자열 메서드이므로 배열을 받으면 실패합니다
  • 31번 라인: navigate()는 문자열 경로를 기대하므로 배열을 받으면 실패합니다

이는 '게시판' 메뉴 클릭 시 내비게이션이 작동하지 않고, 활성 상태 표시도 제대로 동작하지 않는 문제를 야기합니다.

🔎 배열 경로를 처리하도록 로직 수정
  {menus.map((menu) => (
    <S.GNBMenuItem
      key={menu.name}
-     $active={location.pathname.startsWith(menu.path)}
-     onClick={() => navigate(menu.path)}
+     $active={
+       Array.isArray(menu.path)
+         ? menu.path.some((p) => location.pathname.startsWith(p))
+         : location.pathname.startsWith(menu.path)
+     }
+     onClick={() =>
+       navigate(Array.isArray(menu.path) ? menu.path[0] : menu.path)
+     }
    >
      {menu.name}
    </S.GNBMenuItem>
  ))}
🧹 Nitpick comments (1)
src/components/Navigation/DesktopGNB.tsx (1)

14-20: 메뉴 아이템 타입 명시 권장

menu.path가 이제 문자열 또는 배열이 될 수 있으므로, TypeScript 타입을 명시적으로 정의하면 타입 안전성이 향상됩니다.

🔎 명시적 타입 정의 추가
+type MenuItem = {
+  name: string;
+  path: string | string[];
+};
+
-const menus = [
+const menus: MenuItem[] = [
  { name: '동아리일정', path: '/calendar' },
  { name: '게시판', path: ['/board', '/education'] },
  { name: '출석', path: '/attendance' },
  { name: '멤버', path: '/member' },
  { name: '회비', path: '/dues' },
];
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c66d54 and 6017c9c.

📒 Files selected for processing (3)
  • src/components/Navigation/DesktopGNB.tsx
  • src/routes/route.tsx
  • src/styles/dues/Dues.styled.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/styles/dues/Dues.styled.ts
🔇 Additional comments (2)
src/routes/route.tsx (2)

123-138: 레이아웃 분리 구조 확인 완료

게시글 작성/수정을 위한 별도의 PostLayout 블록 구성이 적절합니다:

  • 인증이 필요한 작성/수정 기능을 PrivateRoute로 보호
  • PR 목표에 부합하는 게시/수정 헤더 레이아웃 분리
  • 조회 페이지(ResponsiveLayout)와 명확히 구분된 구조

레이아웃 분리 전략이 깔끔하게 구현되었습니다.


136-136: 교육 라우트 경로의 설계는 의도적이며 일관성이 있습니다

검토 결과 136번 라인의 /education/:part/:postId/edit 경로는 실제로 일관성 있는 구조입니다:

  • /board/education/:part - 교육 게시판 목록 (보기/생성 기능)
  • /education/:part/:postId - 특정 교육 게시글 보기
  • /education/:part/:postId/edit - 특정 교육 게시글 편집

PostDetailMain.tsx에서 실제로 /education/${part}/${postId}/edit로 네비게이션하고 있으며, 라우트 정의가 정확히 일치합니다. 게시판 작업(목록 조회, 생성)은 /board/ 접두사를 사용하고, 특정 콘텐츠에 대한 직접 작업(보기, 편집)은 루트 경로를 사용하는 의도적인 패턴으로, 이는 전체 라우팅 구조 내에서 일관성 있게 적용되고 있습니다.

@woneeeee woneeeee merged commit bda21e8 into develop Jan 7, 2026
1 check was pending
@woneeeee woneeeee deleted the feat/WTH-54-Weeth-디자인-토큰-적용 branch January 7, 2026 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Design CSS 스타일링 🐞 Fix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants