Skip to content

20251001 #544 기능추가 공지사항 pin공지사항 api 개발에 대한 연결#644

Merged
Cassiiopeia merged 8 commits intomainfrom
20251001_#544_기능추가_공지사항_PIN공지사항_API_개발에_대한_연결
Oct 1, 2025

Hidden character warning

The head ref may contain hidden characters: "20251001_#544_\uae30\ub2a5\ucd94\uac00_\uacf5\uc9c0\uc0ac\ud56d_PIN\uacf5\uc9c0\uc0ac\ud56d_API_\uac1c\ubc1c\uc5d0_\ub300\ud55c_\uc5f0\uacb0"
Merged

20251001 #544 기능추가 공지사항 pin공지사항 api 개발에 대한 연결#644
Cassiiopeia merged 8 commits intomainfrom
20251001_#544_기능추가_공지사항_PIN공지사항_API_개발에_대한_연결

Conversation

@Cassiiopeia
Copy link
Copy Markdown
Member

@Cassiiopeia Cassiiopeia commented Oct 1, 2025

Summary by CodeRabbit

  • 신기능
    • 공지 상세 페이지 추가(로딩·에러·토스트 처리 포함) 및 좋아요 토글과 카운트 반영(좋아요/취소 동작 추가).
    • 고정 공지 전용 호출로 복수 고정공지 표시 및 홈 상단 고정 섹션 제공.
    • 상세 및 카드에서 특정 공지로 바로 이동 가능.
  • 리팩터링
    • 공지 목록 흐름 정리: 첫 페이지에서 고정 공지 우선 로드, 상세 라우팅 일원화.
  • 스타일
    • 카드·고정카드 레이아웃·타이포그래피·구분선·스켈레톤 UI 개선 및 패딩/배경 조정.
  • 접근성
    • 고정 카드에 키보드(Enter/Space) 지원 및 ARIA 개선.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 1, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

공지 상세 조회 페이지와 상세 컴포넌트 및 스켈레톤이 추가되었고, 좋아요 API(추가/취소)와 핀 공지 전용 조회 API가 도입되어 목록 페이지는 별도 핀 공지 선로딩 후 분리 렌더링하도록 변경되었습니다. 타입에 핀 정보 및 요청/응답 필드도 추가되었습니다.

Changes

Cohort / File(s) Summary
APIs: Like / Notice
src/apis/likeApi.ts, src/apis/noticePostApi.ts
likeApinoticeBoardLike / cancelNoticeBoardLike 추가. noticePostApigetFilteredNoticePostsfetchFilteredNoticePosts로 리네임하고 fetchNoticePost(단건 조회), fetchPinnedNoticePosts(핀 공지 조회) 추가.
Pages: Notice 목록 / 상세
src/app/notice/page.tsx, src/app/notice/[id]/page.tsx
목록 페이지는 첫 로드(page=0) 시 fetchPinnedNoticePosts로 핀 공지 선로딩하여 pinnedNoticePosts로 분리 렌더. 상세 페이지(/notice/[id]) 신규 추가: URL 파라미터로 단건 조회, 로딩/에러/권한 처리, NoticeDetail 렌더링.
Components: Notice UI
src/components/notice/NoticeCard.tsx, src/components/notice/PinnedNoticeCard.tsx, src/components/notice/NoticeDetail.tsx, src/components/common/skeletons/NoticeDetailSkeleton.tsx, src/components/landing/NoticeSection.tsx
카드들 네비게이션을 상세 경로(/notice/[id])로 변경. PinnedNoticeCard 접근성(키 핸들링) 및 스타일/아이콘 변경. 신규 NoticeDetail 컴포넌트(좋아요 토글 포함)와 NoticeDetailSkeleton 추가. Landing 섹션은 API명 변경(fetchFilteredNoticePosts).
Types: Notice 엔티티/명령/DTO
src/types/api/entities/postgres/noticePost.ts, src/types/api/requests/noticePostCommand.ts, src/types/api/responses/noticePostDto.ts
NoticePostisPinned?: boolean 추가. NoticePostCommandnoticePostId?: string, postId?: string 추가. NoticePostDtonoticePosts?: NoticePost[](핀 공지 목록) 추가.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as 사용자
  participant Router as Next.js Page (/notice/[id])
  participant API as noticePostApi
  participant BE as Backend

  U->>Router: /notice/:id 접근
  Router->>API: fetchNoticePost({ noticePostId })
  API->>BE: POST /api/notice/get
  BE-->>API: NoticePostDto
  API-->>Router: NoticePostDto
  Router-->>U: NoticeDetail 렌더 (로딩/에러/권한 분기)
Loading
sequenceDiagram
  autonumber
  actor U as 사용자
  participant D as NoticeDetail
  participant L as likeApi
  participant BE as Backend

  U->>D: 좋아요 버튼 클릭
  D->>D: isLikeLoading 체크
  alt 이미 좋아요
    D->>L: cancelNoticeBoardLike({ noticePostId })
    L->>BE: POST /api/notice/like/cancel
    BE-->>L: 204 / void
    L-->>D: 완료
    D->>D: isLiked=false, likeCount--
  else 좋아요 아님
    D->>L: noticeBoardLike({ noticePostId })
    L->>BE: POST /api/notice/like
    BE-->>L: NoticePostDto (isLiked, likeCount)
    L-->>D: 업데이트 데이터
    D->>D: isLiked=true, likeCount=응답값
  end
Loading
sequenceDiagram
  autonumber
  actor U as 사용자
  participant Page as Notice 목록 페이지
  participant API as noticePostApi
  participant BE as Backend

  U->>Page: /notice 접속 (page=0)
  Page->>API: fetchPinnedNoticePosts()
  API->>BE: POST /api/notice/get/pinned {}
  BE-->>API: NoticePostDto { noticePosts[] }
  API-->>Page: 핀 공지 목록
  Page->>API: fetchFilteredNoticePosts({... page 요청 ...})
  API->>BE: POST /api/notice/filter
  BE-->>API: Page<NoticePostDto>
  API-->>Page: 일반 공지 페이지 데이터
  Page-->>U: 핀 공지 목록 + 일반 공지 렌더
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • thswogh

Poem

껑충, 소식 왔네 — 핀은 반짝, 상세는 한눈에 🐇
클릭하니 로딩 반짝, 에러면 살짝 경고 🌟
하트 톡! 좋아요 찰칵, 숫자 톡톡 늘어나네 💙
토끼가 전해요 — 공지 하나 더 읽고 갈게요.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning 제목에 날짜(20251001)와 이슈 번호(#544) 등 불필요한 메타데이터가 포함되어 주요 변경 사항이 모호하며, 핵심 기능인 공지사항 Pin API 연결이 간결하게 드러나지 않습니다. 이로 인해 제목만으로 변경의 요지를 빠르게 파악하기 어렵습니다. 제목에서 날짜와 이슈 번호를 제거하고, 공지사항 Pin API 연결 기능을 간결하게 표현하는 예: "공지사항 Pin API 연결 기능 추가"와 같은 형식으로 수정해 주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9dbbc02 and 38ac09c.

📒 Files selected for processing (1)
  • src/app/notice/page.tsx (2 hunks)

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.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 1, 2025

세종말싸미 빌드 결과: ✅ 빌드 성공

빌드 검증이 완료되었습니다.

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
src/components/notice/NoticeDetail.tsx (1)

7-7: default export 누락

likeApi를 import할 때 named import를 사용하고 있으나, src/apis/likeApi.ts 파일을 보면 likeApi 객체가 default export가 아닌 named export로 제공됩니다. 현재 코드는 동작하지만, 일관성을 위해 명확한 named import 구문을 사용하는 것이 좋습니다.

다음과 같이 수정하는 것을 권장합니다:

-import likeApi from "@/apis/likeApi";
+import { likeApi } from "@/apis/likeApi";
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5debd5b and 11b5cde.

⛔ Files ignored due to path filters (1)
  • public/icons/pushpinBlue.svg is excluded by !**/*.svg
📒 Files selected for processing (10)
  • src/apis/likeApi.ts (2 hunks)
  • src/apis/noticePostApi.ts (1 hunks)
  • src/app/notice/[id]/page.tsx (1 hunks)
  • src/app/notice/page.tsx (2 hunks)
  • src/components/notice/NoticeCard.tsx (2 hunks)
  • src/components/notice/NoticeDetail.tsx (1 hunks)
  • src/components/notice/PinnedNoticeCard.tsx (1 hunks)
  • src/types/api/entities/postgres/noticePost.ts (1 hunks)
  • src/types/api/requests/noticePostCommand.ts (1 hunks)
  • src/types/api/responses/noticePostDto.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: 모든 코드에 TypeScript 엄격 타입을 사용합니다.
인터페이스를 타입 별칭보다 선호합니다.
Boolean 변수는 반드시 is 접두사를 사용합니다.
명확한 Command/Dto 패턴 사용
에러는 명시적으로 처리하고 로깅합니다.
변수/함수: camelCase
상수: UPPER_SNAKE_CASE
컴포넌트/클래스: PascalCase
축약어 및 모호한 이름 금지
오류는 항상 명시적으로 발생시킵니다.
특정 오류 타입 사용 (ValueError, TypeError 등)
오류 메시지는 명확하고 조치 가능해야 합니다.
any 타입 사용 금지
불변성 유지

**/*.{ts,tsx}: Boolean variables must use the 'is' prefix (e.g., isLoading, isModalOpen, isDocumentValid, isUserAuthenticated). Do not use 'has', 'can', or 'should' prefixes.
Do not use abbreviations for variable names (e.g., doc, q, QNA). Use full, descriptive names.
Do not use meaningless variable names such as 'data', 'item', or 'temp'.
Do not use the 'any' type or ambiguous names like 'result' or 'value'.
Constants must be named in UPPER_SNAKE_CASE.
Do not use the 'any' type anywhere in the codebase.
All code must be written in TypeScript (.ts or .tsx files).
Do not use generic interface names like 'Props' or prefix interfaces with 'I'.

Files:

  • src/types/api/entities/postgres/noticePost.ts
  • src/apis/likeApi.ts
  • src/types/api/requests/noticePostCommand.ts
  • src/components/notice/NoticeDetail.tsx
  • src/components/notice/PinnedNoticeCard.tsx
  • src/app/notice/[id]/page.tsx
  • src/app/notice/page.tsx
  • src/apis/noticePostApi.ts
  • src/components/notice/NoticeCard.tsx
  • src/types/api/responses/noticePostDto.ts
**/*.ts

📄 CodeRabbit inference engine (.cursorrules)

FormData 기반 API 통신

Files:

  • src/types/api/entities/postgres/noticePost.ts
  • src/apis/likeApi.ts
  • src/types/api/requests/noticePostCommand.ts
  • src/apis/noticePostApi.ts
  • src/types/api/responses/noticePostDto.ts
src/types/**/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

src/types/**/*.ts: TypeScript interfaces should be used for type definitions (not types or classes), especially for objects that may be extended.
All files in src/types/ must only contain type/interface definitions and related constants.

Files:

  • src/types/api/entities/postgres/noticePost.ts
  • src/types/api/requests/noticePostCommand.ts
  • src/types/api/responses/noticePostDto.ts
src/apis/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

API function names must use the verb + noun pattern and prefer 'fetch' over 'get' (e.g., fetchDocuments, createDocument, updateDocument, deleteDocument).

Files:

  • src/apis/likeApi.ts
  • src/apis/noticePostApi.ts
src/types/api/{requests,responses}/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

src/types/api/{requests,responses}/*.ts: API request types must use the Command suffix, and response types must use the Dto suffix (e.g., DocumentCommand, DocumentDto).
All type definitions for API requests and responses must be placed in src/types/api/requests/ and src/types/api/responses/ respectively.

Files:

  • src/types/api/requests/noticePostCommand.ts
  • src/types/api/responses/noticePostDto.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: 함수형 컴포넌트와 TypeScript 인터페이스를 사용합니다.
컴포넌트명은 PascalCase를 사용합니다.
Tailwind CSS 유틸리티 클래스 사용
하드코딩된 색상/크기 대신 디자인 시스템 토큰 활용
반응형 디자인은 모바일 우선 접근 방식
React.memo, useMemo, useCallback 적절히 사용
불필요한 렌더링 방지
서버 컴포넌트 및 정적 생성 최대한 활용
Shadcn UI 및 Radix UI 컴포넌트 우선 사용

Files:

  • src/components/notice/NoticeDetail.tsx
  • src/components/notice/PinnedNoticeCard.tsx
  • src/app/notice/[id]/page.tsx
  • src/app/notice/page.tsx
  • src/components/notice/NoticeCard.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (GEMINI.md)

src/components/**/*.tsx: Component files must be named in PascalCase (e.g., DocumentUploadCard.tsx, QuestionAnswerForm.tsx, UserProfileHeader.tsx).
Props interface must be named as ComponentNameProps (e.g., DocBoardCardProps). Do not use generic names like 'Props' or prefix with 'I'.
Component names must use PascalCase and clearly indicate their role (e.g., DocumentUploadCard, QuestionAnswerForm, UserProfileHeader).
In CSS/JSX, always use Tailwind CSS classes for colors. Do not use hardcoded color values or inline styles for color.
Do not use inline styles for font or color (e.g., style={{ fontFamily: 'SUIT', fontSize: '18px' }}). Use Tailwind classes instead.
Do not use hardcoded pixel values for width, height, or position (e.g., className="w-[393px]", style={{ width: '271px' }}). Use dynamic, responsive units (rem, %, flex, grid) and Tailwind classes.
Do not use inline styles for layout or spacing. Use Tailwind CSS classes for all spacing, sizing, and layout.
Use conditional rendering for loading, error, and empty states in components (e.g., show skeleton, error message, or empty state as appropriate).
Optimize components using React.memo, useMemo, and useCallback where appropriate.
Do not use inline styles for any layout, spacing, font, or color. Use Tailwind CSS classes exclusively.
All new components must have a defined props interface, use PascalCase for the component name, and follow the project's naming and structure conventions.
Do not use hardcoded pixel values for spacing, sizing, or layout. Use Tailwind's spacing and sizing utilities (e.g., p-4, gap-3, w-full, h-16).
All Tailwind spacing and sizing should use the project's standard scale (multiples of 4px, e.g., gap-1, p-2, m-3, etc.).
Do not use inline styles for text overflow or line clamping. Use Tailwind's 'truncate' or 'line-clamp-N' classes.
All component files must use the default export for the main component.

Files:

  • src/components/notice/NoticeDetail.tsx
  • src/components/notice/PinnedNoticeCard.tsx
  • src/components/notice/NoticeCard.tsx
🧬 Code graph analysis (7)
src/apis/likeApi.ts (3)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-16)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/apis/apiUtils.ts (1)
  • postApiRequest (43-54)
src/components/notice/NoticeDetail.tsx (3)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-16)
src/apis/likeApi.ts (1)
  • likeApi (11-27)
src/app/notice/[id]/page.tsx (6)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-16)
src/apis/noticePostApi.ts (1)
  • noticePostApi (5-17)
src/components/header/CommonHeader.tsx (1)
  • CommonHeader (26-67)
src/types/header.ts (1)
  • RIGHT_ITEM (10-17)
src/components/notice/NoticeDetail.tsx (1)
  • NoticeDetail (14-103)
src/app/notice/page.tsx (7)
src/types/api/entities/postgres/noticePost.ts (1)
  • NoticePost (5-13)
src/apis/noticePostApi.ts (1)
  • noticePostApi (5-17)
src/components/header/CommonHeader.tsx (1)
  • CommonHeader (26-67)
src/components/common/skeletons/NoticeListSkeleton.tsx (1)
  • NoticeListSkeleton (6-40)
src/components/notice/PinnedNoticeCard.tsx (1)
  • PinnedNoticeCard (18-57)
src/components/notice/NoticeCard.tsx (1)
  • NoticeCard (19-88)
src/components/common/CommonPagination.tsx (1)
  • CommonPagination (19-151)
src/apis/noticePostApi.ts (3)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-16)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/apis/apiUtils.ts (1)
  • postApiRequest (43-54)
src/components/notice/NoticeCard.tsx (2)
src/components/common/NoticeCard.tsx (1)
  • NoticeCard (12-62)
src/global/time.ts (1)
  • getDateDiff (42-42)
src/types/api/responses/noticePostDto.ts (1)
src/types/api/entities/postgres/noticePost.ts (1)
  • NoticePost (5-13)
🔇 Additional comments (16)
src/types/api/entities/postgres/noticePost.ts (1)

12-12: LGTM! Boolean 필드 명명 규칙 준수

isPinned 필드가 Boolean 명명 규칙(is 접두사)을 올바르게 따르고 있으며, 다른 Boolean 필드들(isHidden, isLiked)과 일관성을 유지하고 있습니다.

src/types/api/responses/noticePostDto.ts (1)

7-7: LGTM! Response DTO 타입 정의 적절함

noticePosts 필드가 Dto 네이밍 규칙을 따르고 있으며, 배열 타입에 대해 복수형 네이밍을 사용하여 명확성을 제공합니다.

src/types/api/requests/noticePostCommand.ts (1)

6-6: LGTM! Request Command 타입 정의 올바름

noticePostId 필드가 Command 네이밍 규칙을 따르고 있으며, 명확한 타입 정의를 사용하고 있습니다.

src/apis/likeApi.ts (2)

7-8: LGTM! 타입 import 적절함

Command/Dto 패턴을 따르는 타입들을 올바르게 import하고 있습니다.


20-22: LGTM! API 함수 구현 일관성 유지

noticeBoardLike 함수가 기존 like API 함수들과 일관된 패턴을 따르고 있으며, Command/Dto 타입을 올바르게 사용하고 있습니다. Mutation 작업이므로 'fetch' 접두사 없이 동사 패턴을 사용하는 것이 적절합니다.

src/components/notice/NoticeDetail.tsx (4)

14-19: LGTM - 상태 초기화가 적절합니다

Boolean 변수에 is 접두사를 사용하고, 안전한 fallback 값을 제공하여 타입 안전성을 보장하고 있습니다. 코딩 가이드라인을 준수합니다.


20-40: LGTM - 좋아요 토글 로직이 안전합니다

동시 요청 방지(isLikeLoading 체크), 적절한 에러 처리, 그리고 finally 블록에서의 로딩 상태 초기화가 모두 올바르게 구현되어 있습니다.


71-93: LGTM - 접근성을 고려한 버튼 구현

aria-label, disabled 속성, 그리고 시각적 피드백(색상 전환)이 모두 적절하게 구현되어 있습니다. Tailwind 클래스만을 사용하여 스타일링하고 있으며, 인라인 스타일이 없습니다.


47-48: fallback 텍스트 적절성 검토 필요
NoticePost 타입에서 title과 content가 optional로 정의되어 있어 실제로 빈 값이 노출될 수 있습니다. 빈 값 발생 시 ‘제목 없음’/‘내용 없음’ 메시지를 그대로 사용자에게 보여주는 것이 UX 관점에서 적절한지 UX팀 또는 PO와 확인해주세요.

src/app/notice/page.tsx (7)

21-21: LGTM - 상태 선언이 적절합니다

pinnedNoticePosts 변수명이 명확하고, 타입이 올바르게 지정되어 있습니다.


25-36: 핀 공지사항 로딩 실패 시 사용자 피드백 부족

핀 공지사항을 불러오는데 실패해도 에러가 콘솔에만 로깅되고 사용자에게는 아무런 피드백이 없습니다. 이는 의도된 동작일 수 있지만, 사용자 경험 측면에서 검토가 필요합니다.

팀 내에서 핀 공지사항 로딩 실패 시 사용자에게 알림(예: toast)을 표시할지, 아니면 조용히 실패 처리할지 정책을 확인하세요. 만약 알림이 필요하다면 다음과 같이 수정할 수 있습니다:

+  const { showWarningToast } = useCommonToast();
+
   const fetchPinnedNoticePosts = useCallback(async () => {
     try {
       const pinnedResponse = await noticePostApi.getPinnedNoticePosts();
       if (pinnedResponse.noticePosts) {
         setPinnedNoticePosts(pinnedResponse.noticePosts);
       }
     } catch (error) {
       console.error("핀된 공지사항을 불러오는데 실패했습니다:", error);
+      showWarningToast("핀된 공지사항을 불러오는데 실패했습니다.");
       // 핀된 공지사항 실패는 전체 로딩 실패로 처리하지 않음
     }
   }, []);

45-48: 첫 페이지에서만 핀 공지사항을 가져오는 로직이 적절합니다

페이지 번호가 0일 때만 핀 공지사항을 가져와서 불필요한 API 호출을 방지하고 있습니다. 효율적인 구현입니다.


66-66: 의존성 배열에 fetchPinnedNoticePosts 추가 확인

fetchPinnedNoticePosts를 의존성 배열에 추가한 것은 올바른 조치입니다. useCallback으로 메모이제이션되어 있어 무한 루프의 위험은 없습니다.


127-137: 핀 공지사항 렌더링이 올바르게 구현되었습니다

배열을 순회하며 각 핀 공지사항을 렌더링하고, 고유한 key를 사용하며, 일반 공지사항과의 간격도 적절히 설정했습니다.


140-144: 일반 공지사항 렌더링 개선 확인

space-y-4를 사용하여 간격을 일관되게 관리하고, map을 통해 동적 렌더링을 구현했습니다. 이전보다 깔끔하고 유지보수하기 쉬운 코드입니다.


84-84: 로딩 스켈레톤 패딩 변경 확인

px-6에서 px-5로 변경되어 메인 렌더링의 패딩(line 122의 px-5)과 일치합니다. 일관성을 유지하는 좋은 변경입니다.

Comment on lines +10 to +12
// 단일 공지사항 상세 조회
getNoticePost: async (command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

API 함수 명명 규칙 위반

코딩 가이드라인에 따르면 API 함수는 'get' 대신 'fetch'를 선호해야 합니다. getNoticePostfetchNoticePost로 변경해 주세요.

다음 diff를 적용하여 명명 규칙을 준수하세요:

-  // 단일 공지사항 상세 조회
-  getNoticePost: async (command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
-    postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),
+  // 단일 공지사항 상세 조회
+  fetchNoticePost: async (command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
+    postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),

코딩 가이드라인 기준

📝 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
// 단일 공지사항 상세 조회
getNoticePost: async (command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),
// 단일 공지사항 상세 조회
fetchNoticePost: async (command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),
🤖 Prompt for AI Agents
In src/apis/noticePostApi.ts around lines 10 to 12, rename the API function from
getNoticePost to fetchNoticePost to follow the naming guideline; update the
exported property name and its implementation to fetchNoticePost: async
(command: Partial<NoticePostCommand>): Promise<NoticePostDto> =>
postApiRequest<NoticePostCommand, NoticePostDto>("/api/notice/get", command),
and then search & replace all usages/imports across the codebase (including
tests and type declarations) to refer to fetchNoticePost instead of
getNoticePost to keep signatures and behavior unchanged.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 1, 2025

세종말싸미 빌드 결과: ❌ 빌드 실패

빌드 검증이 완료되었습니다.

📋 빌드 에러 로그

> malsami-fe@0.1.24 build
> next build

  ▲ Next.js 14.2.30
  - Environments: .env.local

   Creating an optimized production build ...
 ✓ Compiled successfully
   Linting and checking validity of types ...
Failed to compile.

./src/app/board/document/sub/hot-download/page.tsx:148:9
Type error: Type '{ onReset: () => void; onConfirm: (filtering: Partial<DocumentCommand>) => Promise<void>; currentFiltering: Partial<DocumentCommand>; trigger: Element; activeColor: string; }' is not assignable to type 'IntrinsicAttributes & DocumentFilteringBottomSheetProps'.
  Property 'activeColor' does not exist on type 'IntrinsicAttributes & DocumentFilteringBottomSheetProps'.

�[0m �[90m 146 |�[39m         currentFiltering�[33m=�[39m{currentFiltering}�[0m
�[0m �[90m 147 |�[39m         trigger�[33m=�[39m{�[33m<�[39m�[33mdiv�[39m �[35m/>} /�[39m�[33m/�[39m 빈 트리거 (�[33mHeader의�[39m 메뉴 버튼으로 제어)�[0m
�[0m�[31m�[1m>�[22m�[39m�[90m 148 |�[39m         activeColor�[33m=�[39m�[32m"#00D1F2"�[39m �[90m// 파란색 테마�[39m�[0m
�[0m �[90m     |�[39m         �[31m�[1m^�[22m�[39m�[0m
�[0m �[90m 149 |�[39m       �[33m/�[39m�[33m>�[39m�[0m
�[0m �[90m 150 |�[39m     �[33m<�[39m�[33m/�[39m�[33mdiv�[39m�[33m>�[39m�[0m
�[0m �[90m 151 |�[39m   )�[33m;�[39m�[0m
Next.js build worker exited with code: 1 and signal: null

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (3)
src/types/api/requests/noticePostCommand.ts (1)

6-7: JSDoc 주석으로 일관성을 개선하세요.

postId에는 인라인 주석이 있지만 noticePostId에는 설명이 없습니다. 두 필드 모두에 대해 JSDoc 스타일 주석을 사용하면 일관성과 IDE 지원이 향상됩니다.

다음과 같이 수정하는 것을 권장합니다:

 export interface NoticePostCommand {
+  /**
+   * 공지사항 게시글 고유 식별자
+   */
   noticePostId?: string;
-  postId?: string; // 백엔드 좋아요 API에서 사용
+  /**
+   * 백엔드 좋아요 API에서 사용하는 게시글 식별자
+   */
+  postId?: string;
   member?: Member;
src/components/notice/PinnedNoticeCard.tsx (1)

27-39: 시맨틱 HTML 고려: 실제 button 엘리먼트 사용을 권장합니다.

<div role="button">는 키보드 접근성을 수동으로 구현하고 있지만, 네이티브 <button> 엘리먼트를 사용하면 더 나은 접근성과 브라우저 기본 동작을 얻을 수 있습니다.

다음과 같이 변경하는 것을 권장합니다:

-    <div
+    <button
+      type="button"
       className="flex w-full cursor-pointer items-center gap-3 rounded-xl bg-blue-50 p-4 transition-all hover:bg-blue-100"
       onClick={handleCardClick}
-      onKeyDown={e => {
-        if (e.key === "Enter" || e.key === " ") {
-          e.preventDefault();
-          handleCardClick();
-        }
-      }}
-      role="button"
-      tabIndex={0}
       aria-label={`고정된 공지: ${noticePost.title ?? "제목 없음"}`}
     >
       {/* ... */}
-    </div>
+    </button>
src/app/notice/page.tsx (1)

26-36: 에러 핸들링 개선 필요

핀된 공지사항 로딩 실패 시 console.error만 사용하고 있습니다. 코딩 가이드라인에 따르면 에러는 명시적으로 처리하고 로깅해야 합니다.

명시적인 에러 로깅을 위해 다음과 같이 개선하는 것을 권장합니다:

     } catch (error) {
-      console.error("핀된 공지사항을 불러오는데 실패했습니다:", error);
+      const errorMessage = error instanceof Error ? error.message : "알 수 없는 오류";
+      console.error(`핀된 공지사항 로드 실패: ${errorMessage}`, error);
       // 핀된 공지사항 실패는 전체 로딩 실패로 처리하지 않음
     }

코딩 가이드라인 기준

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11b5cde and 9dbbc02.

📒 Files selected for processing (9)
  • src/apis/likeApi.ts (2 hunks)
  • src/apis/noticePostApi.ts (1 hunks)
  • src/app/notice/[id]/page.tsx (1 hunks)
  • src/app/notice/page.tsx (2 hunks)
  • src/components/common/skeletons/NoticeDetailSkeleton.tsx (1 hunks)
  • src/components/landing/NoticeSection.tsx (1 hunks)
  • src/components/notice/NoticeDetail.tsx (1 hunks)
  • src/components/notice/PinnedNoticeCard.tsx (1 hunks)
  • src/types/api/requests/noticePostCommand.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/app/notice/[id]/page.tsx
  • src/components/notice/NoticeDetail.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: 모든 코드에 TypeScript 엄격 타입을 사용합니다.
인터페이스를 타입 별칭보다 선호합니다.
Boolean 변수는 반드시 is 접두사를 사용합니다.
명확한 Command/Dto 패턴 사용
에러는 명시적으로 처리하고 로깅합니다.
변수/함수: camelCase
상수: UPPER_SNAKE_CASE
컴포넌트/클래스: PascalCase
축약어 및 모호한 이름 금지
오류는 항상 명시적으로 발생시킵니다.
특정 오류 타입 사용 (ValueError, TypeError 등)
오류 메시지는 명확하고 조치 가능해야 합니다.
any 타입 사용 금지
불변성 유지

**/*.{ts,tsx}: Boolean variables must use the 'is' prefix (e.g., isLoading, isModalOpen, isDocumentValid, isUserAuthenticated). Do not use 'has', 'can', or 'should' prefixes.
Do not use abbreviations for variable names (e.g., doc, q, QNA). Use full, descriptive names.
Do not use meaningless variable names such as 'data', 'item', or 'temp'.
Do not use the 'any' type or ambiguous names like 'result' or 'value'.
Constants must be named in UPPER_SNAKE_CASE.
Do not use the 'any' type anywhere in the codebase.
All code must be written in TypeScript (.ts or .tsx files).
Do not use generic interface names like 'Props' or prefix interfaces with 'I'.

Files:

  • src/apis/noticePostApi.ts
  • src/apis/likeApi.ts
  • src/components/landing/NoticeSection.tsx
  • src/app/notice/page.tsx
  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
  • src/types/api/requests/noticePostCommand.ts
  • src/components/notice/PinnedNoticeCard.tsx
**/*.ts

📄 CodeRabbit inference engine (.cursorrules)

FormData 기반 API 통신

Files:

  • src/apis/noticePostApi.ts
  • src/apis/likeApi.ts
  • src/types/api/requests/noticePostCommand.ts
src/apis/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

API function names must use the verb + noun pattern and prefer 'fetch' over 'get' (e.g., fetchDocuments, createDocument, updateDocument, deleteDocument).

Files:

  • src/apis/noticePostApi.ts
  • src/apis/likeApi.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursorrules)

**/*.tsx: 함수형 컴포넌트와 TypeScript 인터페이스를 사용합니다.
컴포넌트명은 PascalCase를 사용합니다.
Tailwind CSS 유틸리티 클래스 사용
하드코딩된 색상/크기 대신 디자인 시스템 토큰 활용
반응형 디자인은 모바일 우선 접근 방식
React.memo, useMemo, useCallback 적절히 사용
불필요한 렌더링 방지
서버 컴포넌트 및 정적 생성 최대한 활용
Shadcn UI 및 Radix UI 컴포넌트 우선 사용

Files:

  • src/components/landing/NoticeSection.tsx
  • src/app/notice/page.tsx
  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
  • src/components/notice/PinnedNoticeCard.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (GEMINI.md)

src/components/**/*.tsx: Component files must be named in PascalCase (e.g., DocumentUploadCard.tsx, QuestionAnswerForm.tsx, UserProfileHeader.tsx).
Props interface must be named as ComponentNameProps (e.g., DocBoardCardProps). Do not use generic names like 'Props' or prefix with 'I'.
Component names must use PascalCase and clearly indicate their role (e.g., DocumentUploadCard, QuestionAnswerForm, UserProfileHeader).
In CSS/JSX, always use Tailwind CSS classes for colors. Do not use hardcoded color values or inline styles for color.
Do not use inline styles for font or color (e.g., style={{ fontFamily: 'SUIT', fontSize: '18px' }}). Use Tailwind classes instead.
Do not use hardcoded pixel values for width, height, or position (e.g., className="w-[393px]", style={{ width: '271px' }}). Use dynamic, responsive units (rem, %, flex, grid) and Tailwind classes.
Do not use inline styles for layout or spacing. Use Tailwind CSS classes for all spacing, sizing, and layout.
Use conditional rendering for loading, error, and empty states in components (e.g., show skeleton, error message, or empty state as appropriate).
Optimize components using React.memo, useMemo, and useCallback where appropriate.
Do not use inline styles for any layout, spacing, font, or color. Use Tailwind CSS classes exclusively.
All new components must have a defined props interface, use PascalCase for the component name, and follow the project's naming and structure conventions.
Do not use hardcoded pixel values for spacing, sizing, or layout. Use Tailwind's spacing and sizing utilities (e.g., p-4, gap-3, w-full, h-16).
All Tailwind spacing and sizing should use the project's standard scale (multiples of 4px, e.g., gap-1, p-2, m-3, etc.).
Do not use inline styles for text overflow or line clamping. Use Tailwind's 'truncate' or 'line-clamp-N' classes.
All component files must use the default export for the main component.

Files:

  • src/components/landing/NoticeSection.tsx
  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
  • src/components/notice/PinnedNoticeCard.tsx
src/components/common/skeletons/*Skeleton.tsx

📄 CodeRabbit inference engine (GEMINI.md)

src/components/common/skeletons/*Skeleton.tsx: Skeleton components must be placed in src/components/common/skeletons/ and named as OriginalComponentNameSkeleton (e.g., MovingCardSkeleton.tsx).
Skeleton components must not accept any props; all structure and count must be hardcoded and static.

Files:

  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
src/types/**/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

src/types/**/*.ts: TypeScript interfaces should be used for type definitions (not types or classes), especially for objects that may be extended.
All files in src/types/ must only contain type/interface definitions and related constants.

Files:

  • src/types/api/requests/noticePostCommand.ts
src/types/api/{requests,responses}/*.ts

📄 CodeRabbit inference engine (GEMINI.md)

src/types/api/{requests,responses}/*.ts: API request types must use the Command suffix, and response types must use the Dto suffix (e.g., DocumentCommand, DocumentDto).
All type definitions for API requests and responses must be placed in src/types/api/requests/ and src/types/api/responses/ respectively.

Files:

  • src/types/api/requests/noticePostCommand.ts
🧠 Learnings (3)
📚 Learning: 2025-08-07T11:54:41.392Z
Learnt from: CR
PR: Sejong-Balsamic/Malsami-FE#0
File: GEMINI.md:0-0
Timestamp: 2025-08-07T11:54:41.392Z
Learning: Applies to src/apis/*.ts : API function names must use the verb + noun pattern and prefer 'fetch' over 'get' (e.g., fetchDocuments, createDocument, updateDocument, deleteDocument).

Applied to files:

  • src/apis/noticePostApi.ts
📚 Learning: 2025-08-07T11:54:41.392Z
Learnt from: CR
PR: Sejong-Balsamic/Malsami-FE#0
File: GEMINI.md:0-0
Timestamp: 2025-08-07T11:54:41.392Z
Learning: Applies to src/components/common/skeletons/*Skeleton.tsx : Skeleton components must be placed in src/components/common/skeletons/ and named as OriginalComponentNameSkeleton (e.g., MovingCardSkeleton.tsx).

Applied to files:

  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
📚 Learning: 2025-08-07T11:54:41.392Z
Learnt from: CR
PR: Sejong-Balsamic/Malsami-FE#0
File: GEMINI.md:0-0
Timestamp: 2025-08-07T11:54:41.392Z
Learning: Applies to src/components/common/skeletons/*Skeleton.tsx : Skeleton components must not accept any props; all structure and count must be hardcoded and static.

Applied to files:

  • src/components/common/skeletons/NoticeDetailSkeleton.tsx
🧬 Code graph analysis (5)
src/apis/noticePostApi.ts (3)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-17)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/apis/apiUtils.ts (1)
  • postApiRequest (43-54)
src/apis/likeApi.ts (3)
src/types/api/requests/noticePostCommand.ts (1)
  • NoticePostCommand (5-17)
src/types/api/responses/noticePostDto.ts (1)
  • NoticePostDto (5-9)
src/apis/apiUtils.ts (1)
  • postApiRequest (43-54)
src/components/landing/NoticeSection.tsx (1)
src/apis/noticePostApi.ts (1)
  • noticePostApi (5-17)
src/app/notice/page.tsx (7)
src/types/api/entities/postgres/noticePost.ts (1)
  • NoticePost (5-13)
src/apis/noticePostApi.ts (1)
  • noticePostApi (5-17)
src/components/header/CommonHeader.tsx (1)
  • CommonHeader (26-67)
src/components/common/skeletons/NoticeListSkeleton.tsx (1)
  • NoticeListSkeleton (6-40)
src/components/notice/PinnedNoticeCard.tsx (1)
  • PinnedNoticeCard (18-57)
src/components/notice/NoticeCard.tsx (1)
  • NoticeCard (19-88)
src/components/common/CommonPagination.tsx (1)
  • CommonPagination (19-151)
src/components/common/skeletons/NoticeDetailSkeleton.tsx (2)
src/components/common/skeletons/NoticeListSkeleton.tsx (1)
  • NoticeListSkeleton (6-40)
src/components/common/skeletons/NoticeCardSkeleton.tsx (1)
  • NoticeCardSkeleton (5-35)
🔇 Additional comments (13)
src/components/common/skeletons/NoticeDetailSkeleton.tsx (1)

1-54: LGTM! 스켈레톤 컴포넌트가 가이드라인을 준수합니다.

스켈레톤 컴포넌트가 프로젝트 규칙을 잘 따르고 있습니다:

  • props 없이 정적 구조로 구성
  • Tailwind CSS 클래스 사용
  • 적절한 default export
  • 명확한 JSDoc 주석
src/components/landing/NoticeSection.tsx (1)

39-43: LGTM! API 메서드명이 코딩 가이드라인을 준수합니다.

fetchFilteredNoticePosts로의 변경이 프로젝트 가이드라인(fetch 선호)을 올바르게 따르고 있습니다.

src/components/notice/PinnedNoticeCard.tsx (2)

38-38: 이전 리뷰 이슈가 해결되었습니다.

aria-label에 fallback 값(?? "제목 없음")을 추가하여 이전 리뷰에서 지적된 undefined 노출 문제가 수정되었습니다.


46-51: LGTM! 사용자 친화적인 fallback 처리가 잘 구현되었습니다.

제목과 내용이 없을 때 명확한 fallback 문자열을 제공하여 UX를 개선했습니다.

src/apis/likeApi.ts (1)

20-26: 취소 엔드포인트 반환 타입 확인 필요

noticeBoardLikeNoticePostDto를 반환하는 반면, cancelNoticeBoardLikevoid를 반환하고 있습니다. 백엔드 취소 API가 실제로 응답 본문을 반환하는지 스펙을 확인하고, 필요하다면 프론트엔드 반환 타입을 NoticePostDto로 변경해주세요.

src/apis/noticePostApi.ts (3)

7-8: 명명 규칙 준수 확인

fetchFilteredNoticePosts로 정상적으로 변경되어 코딩 가이드라인을 준수하고 있습니다.


10-12: 명명 규칙 준수 확인

fetchNoticePost로 명명되어 코딩 가이드라인의 'fetch' 사용 원칙을 정확히 따르고 있습니다. 이전 리뷰 코멘트의 우려사항이 해결되었습니다.


14-16: 명명 규칙 준수 확인

fetchPinnedNoticePosts로 명명되어 코딩 가이드라인을 준수하고 있습니다. 이전 리뷰 코멘트의 우려사항이 해결되었습니다.

src/app/notice/page.tsx (5)

21-21: 배열 타입 선언 확인

pinnedNoticePosts의 타입이 NoticePost[]로 명시적으로 선언되어 타입 안정성을 보장하고 있습니다.


46-48: 조건부 API 호출 로직 확인

첫 페이지(requestedPageNumber === 0)일 때만 핀된 공지사항을 가져오는 로직이 적절합니다. 불필요한 API 호출을 방지하고 있습니다.


129-133: React key prop fallback 적용 확인

noticePostId || \pinned-${index}`패턴으로 key fallback이 적용되어 이전 리뷰 코멘트의 우려사항이 해결되었습니다.noticePostIdundefined`인 경우에도 안정적인 key를 제공합니다.


140-144: 일반 공지사항 렌더링에도 key fallback 확인

일반 공지사항 목록에도 동일한 key fallback 패턴(noticePostId || \notice-${index}``)이 적용되어 타입 안정성을 보장하고 있습니다.


50-54: API 호출 검증 필요
fetchFilteredNoticePosts가 /api/notice/filter 백엔드 엔드포인트에서 핀된 공지사항을 제외하고 일반 공지사항만 반환하는지 확인하세요. 그렇지 않으면 페이지와 Landing 섹션에서 핀된 공지사항이 중복 렌더링될 수 있습니다.

@Cassiiopeia Cassiiopeia merged commit 841c3de into main Oct 1, 2025
1 of 2 checks passed
@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 1, 2025

세종말싸미 빌드 결과: ❌ 빌드 실패

빌드 검증이 완료되었습니다.

📋 빌드 에러 로그

> malsami-fe@0.1.24 build
> next build

  ▲ Next.js 14.2.30
  - Environments: .env.local

   Creating an optimized production build ...
 ✓ Compiled successfully
   Linting and checking validity of types ...
Failed to compile.

./src/app/board/document/sub/hot-download/page.tsx:148:9
Type error: Type '{ onReset: () => void; onConfirm: (filtering: Partial<DocumentCommand>) => Promise<void>; currentFiltering: Partial<DocumentCommand>; trigger: Element; activeColor: string; }' is not assignable to type 'IntrinsicAttributes & DocumentFilteringBottomSheetProps'.
  Property 'activeColor' does not exist on type 'IntrinsicAttributes & DocumentFilteringBottomSheetProps'.

�[0m �[90m 146 |�[39m         currentFiltering�[33m=�[39m{currentFiltering}�[0m
�[0m �[90m 147 |�[39m         trigger�[33m=�[39m{�[33m<�[39m�[33mdiv�[39m �[35m/>} /�[39m�[33m/�[39m 빈 트리거 (�[33mHeader의�[39m 메뉴 버튼으로 제어)�[0m
�[0m�[31m�[1m>�[22m�[39m�[90m 148 |�[39m         activeColor�[33m=�[39m�[32m"#00D1F2"�[39m �[90m// 파란색 테마�[39m�[0m
�[0m �[90m     |�[39m         �[31m�[1m^�[22m�[39m�[0m
�[0m �[90m 149 |�[39m       �[33m/�[39m�[33m>�[39m�[0m
�[0m �[90m 150 |�[39m     �[33m<�[39m�[33m/�[39m�[33mdiv�[39m�[33m>�[39m�[0m
�[0m �[90m 151 |�[39m   )�[33m;�[39m�[0m
Next.js build worker exited with code: 1 and signal: null

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.

1 participant