[#5] feat: 공지사항 - #6
Merged
Hidden character warning
The head ref may contain hidden characters: "feat/5-\uacf5\uc9c0\uc0ac\ud56d"
Merged
Conversation
- .gitignore: .claude/* 통째 제외 → settings.local.json만 제외하는 정석 형태로 변경 - CLAUDE.md도 팀 공유 대상에 포함
- 공지 목록(/notice): 고정 공지 상단, 카테고리 필터, 번호 페이지네이션
(?page=·?category= URL 동기화, 이전/다음 버튼 항상 노출)
- 공지 상세(/notice/:id): 본문 표시, 잘못된 id 처리, 목록 복귀
- 문의/요청(/support, 로그인 필요): 작성 폼(검증·토스트) + 내 문의 내역
- 타입/데이터/스토어: notice·inquiry 타입, 결정적 mock 공지,
사용자별 localStorage 문의 스토어(useInquiries)
- Header '공지사항' 죽은 링크 연결, UserMenu에 '문의/요청' 진입점 추가
- 빌드 수정: AuthError 파라미터 프로퍼티 제거(erasableSyntaxOnly 충돌 해소)
날짜 선택 후 리스트를 보는 흐름에서 위로 스크롤하지 않고 정렬·필터 상태를 다룰 수 있도록 컨트롤을 성격별로 재배치한다.
- 정렬을 리스트 헤더로 이동 (SortDropdown 신규): 마감 임박순/조회순
- 적용된 검색어·필터를 리스트 위에 칩으로 표시 (ActiveFilterTags 신규)
- 칩 개별 해제 및 전체 초기화 지원
- FilterPanel은 상단 드롭다운만 유지하고 칩 표시 블록 제거 (중복 방지)
- recruitmentFilter: 고정 sortByDeadline을 sortRecruitments(sort)로 확장
- 보드 필터 공유 상수를 filterMeta로 분리 - FilterPanel의 로컬 Chip을 ui/Chip으로 통합 - BoardPage의 검색 상태·검증·URL 동기화를 BoardSearchBar로 추출
There was a problem hiding this comment.
Pull request overview
공지사항(/notice, /notice/:id)과 문의/요청(/support) 기능을 추가하고, 보드 검색/필터/정렬 UI를 개선하며, 전역 문서 타이틀/에러 바운더리/404 라우팅을 도입한 PR입니다.
Changes:
- 공지사항 목록/상세 및 읽음 상태(로그인 시) 처리 + mock 데이터/타입 추가
- 문의/요청 작성 및 내역(로그인 필요) + localStorage 기반 per-user 스토어 추가
- 보드 검색/필터/정렬 UI 리팩터(정렬 드롭다운, 활성 필터 칩, 검색 컴포넌트 분리) 및 공통 UX(문서 타이틀, 모바일 헤더 메뉴, 404/에러 바운더리) 보강
Reviewed changes
Copilot reviewed 46 out of 58 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.app.json | TS 설정 파일(선행 공백 라인 포함) 변경 |
| src/types/notice.ts | 공지 카테고리/라벨/타입 정의 추가 |
| src/types/inquiry.ts | 문의/요청 타입/라벨/타입 정의 추가 |
| src/pages/SupportPage.tsx | 문의/요청 작성 + 내역 UI 추가(로그인 필요) |
| src/pages/SignupPage.tsx | 문서 타이틀 설정 추가 |
| src/pages/RecruitmentDetailPage.tsx | 문서 타이틀 설정 및 제목 스타일 조정 |
| src/pages/NotificationsPage.tsx | 문서 타이틀 추가 및 상단 UI 구조 변경 |
| src/pages/NoticePage.tsx | 공지 목록/필터/페이지네이션 UI 추가 |
| src/pages/NoticeDetailPage.tsx | 공지 상세 UI + 읽음 처리 + 잘못된 ID 처리 |
| src/pages/NotFoundPage.tsx | 404 페이지 추가 |
| src/pages/mypage/NotificationSettingsPage.tsx | 문서 타이틀 추가 및 draft 동기화 로직 정리 |
| src/pages/mypage/MyPage.tsx | 마이페이지 상단 아이콘 영역 제거 |
| src/pages/mypage/AccountSettingsPage.tsx | 문서 타이틀 추가 |
| src/pages/LoginPage.tsx | 문서 타이틀 추가 |
| src/pages/HomePage.tsx | 문서 타이틀 기본 설정 추가 |
| src/pages/ForgotPasswordPage.tsx | 문서 타이틀 추가 |
| src/pages/BoardPage.tsx | 검색/정렬/활성 필터 UI 리팩터 및 정렬 로직 교체 |
| src/lib/useDocumentTitle.ts | 라우트별 document.title 훅 추가 |
| src/lib/recruitmentFilter.ts | 정렬 키/옵션 및 sortRecruitments 도입 |
| src/lib/noticeReadState.ts | 공지 읽음 상태 per-user 스토어 추가 |
| src/lib/inquiries.ts | 문의/요청 per-user 스토어 추가 |
| src/lib/date.ts | 공지용 날짜 포맷 유틸 추가(formatFullDate) |
| src/lib/api/auth.ts | AuthError 구현 변경(코드 필드 명시) |
| src/data/mockNotices.ts | 공지 mock 데이터 및 getNoticeById 추가 |
| src/components/ui/Chip.tsx | Chip 색상 확장(선택 스타일 색상화) |
| src/components/ui/Badge.tsx | 텍스트 사이즈 조정 |
| src/components/notice/NoticeCategoryBadge.tsx | 공지 카테고리 배지 컴포넌트 추가 |
| src/components/layout/UserMenu.tsx | 문의/요청 링크 추가 |
| src/components/layout/Header.tsx | 공지사항 링크 + 모바일 메뉴 토글 UI 추가 |
| src/components/ErrorBoundary.tsx | 전역 에러 바운더리 추가 |
| src/components/board/SortDropdown.tsx | 보드 정렬 드롭다운 추가 |
| src/components/board/RecruitmentListCard.tsx | 텍스트 사이즈 조정 |
| src/components/board/MonthYearPicker.tsx | 드롭다운 열기 시 연도 동기화 로직 변경 |
| src/components/board/FilterPanel.tsx | 필터 드롭다운/칩 구성 리팩터(공유 메타 분리) |
| src/components/board/filterMeta.ts | 필터 라벨/색상 메타 분리 |
| src/components/board/CalendarBoard.tsx | 텍스트 사이즈 조정 |
| src/components/board/BoardSearchBar.tsx | 보드 검색 컴포넌트 분리 |
| src/components/board/ActiveFilterTags.tsx | 활성 검색/필터 칩 표시 및 개별 해제 UI 추가 |
| src/App.tsx | notice/support 라우팅 추가 + ErrorBoundary/NotFoundPage 적용 |
| eslint.config.js | @typescript-eslint/no-unused-vars 규칙 강화 |
| .vite/deps/react.js | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react-dom-NWqLDSPJ.js.map | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react-dom-NWqLDSPJ.js | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react-BQupsjBO.js.map | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react-BQupsjBO.js | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react_jsx-dev-runtime.js.map | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/react_jsx-dev-runtime.js | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/package.json | Vite 프리번들 산출물 커밋 포함 |
| .vite/deps/_metadata.json | Vite 프리번들 산출물 커밋 포함 |
| .gitignore | ignore 항목 변경(추가 정리가 필요) |
| .claude/rules/code-style.md | 코드 스타일 가이드 추가 |
| .claude/docs/notice-spec.md | 공지/문의 명세 문서 추가 |
| .claude/CLAUDE.md | 레포 작업 가이드 문서 추가 |
Files not reviewed (3)
- .vite/deps/react-BQupsjBO.js: Language not supported
- .vite/deps/react-dom-NWqLDSPJ.js: Language not supported
- .vite/deps/react_jsx-dev-runtime.js: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -1,44 +1,50 @@ | |||
| import { useMemo, useState, useEffect } from 'react' | |||
| import { useMemo, useState } from 'react' | |||
Comment on lines
+44
to
+47
| if (urlQuery !== syncedQuery) { | ||
| setSyncedQuery(urlQuery) | ||
| setFilter((prev) => (prev.query === urlQuery ? prev : { ...prev, query: urlQuery })) | ||
| } |
| @@ -0,0 +1,84 @@ | |||
| import { useState } from 'react' | |||
Comment on lines
+24
to
+28
| if (committedQuery !== syncedQuery) { | ||
| setSyncedQuery(committedQuery) | ||
| setDraft(committedQuery) | ||
| setError(null) | ||
| } |
Comment on lines
+38
to
+42
| {unreadCount > 0 && ( | ||
| <div className="flex items-center justify-between mb-8 gap-3 flex-wrap"> | ||
| <span className="text-xs font-medium text-stone-500"> | ||
| 읽지 않은 알림{' '} | ||
| <span className="text-orange-500 tabular-nums">{unreadCount}</span>개 |
Comment on lines
+30
to
+34
| type="button" | ||
| aria-label="메뉴 열기" | ||
| aria-expanded={menuOpen} | ||
| onClick={() => setMenuOpen((prev) => !prev)} | ||
| className="md:hidden p-2 -ml-2 rounded-lg text-stone-500 hover:text-stone-800 hover:bg-stone-100 transition-colors" |
Comment on lines
+8
to
+20
| function isInquiryArray(value: unknown): value is Inquiry[] { | ||
| return Array.isArray(value) | ||
| } | ||
|
|
||
| function loadInquiries(userId: string | null): Inquiry[] { | ||
| const key = inquiriesKey(userId) | ||
| if (!key || typeof window === 'undefined') return [] | ||
| try { | ||
| const raw = window.localStorage.getItem(key) | ||
| if (!raw) return [] | ||
| const parsed = JSON.parse(raw) | ||
| return isInquiryArray(parsed) ? parsed : [] | ||
| } catch { |
Comment on lines
+1
to
+5
| { | ||
| "hash": "413ad7ff", | ||
| "configHash": "b7adec69", | ||
| "lockfileHash": "69d4dc3c", | ||
| "browserHash": "81043c73", |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📄 작업 내용
공지사항
보드 검색/필터/정렬
공통
✅ PR 유형
🧪 테스트 내용
🙋🏻 참고 사항
🔗 관련 이슈
resolved: #5