Skip to content

release: dev를 main에 반영 - #248

Merged
sjungwon03 merged 121 commits into
mainfrom
dev
Jul 24, 2026
Merged

release: dev를 main에 반영#248
sjungwon03 merged 121 commits into
mainfrom
dev

Conversation

@sjungwon03

Copy link
Copy Markdown
Member

변경 내용

dev에 쌓인 변경사항을 main에 반영한다(121 commits). main 전용으로 존재하던 .github/ 템플릿 커밋 2건은 현재 dev의 템플릿과 내용이 동일해 병합 시 충돌이 없음을 로컬에서 dry-run으로 확인했다.

주요 항목(최근 순):

검증

  • git merge --no-commit --no-ff origin/dev 로컬 dry-run — 충돌 없음
  • 각 커밋은 이미 개별 PR에서 CI(lint, build) 통과 후 dev에 머지된 상태

연결 이슈

없음(릴리즈 반영)

sjungwon03 and others added 30 commits July 6, 2026 16:15
* chore: 이슈 템플릿 오타 수정

* chore: pnpm 고정
* chore: CLAUDE 및 rules 문서 업데이트 및 내용 정리

* chore: 라우팅·상태관리 라이브러리 설치

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 경로 alias를 src 기준으로 정비하고 shared 레이어 골격 도입

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: policy·user 도메인을 entities 레이어로 승격하고 DTO-모델 mapper 분리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 검색·찜·비교 기능을 feature 슬라이스로 분리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 추천·신청관리 기능을 feature 슬라이스로 분리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 인증·프로필·챗봇·관리자 기능을 feature 슬라이스로 분리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 복수 feature 조합 UI를 widgets 레이어로 구성

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: React Router 기반 pages·app 레이어로 화면 조립 전환

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: App.tsx 모놀리스 및 레거시 components 디렉터리 제거

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: rules.md에 따라 dev 서버를 vite 정적 SPA로 전환

server.ts(Express) 및 챗봇 프록시용 서버 의존성(express, dotenv, @google/genai, tsx, esbuild)을 제거하고 dev/build/start 스크립트를 vite 명령으로 교체한다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: shared 공용 유틸과 ErrorState 컴포넌트 추가

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 신청 목표일 ISO 정규화 및 randomUUID insecure context 폴백

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 생활·상태 필터가 실제 검색 결과에 반영되도록 구현

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 검색 디바운스와 이전 결과 유지, 한글 IME Enter 가드 적용

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 최초 로그인 프로필 마법사 분기 복구 및 redirect 경로 보존

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 관리자 라우트 role 가드 적용 및 라우트 지연 로딩 분할

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 신청관리 시작 실패 경로 처리 및 에러 카드 ErrorState 통합

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 카테고리 배지 로직 통합 및 불필요한 정책 조회 제거

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 미사용 쿼리 훅과 apiError 재수출 제거

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 관리자 동기화 요약 지표를 하드코딩에서 쿼리 조회로 전환

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refac: 공용 상수 통합 및 rules.md 컨벤션 정리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: 관리자 메뉴 링크를 admin role에만 노출하도록 조건부 렌더링

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: 검색 지역 필터에 '전국' 옵션 추가로 controlled select 불일치 해소

showNationwideOnly가 region="전국"을 세팅하지만 REGIONS 옵션에 없어
select 값이 빈 표시되던 문제를 검색 전용 REGION_FILTER_OPTIONS로 해결.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: 신청관리 목록의 policies 쿼리 로딩·에러 게이트 추가

trackers·policies mock 딜레이 차로 policies가 pending/실패인 동안
목록은 비고 탭 카운트는 raw trackers 기준이라 불일치가 생기던 문제를,
두 쿼리를 함께 로딩/에러 게이트해 일관된 UI로 해소.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: 마이페이지 저장 카운트 조인 기준 통일 및 로딩·에러 처리

- '관심 정책 저장' 카드를 raw savedPolicyIds.length에서 조인된
  savedPolicies.length로 변경해 p_missing 등으로 인한 4 vs 3 불일치 해소
- bookmark·trackers·policies 세 쿼리를 함께 로딩(Skeleton)/에러(ErrorState)
  게이트해 카운트-목록 레이스 제거
- useBookmark가 refetch를 노출하도록 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: 프로필 마법사 키워드 중복 추가 방지

같은 키워드 추가 시 key 충돌과 removeKeyword의 복사본 일괄 삭제를
유발하던 문제를, addKeyword에 중복 체크를 추가해 해소.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: 홈 맞춤 추천 섹션 로딩·에러 상태 처리

추천 쿼리가 pending/실패일 때 로그인 사용자에게 빈 섹션과 '0건'만
노출되던 문제를, useRecommendations의 isLoading/isError/reload를 사용해
Skeleton·ErrorState로 처리(정책 목록 섹션과 일관).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore: 코드래빗 설정 파일 추가

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: 코드래빗 자동 리뷰 대상 브랜치를 main에서 dev로 정정

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: rules.md 개선

fsd 구조에 맞게 rules.md 개선

Co-authored-by: Chaeyeon Han <91395969+imi21123@users.noreply.github.com>

* chore: 코드래빗 리뷰 반영 (pnpm allowBuilds 중복 정리·매퍼 경로 정정)

- pnpm-workspace.yaml: dev 머지로 생긴 esbuild 중복 키 제거, dev 의존성
  트리에 없는 protobufjs 제거해 esbuild만 남김
- .coderabbit.yaml: src/** 매퍼 위치 지침을 실제 구조(entities/{domain}/model)에
  맞게 정정

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Chaeyeon Han <91395969+imi21123@users.noreply.github.com>
* chore: 브랜드 로고 및 아이콘 이미지 교체

- 헤더의 임시 로고(Sparkles + 텍스트)를 확정된 YouthPick 가로 로고 이미지로 교체
- 브랜드 아이콘/가로 로고 이미지 에셋 추가
- index.html에 파비콘(브랜드 아이콘) 연결

Closes #14

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: 헤더 로고 이미지를 장식용으로 처리

Link의 aria-label이 접근성 이름을 제공하므로 중복을 피하기 위해
로고 img의 alt를 빈 문자열로 변경한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore: Claude 에이전트 하네스 구성 및 Biome 도입

- AGENTS.md를 도구 무관 정본 진입점으로 추가하고 CLAUDE.md는 @AGENTS.md import로 축소
- rules.md를 주제별 .claude/rules/*(workflow/architecture/component-pattern/react/typescript/api-design/design-system/naming/testing)로 분리
- .claude 하네스 추가: commands(fix-issue), agents(code-reviewer), skills(security-review), workflows(pr-review), hooks(block-secrets/format-on-save), output-styles(teaching), settings.json
- 포매터·린터로 Biome 도입: biome.json, format/check/lint/typecheck 스크립트, format-on-save 훅을 Biome로 전환
- CLAUDE.local.md·settings.local.json gitignore, .mcp.json/.worktreeinclude 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: Biome 규칙에 맞춰 접근성·타입 정리 및 포맷 적용

- button type 명시(79), 장식용 svg aria-hidden, label-control 연결(11)
- list index key 제거(5) → 안정 키, 클릭 div → button 전환
- any 제거(unknown/구체 타입), non-null 단언 제거(main.tsx 루트 가드)
- server.ts/vite.config.ts import 정렬, 전체 Biome 포맷 적용
- biome check . / tsc --noEmit / vite build 모두 통과

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: 하네스 문서의 base 브랜치를 main에서 dev로 정정

레포 기본 브랜치가 dev이므로(main은 미사용) 브랜치 생성·PR base·리뷰 diff 기준을 dev로 수정.
- AGENTS.md / rules/workflow.md: 기본 브랜치 dev 명시
- commands/fix-issue.md: git switch dev, gh pr create --base dev
- agents/code-reviewer.md, skills/security-review, workflows/pr-review.js: diff 기준 origin/dev

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: CodeRabbit 리뷰 반영 (자동 스크롤 회귀·챗봇 에러 처리 외)

코드 수정:
- ChatbotAssistant: 자동 스크롤 useEffect 의존성 []→[messages, loading] 복구(회귀 수정)
- ChatbotAssistant: /api/chat 응답 res.ok 확인 + AbortSignal.timeout(15s)로 무한 로딩 방지
- PolicyCard: 미사용 _LogoIcon·IconMap 죽은 코드 및 관련 import 제거
- package.json: dependencies의 중복 vite 제거(devDependencies에만 유지)

하네스 문서/훅:
- block-secrets.sh: rm 롱옵션(--recursive/--force) 및 grep/sed/awk/입력 리다이렉션(.env) 차단 보강
- rules/react.md: type=button은 기본 버튼, 제출/리셋은 submit/reset 명시
- rules/design-system.md: CTA 대비 오류 text-primary→text-primary-foreground
- AGENTS.md: pnpm start(=node dist/server.cjs) 및 format-on-save(Biome) 설명 정정
- MEMORY.md: 검증 명령을 Biome(lint) + tsc(typecheck)로 정정

검증: biome check . / tsc --noEmit / vite build 모두 통과.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: 코드래빗 리뷰 후속 반영 (브랜치 타입·typecheck·CTA 대비)

- fix-issue.md: 브랜치 타입을 rules.md 정본대로 feat|fix|docs|refac로 좁힘,
  검증 단계에 typecheck 명시, 로직 위치 model→hooks 정정
- rules.md: 브랜드 CTA 대비 오류 text-primary→text-primary-foreground(§13 디자인)
- pr-review.js: Workflow 런타임 스크립트임을 주석화(top-level return 오탐 설명)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs: 하네스의 스테일한 FSD 세그먼트 참조 정정 (model→hooks, lib→entities/model)

리뷰 지적(code-reviewer.md:25) 반영 및 동일 유형 일괄 정정. dev의 rules.md 정본 기준:
- 기능 로직 위치: `model` → feature의 `hooks`
- DTO→model 매퍼 위치: `lib` → 공유는 `entities/*/model`, feature 전용은 feature `api` 근처
- code-reviewer.md 맥락 세그먼트 예시(feature api/hooks/components, entity api/model/ui)로 명확화

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore: 프론트엔드 CI 파이프라인 추가

GitHub Actions로 lint·build 자동 검증 파이프라인을 구축한다.

- push/pull_request(dev, main)에서 lint·build 실행
- corepack + pnpm 11.9.0 고정, pnpm 의존성 캐시
- Node 버전은 .nvmrc(22)로 고정하여 로컬/CI 일치
- concurrency로 중복 실행 취소

Closes #18

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: CI checkout 자격증명 비활성화 및 최소 권한 적용

zizmor artipacked 경고 대응. checkout이 남기는 GITHUB_TOKEN을
이후 pnpm install의 서드파티 postinstall이 탈취할 위험을 차단한다.

- actions/checkout에 persist-credentials: false 추가
- job은 push 권한이 불필요하므로 permissions: contents: read로 축소

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* refac: 메인 페이지 알림 구독 카드 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 헤더·모바일 네비 정리 (마이페이지 메뉴 삭제, 비로그인 신청관리 탭 숨김)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 메인 신규 정책 그리드 6개 확대 및 홈 사이드바(최근 본 정책·챗봇) 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 최근 본 정책 마이페이지 이동 및 즐겨찾기 영역 제거

- 활동 지표의 관심 항목을 신청관리 관심 탭 기준으로 전환

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 챗봇을 우측 하단 플로팅 버튼으로 전환

- 메인 사이드바 카드(ChatbotWidget) 제거, RootLayout에 전역 플로팅 진입점 추가
- 추천 질문 칩을 ChatDrawer 초기 상태로 이동

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 모바일 하단 네비를 fixed로 고정해 스크롤 끝에서 밀려 올라가는 현상 해결

- sticky 대신 fixed로 항상 뷰포트 하단 고정, app-root에 모바일 하단 패딩(pb-16) 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 비교 최대 3개까지 확대

- MAX_COMPARE_COUNT 2 -> 3, 비교 패널·상세 다이얼로그를 동적 N-슬롯(최대 3개)으로 재구성
- 슬롯별 강조색(compareSlots)을 패널 배지와 다이얼로그 제목에서 공유

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 메인 페이지에 정책 비교 패널 배치

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 챗봇 플로팅 전환 후 검색 페이지에 남은 중복 챗봇 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 카드에서 상세보기 버튼 제거하고 카드 클릭으로 상세 이동

- 카드 전체를 덮는 접근성 버튼으로 상세 이동, 비교·찜 버튼은 z-10으로 올려 독립 동작

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: CTA 버튼 그라데이션을 단색(bg-primary)으로 통일

- 텍스트 그라데이션·장식 배경·아바타는 유지, 버튼만 단색화

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 메인 정책 비교를 우측 슬라이드-인 독으로 전환

- 미선택 시 숨김, 정책을 담으면 우측에서 슬라이드-인, 접기/펼치기 토글 지원
- 검색 페이지의 인라인 비교 패널은 기존대로 유지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 헤더 메뉴에서 홈 제거 (로고가 홈 링크 역할)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 메인 정책 비교 상세 모달을 독(transform) 밖에서 렌더해 화면 중앙 모달로 통일

- CompareDetailDialog를 ComparePanelPresenter에서 분리해 각 컨테이너 최상위에서 렌더
- 슬라이드-인 독의 transform 컨테이너에 갇혀 사이드바처럼 보이던 모달을 검색 페이지와 동일하게 통일

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 비교 모달 중복 닫기 버튼 제거 및 열림 시 배경 스크롤 잠금

- 하단 닫기 버튼 삭제(우상단 X로 통일)
- useBodyScrollLock 공통 훅 추가: 모달 열림 동안 body 스크롤 잠금(스크롤바 폭 padding 보정)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 비교 모달 '상세 혜택 및 요건' 라벨 줄바꿈 방지 및 칸 구분선 추가

- 라벨 칸 폭을 넓히고 whitespace-nowrap 적용해 긴 라벨이 2줄로 깨지지 않게 수정
- 정책 칸 사이에 옅은 세로 구분선(border-slate-100) 추가로 열 구분을 명확히

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 찾기 페이지도 메인처럼 정책 비교 슬라이드-인 독 + 3열 그리드로 통일

- ComparePanelContainer(인라인 사이드바) -> CompareDockContainer로 교체
- 8:4 분할 레이아웃 제거, 전체 폭 3열 그리드(POLICY_GRID_CLASS)로 재구성
- POLICY_GRID_CLASS를 policy-card-grid 위젯에서 export해 Home·Search가 공유

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 정책 비교 슬라이드 독 너비 확대 (지역·정책명 잘림 방지)

- w-80(320px) -> w-[28rem](448px)로 확대해 3열 슬롯에서 지역·제목 텍스트 잘림 해소

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 맞춤 추천 페이지도 정책 비교 슬라이드-인 독으로 전환

- ComparePanelContainer(인라인) -> CompareDockContainer로 교체
- 검색 페이지에 이어 발견된 중복 챗봇(ChatbotContainer 인라인) 제거
- 더 이상 쓰이지 않는 ComparePanelContainer 삭제

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 맞춤 추천 카드 목록을 2열 그리드로 배치

- 비교 패널이 사이드바에서 독으로 이동하며 생긴 여유 폭을 2열로 활용

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: 맞춤 추천 카드에서도 정책 비교 담기 가능하도록 추가

- RecommendationCard에 비교 토글 버튼 추가, useCompare로 배선
- 홈/검색과 동일한 비교함(zustand)을 공유해 추천 페이지에서도 비교 슬라이드 독 이용 가능

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 비교 슬롯 카테고리 뱃지를 슬롯 고정색 대신 실제 카테고리 색으로 표시

- getPolicyCategoryBadgeClasses로 다른 화면(정책 카드·상세 모달 등)과 동일한 카테고리 색상 사용
- 슬롯 고정 badge 필드 제거(더 이상 사용되지 않음), 정책 제목 강조색(title)은 열 구분용으로 유지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 교육 카테고리 뱃지 색이 주거와 유사해 teal로 구분

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 정책 카드 설명 2줄 표시 시 하단 잘림 수정

- h-7.5(30px) 고정 높이가 leading-relaxed 2줄 실제 높이(35.75px)보다 작아 발생
- min-h-9(36px)로 교체해 2줄 텍스트가 온전히 보이도록 수정

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 모바일 헤더 햄버거 메뉴 제거, 관리자 링크는 프로필 옆 텍스트 버튼으로 이동

- 하단 탭바(MobileNav)와 메뉴가 대부분 중복돼 햄버거 패널 제거
- 관리자 전용 링크는 로그인 시 프로필 버튼 옆에 텍스트로 노출(데스크톱·모바일 공통)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: Footer 컴포넌트 제거 (프로젝트 특성상 약관·정책 콘텐츠 불필요)

- RootLayout에서 렌더 제거 + Footer.tsx 및 export 완전 삭제
- 포트폴리오/실습용 프로젝트라 실제 이용약관·개인정보처리방침 콘텐츠를 채울 계획이 없어 남겨둘 이유가 없음

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 마이페이지 활동 지표 타일이 신청관리의 해당 탭으로 바로 열리도록 수정

- 관심 정책/신청 준비중/결과 대기목록 타일이 모두 신청관리 기본(전체) 탭으로만 이동하던 문제
- MyPage에서 tab 쿼리 파라미터(?tab=관심 등)를 함께 전달
- TrackerContainer가 해당 파라미터를 초기 activeTab으로 사용(유효하지 않으면 전체로 폴백)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 정책 카드 전체 클릭 오버레이가 제목 툴팁을 가리던 문제 수정

- 카드 전체를 덮는 상세 이동 버튼에 title 속성을 추가해, line-clamp로 잘린 제목에 마우스를 올리면 네이티브 툴팁이 뜨도록 복원

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 정책 비교 상세 모달에 다이얼로그 접근성 속성 및 키보드/배경 클릭 닫기 추가

- role="dialog" aria-modal="true" aria-labelledby(제목 id 연결) 추가
- Escape 키 및 배경(패널 바깥) 클릭으로 닫기 지원, 패널 내부 클릭은 무시

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 로딩 스켈레톤 개수를 하드코딩 대신 공유 상수에서 파생

- policy-card-grid 위젯에 POLICY_GRID_SKELETON_COUNT 상수 추가(POLICY_GRID_CLASS와 함께 export)
- HomePage의 HOME_POLICY_COUNT가 이 상수를 참조해 실제 표시 개수와 항상 일치
- Home·Search 로딩 스켈레톤 모두 이 상수 기준으로 개수를 생성해 그리드 변경 시 드리프트 방지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 클릭 가능한 버튼에 포인터 커서 복원

- Tailwind v4 preflight가 button 기본 커서를 default로 초기화해, 앱 내 모든 onClick 버튼이 포인터 커서 없이 표시되던 문제
- 개별 컴포넌트마다 cursor-pointer를 추가하는 대신 전역 CSS(button:not(:disabled))로 일괄 복원
- 비활성 버튼은 대상에서 제외해 기존 상태 유지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* refac: 온보딩 프로필 모델에 결혼상태·전공·특화조건·연소득 필드 추가

- UserProfile에 maritalStatus/major/specialConditions/annualIncome/incomeUnknown 추가
- 온보딩 완료 판단을 interests.length 대신 명시적 isOnboarded 플래그로 분리
  (관심분야가 선택 항목이 되면서 기존 판단 기준이 더 이상 유효하지 않음)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 온보딩 옵션 버튼 그리드를 공용 컴포넌트로 추출, 학력 옵션에 고졸 미만 추가

- WizardStepStatus 내부의 OptionButtonGrid를 components/common으로 분리(단일/복수 선택 겸용)
- 신규 추가 조건 단계에서도 재사용

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: 온보딩 마법사에 결혼상태·전공·특화조건·연소득 선택 단계 추가

- 신규 3단계 '추가 조건(선택)': 결혼상태·전공계열(단일선택), 특화조건(복수선택), 연소득+소득무관 체크
- 관심분야(기존 3단계 -> 4단계)를 완전한 선택 항목으로 전환, 완료 시 최소 1개 요구 로직 제거
- 건너뛴 선택 항목은 기본값(제한없음/빈 배열/null)으로 저장되어 매칭 시 '제한없음'으로 취급됨

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 마이페이지 프로필 요약 카드에 신규 온보딩 필드 노출

- 결혼상태·전공계열·특화조건·연소득 행 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 모델에 온보딩 매칭용 조건 필드 추가 (연령·결혼상태·전공·특화조건·소득상한)

- Policy/PolicyDto에 ageMin/ageMax/maritalCondition/majorCondition/specialConditionTags/incomeMax/eligibleStatuses 추가
- mock 데이터 11건에 기존 description·target 텍스트와 일치하는 값으로 채움(예: p5는 설명에 명시된 '미혼' 조건 반영)
- 값이 없으면(null/빈 배열) 제한없음으로 취급

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 기준 연도(CURRENT_YEAR)를 공유 상수로 추출

- WizardStepBasic에 하드코딩돼 있던 값을 shared/constants로 이동, 매칭 로직의 나이 계산과 공유

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: 정책 추천 매칭 로직에 온보딩 신규 필드 반영

- 연령(생년월일 기반), 취업/학력 상태(eligibleStatuses 매핑), 결혼상태, 전공, 특화조건, 연소득 상한을 실제 채점에 반영
- 유저가 스킵한(제한없음) 항목은 항상 통과로 간주, 정책에 조건이 없으면 중립 처리
- 순위 다양성용 가짜 변동값(index 기반)과 하드코딩된 추천 사유·가짜 (+NN)점 배지를 제거하고, 실제 매칭 근거만 동적으로 표시
- eligibleStatuses·incomeMax 등은 실서비스 API 필드가 준비되면 그대로 교체 가능하도록 정책 모델에 위치

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: AuthUser에 email·provider 필드 추가 (OAuth 계정 정보 명시)

- 소셜 로그인 mock에서 provider별 이메일을 함께 저장
- 기존에 없던 email 필드를 신설해 OAuth에서 받아오는 정보를 온보딩 프로필과 구분할 근거 마련

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 마이페이지에서 OAuth 계정 정보와 온보딩 프로필을 명확히 분리 표시

- 계정 카드: 'OAuth 계정 정보' 라벨 추가, 이메일 노출, 로그인 제공자를 하드코딩된 'Google' 대신 실제 값으로 표시
- ProfileSummaryCard: '온보딩 프로필' 라벨 추가로 출처가 다른 두 섹션을 시각적으로 구분

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 마이페이지 섹션 타이틀을 딱딱한 표현 대신 간결한 문구로 변경

- 'OAuth 계정 정보' 라벨 제거(카드 자체로 계정 정보임이 드러남)
- '개인 맞춤 조건 가중치 프로필' -> '맞춤 프로필 정보'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 맞춤 프로필 정보 카드를 타일+칩 레이아웃으로 재구성

- 9개 항목을 세로 label:value 목록으로 나열하던 방식을 그리드형 정보 타일(거주지/출생연도/취업상태/학력/결혼상태/전공계열/연소득)로 변경
- 특화조건·관심분야는 별도 섹션의 칩 리스트로 분리해 한눈에 스캔되도록 개선
- ChipGroup 내부 컴포넌트로 중복 렌더링 로직 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 특화조건·관심분야를 별도 섹션 대신 동일한 정보 타일 그리드 안에 배치

- ChipGroup(타일 밖 독립 섹션) -> ChipTile(그리드 안 전체 폭 타일)로 변경
- 배경(bg-slate-50)·둥근 모서리 등 다른 정보 타일과 동일한 컨테이너 스타일 적용, 내부만 뱃지로 채워 시각적 튐 제거
- 특화조건 미설정 시 표시 텍스트도 다른 타일 값과 동일한 스타일로 통일

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 맞춤 프로필 카드에 추가 키워드 타일 추가 및 레이아웃 재배치

- 온보딩에서 수집하지만 표시가 빠져있던 추가 타겟 키워드(profile.keywords) 타일 추가
- 특화조건을 연소득 옆 2칸 폭으로 배치해 한 행을 채우도록 정리
- 관심분야·추가 키워드는 온보딩에서 함께 수집되지만 매칭 반영 여부가 달라(관심분야는 채점 반영, 키워드는 미반영)
  타일은 유지하되 1:1 비율로 한 줄에 나란히 배치

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 온보딩 기본값을 미선택 상태로 초기화

- birthYear/region/subRegion/employmentStatus/educationStatus가 실제 값처럼 보이는 데모 기본값(1998년생·서울특별시 마포구·미취업·구직·대학 졸업)으로
  채워져 있어 아무것도 고르지 않았는데 이미 답한 것처럼 보이던 문제
- 빈 값(0/'')으로 변경, 취업상태·학력 버튼은 아무것도 선택되지 않은 상태로 시작
- 출생연도·거주 광역시도 select에 미선택 placeholder 옵션 추가, 시군구(선택 항목)는 '선택 안 함' 옵션 추가
- 마법사 단계별 필수 검증은 추가하지 않음(하단 '나중에 하기'로 언제든 보류 가능)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 결혼상태·전공계열의 '제한없음' 사전선택 및 '(선택)' 표기 제거

- WizardStepAdditional: 3단계 헤딩에서 '(선택)' 문구 제거, 결혼상태/전공계열 옵션에서 '제한없음' 버튼 삭제
- 기본값을 '제한없음'에서 빈 값('')으로 변경해 아무것도 눌리지 않은 상태로 시작
- recommendApi의 matchesOpenCondition이 유저 미선택을 '제한없음' 문자열 대신 빈 값으로 판단하도록 수정(정책 쪽 제한없음 값은 그대로 유지)
- ProfileSummaryCard에 결혼상태·전공계열 미설정 시 '미설정' 폴백 표시 추가(빈 타일 방지)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 마이페이지 프로필 타일 전체에 미설정 폴백 일관 적용

- 거주지·출생연도·취업상태·학력도 값이 비어있을 때 빈 텍스트 대신 '미설정' 표시
- 거주지는 subRegion 없을 때 후행 공백 없이 표시

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 관심분야 뱃지 색을 정책 카드 카테고리 색과 통일

- getPolicyCategoryBadgeClasses를 entities/policy에서 shared/utils로 이동(entities 간 횡단 import 없이 재사용하기 위함)
- entities/policy/ui/PolicyCategoryBadge는 shared 구현을 재사용, 기존 export 경로(@/entities/policy)는 그대로 유지
- ProfileSummaryCard의 ChipTile이 값별 색상 함수를 받을 수 있게 확장, 관심분야 칩에 카테고리별 정책 카드 색상 적용

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 위험 설정 구역 문구를 '맞춤 프로필 정보'로 용어 통일

- '프로필 데이터 가중치' -> 상단 카드 제목과 동일한 '맞춤 프로필 정보'

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: 온보딩 미완료 시 맞춤 추천 탭 진입을 프로필 설정으로 안내

- RequireOnboarded 라우트 가드 추가: ROUTES.recommend를 ProtectedRoute 안에 중첩해 인증은 됐지만 온보딩을 완료하지 않은 사용자가 접근하면 /profile/setup으로 리다이렉트
- ProtectedRoute와 동일한 state.from 규약을 사용해 마법사 완료 후 원래 가려던 경로(/recommend)로 복귀

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: 온보딩 1·2단계 필수값 미입력 시 다음 버튼 비활성화

- 출생연도·거주지역(1단계), 취업상태·학력(2단계)이 비어있으면 '다음 단계로' 버튼을 disabled 처리
- 비활성 시 회색으로 스타일 변경, hover 효과 제거(disabled:hover:brightness-100), 클릭 자체가 막혀 토스트 경고 불필요
- 마법사 자체는 '나중에 하기'로 언제든 건너뛸 수 있음(요구사항 변경 없음)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: 온보딩 '나중에 하기' 항상 홈으로 이동, 어색한 보류 안내 토스트 제거

- from(원래 가려던 경로)이 RequireOnboarded로 보호된 화면(예: 맞춤 추천)이면 스킵 후에도 즉시 다시
  온보딩으로 튕기는 문제가 있어, 스킵은 from을 무시하고 항상 홈으로 이동하도록 수정
- '설정 마법사가 일시적으로 보류되었습니다.' 토스트 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refac: 정책 매칭 조건 '제한없음' 매직스트링을 공유 상수로 추출

policy.dto.ts/policy.types.ts의 매칭 조건 필드 주석을 실제 의미에 맞게 보정하고,
K-디지털 트레이닝 mock 데이터에 majorCondition(공학계열) 케이스를 추가해
전공 조건 매칭 로직의 테스트 커버리지를 확보한다.

* fix: 추천 매칭 로직의 사유 오귀속 및 소프트 감점 문제 수정

- 자격상태 매칭 사유가 항상 employmentStatus 기준으로만 표시되던 것을
  실제 매칭된 축(employment/education)에 맞게 표시하도록 수정
- 연령/혼인상태/전공/소득 조건 불일치 시 감점만 하고 목록에는 남아있던
  문제를 hardIneligible 플래그로 명확히 제외하도록 수정

* fix: 소득 모름 체크 시 입력했던 연소득 값이 삭제되는 문제 수정

체크 해제 시 이전에 입력한 값이 복원되지 않던 문제를 annualIncome을
그대로 유지하도록 수정. matchesIncome은 incomeUnknown 플래그만으로
이미 중립 처리하므로 값 삭제가 불필요했다.

* fix: 추천 건수 하드코딩(28건) 제거하고 실제 매칭 건수로 표시

ProfileBriefing이 항상 "총 28건 추천"을 표시하던 것을 useRecommendations의
실제 계산 결과(recommendations.length)로 교체. 온보딩 완료 토스트도
확정되지 않은 건수를 미리 단언하던 문구를 일반 안내로 변경.

* fix: 추천 로딩/에러 중 매칭 수가 0건으로 잘못 표시되는 문제 수정

useRecommendations가 로딩 중엔 recommendations를 빈 배열로 반환해
ProfileBriefing이 "총 0건 추천"을 순간적으로 표시하던 문제를 수정.
로딩 중엔 스켈레톤, 실패 시엔 안내 문구를 보여주고 RecommendationFeed의
자체 로딩/에러 UI는 그대로 둔다.

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat: 커뮤니티 게시판 목록/상세, 댓글·대댓글 기능 추가

카테고리(전체/정책질문/정책후기/잡담) 탭과 검색을 지원하는 커뮤니티 목록,
게시글 상세 페이지, 로그인 사용자의 댓글·대댓글 작성 기능을 구현.
게시글 작성 UI는 에디터 라이브러리 도입 여부가 미정이라 이번 범위에서 제외.

Closes #17

* feat: 커뮤니티 게시글 좋아요 기능 추가

policy-bookmark 패턴을 참고해 features/community-like(mock API + 훅)를
추가하고, 목록/상세 카드에 좋아요 토글을 배선한다. feature 간 직접 의존을
피하기 위해 widgets/community-post-grid를 신설해 카테고리 탭·검색
(community-board)과 좋아요(community-like) 조합을 위젯 레이어에서
담당하도록 재구성한다.

* feat: 마이페이지에 좋아요한 게시글 모아보기 추가

RecentlyViewed 위젯 패턴을 참고해 widgets/liked-community-posts를 추가하고
마이페이지에 배치한다.

* feat: 본인 댓글 수정·삭제 및 아바타 표시 추가

- authorEmail로 댓글 작성자를 식별해 소유권을 판별하고, mock API에
  update/delete를 추가한다. 삭제는 ConfirmDialog로 확인한다.
- CommentRow를 도입해 댓글·대댓글에 이니셜 아바타를 표시하고,
  답글/수정/삭제 버튼을 작성자 프로필 행 오른쪽에 배치한다.
- 댓글 입력 폼에는 작성자 아바타를 넣지 않는다.

* refac: 댓글 입력 버튼을 인풋 옆으로 이동

textarea 아래에 있던 등록/취소 버튼을 textarea 옆(가로 배치)으로 옮긴다.
댓글·답글·수정 폼에 공통 적용된다.

* feat: 커뮤니티 게시글 정렬/작성 지원을 위한 entity 확장

- 정렬 옵션 모델(model/communityPostSort.ts)과 검색 파라미터의 sort 필드,
  mock API의 정렬 로직(조회수/댓글/좋아요/최신)을 추가한다.
- 좋아요순 정렬을 노출할 수 있도록 게시글에 likeCount(집계 좋아요 수)를
  추가하고 카드/상세 UI에 표시한다.
- 게시글 작성을 위해 mock 저장소를 mutable store로 전환하고
  createCommunityPost를 추가한다.

* feat: 커뮤니티 게시글 정렬 기능 추가

카테고리 탭 옆에 정렬 셀렉트(최신순/조회수순/댓글순/좋아요순)를 추가하고,
검색·카테고리와 동일하게 URL 쿼리스트링(?sort=)과 동기화한다.

* feat: 게시글 작성 임시 UX 추가

에디터 라이브러리 도입 전까지 사용할 임시 작성 화면(카테고리 선택 + 제목 +
내용)을 추가한다. /community/write는 로그인 사용자만 접근 가능하며,
등록 후 새 게시글 상세로 이동한다. 목록 페이지에 글쓰기 진입 버튼을 추가한다.

* fix: 댓글 입력 폼 인풋/버튼 세로 정렬 수정

items-end로 하단 정렬되어 있어 textarea와 버튼의 높이 차이만큼 어긋나
보이던 문제를 items-center로 바꾸고 버튼 padding/line-height를 조정해
정확히 중앙 정렬되도록 수정한다.

* refac: 커뮤니티 카테고리 탭 하단 구분선 제거

* fix: 좋아요 토글 시 집계 좋아요 수가 갱신되지 않던 문제 수정

좋아요 개인 상태(community-like)와 게시글의 집계 좋아요 수(likeCount)가
별도 mock 저장소로 분리되어 있어 토글해도 숫자가 그대로였다.
adjustCommunityPostLikeCount를 추가해 토글 시 게시글의 likeCount를
함께 갱신하고, 관련 쿼리를 invalidate해 목록/상세에 즉시 반영한다.

* refac: 게시글 상세 좋아요 수 표시를 목록과 동일하게 축소

목록 카드의 좋아요 버튼과 동일한 크기(text-[10px], h-3.5 w-3.5)로 맞춘다.

* fix: 댓글 등록/수정/삭제 실패 시 상태 유지 및 에러 토스트

- useCommunityCommentMutations의 각 mutation에 onError를 추가해 실패를
  토스트로 알리고, mutate 호출 시 옵션 콜백(onSuccess)을 전달할 수 있게 한다.
- CommentListContainer는 입력값/편집상태/삭제대상 초기화를 성공 콜백
  안에서만 수행하도록 바꿔, 실패해도 사용자가 입력을 잃지 않게 한다.
- ConfirmDialog에 confirmDisabled를 추가하고 삭제 진행 중(isDeleting)에는
  확인 버튼을 비활성화해 중복 삭제 요청을 막는다.

* fix: 게시글 작성 실패 시 에러 토스트 및 이동 방지

useCreateCommunityPost에 onError를 추가해 실패를 토스트로 알리고,
CommunityPostWriteContainer는 try/catch로 감싸 실패 시 unhandled
rejection과 잘못된 페이지 이동을 방지한다.

* fix: 좋아요 토글 정합성/캐시 범위/에러 노출 개선

- communityLikeApi: 지연(delay) 전에 현재 좋아요 상태를 읽도록 바꿔 짧은
  시간 내 중복 토글 시 잃어버린 갱신(lost update)을 방지한다.
- useCommunityLike: 좋아요 목록을 Set으로 변환해 isLiked 조회를 O(1)로
  만들고, 토글 성공 시 community-post 쿼리 전체를 무효화하는 대신 해당
  게시글의 likeCount만 캐시에서 직접 patch해 불필요한 refetch를 없앤다.
  또한 toggleMutation의 에러 상태(isToggling/toggleError)를 노출하고
  실패 시 토스트로 알린다.

* fix: 마이페이지 좋아요 목록 에러 상태 처리 추가

useCommunityLike/useCommunityPostSearchQuery의 isError를 반영하지 않아
조회 실패가 빈 목록("아직 좋아요한 게시글이 없습니다")으로 오인되던 문제를
수정한다. 로딩/에러/빈/성공 상태를 분리해 처리한다.

* fix: 댓글 아바타를 헤더 프로필과 동일한 단색으로 통일

MyPage 아바타의 그라데이션 스타일을 따라갔었는데, 실제 프로필 아이콘(헤더)은
단색(bg-primary)이므로 댓글 아바타도 동일하게 맞춘다.

* refac: 커뮤니티 접근성 개선

- 장식용 아이콘(검색, 조회수, 댓글수)에 aria-hidden 추가
- 좋아요 버튼 aria-label이 눌림 상태(좋아요/좋아요 취소)를 반영하도록 수정
- 답글 토글 버튼에 aria-expanded 추가
- 임시 작성 안내 배너에 role="status"/aria-live 추가
- 게시글 카드 루트를 article로, 목록을 ul/li로 바꿔 시맨틱 마크업 적용
- 카드 전체 덮개 버튼에 focus-visible 표시 추가

* refac: 카테고리/좋아요 타입 강화 및 잡다한 정확성 정리

- 카테고리를 식별할 수 없는 DTO는 개발 모드에서 console.warn으로 알려
  원인 파악 없이 게시글이 조용히 사라지는 문제를 완화한다.
- CreateCommunityPostParams.category를 CommunityPostCategory로,
  adjustCommunityPostLikeCount의 delta를 1 | -1로 좁혀 오용을 컴파일
  시점에 막는다.
- URL 쿼리스트링에서 온 카테고리 값을 normalizeCommunityPostCategory로
  검증해 알 수 없는 값이면 기본값(전체)으로 되돌린다.
- 게시글 생성 시각을 KST 기준으로 포맷해 자정 근처 UTC 오프셋으로 날짜가
  하루 어긋나는 문제를 막는다.
- buildCommunityDetailPath에서 postId를 encodeURIComponent로 감싼다.
- CommunityPage의 필터 초기화가 하드코딩된 '전체' 대신 공용 상수
  DEFAULT_CATEGORY_VALUE를 재사용하도록 정리한다.

* fix: 커뮤니티 검색어 입력 시 한글 조합 깨짐 수정

검색어 input의 value를 URL(searchParams)에 직접 물려 controlled하고
있어, 매 키 입력마다 라우터 리렌더를 거치면서 한글 조합(IME) 입력이
중간에 깨지는 문제가 있었다. 검색어를 로컬 state로 관리해 입력은 항상
즉시·동기적으로 반영되게 하고, URL에는 디바운스로만 반영해 공유 가능한
상태는 유지한다.

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
* refac: 정책 카드 찜하기를 관심(별 아이콘/노란색)으로 변경

Heart(하트)/rose 색상을 Star(별)/yellow 색상으로 바꾸고 라벨을 "관심"으로 통일한다. 같은 isSaved 상태를 쓰는 정책 상세 모달의 토글 버튼도 함께 맞춘다.

* refac: 신청 관리에서 결과대기 상태 삭제

TrackerStatus에서 결과대기를 제거하고 상태 탭·카드 뱃지·상세 패널 옵션·mock 데이터의 관련 참조를 정리한다. 마이페이지 활동 지표의 결과 대기목록 카드는 신청완료 카드로 대체한다.

* fix: 신청 관리 빈 상태 문구에서 신청 일감을 정책으로 수정

* refac: 정책 상세 모달 관심 토글 버튼 문구를 관심으로 통일

PolicyCard는 이미 상태와 무관하게 "관심"으로 고정 표기하는데, 모달만 담김/담기로 남아있던 불일치를 정리한다.

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
* refac: 검색어 URL 동기화 로직을 공용 훅으로 통합

정책 검색은 즉시 반영, 커뮤니티 검색은 로컬 state + setTimeout 지연 반영으로
서로 다르게 구현되어 있던 URL 동기화 방식을 useUrlSyncedSearchQuery로 통합한다.
IME 조합 중 값이 깨지는 문제를 방지하는 커뮤니티 쪽 방식을 기준으로 맞췄다.

* refac: SEARCH_DEBOUNCE_MS 상수 중복 제거

usePolicySearch, useCommunityBoard 양쪽에 하드코딩되어 있던
디바운스 상수를 shared/constants로 옮겨 공유한다.

* refac: mock API 검색어 매칭 로직 공유 유틸로 정리

policyApi, communityPostApi에 각각 유사하게 구현되어 있던 자유 검색어
매칭(trim/lowercase/includes) 로직을 matchesTextQuery로 추출한다.
cloneDto는 DTO shape이 서로 달라(정책은 details 배열 포함) 도메인별로 유지한다.

* refac: 커뮤니티 게시글 검색을 제출(버튼/Enter) 시에만 실행되도록 변경

입력할 때마다 즉시 검색되던 것을, 정책 검색과 동일하게 검색어 입력은
draftQuery로만 반영하고 "검색하기" 버튼 또는 Enter를 눌렀을 때만
실제 검색과 URL(q)에 반영되도록 변경한다.

* refac: 정책 검색도 제출(버튼/Enter) 시에만 실행되도록 변경

usePolicySearch도 커뮤니티와 동일하게 draftQuery + 제출 시 반영 방식으로
바꿔 세 검색 진입점(홈 검색, 정책 검색, 커뮤니티 검색) 모두 버튼을 눌러야
검색되도록 통일한다.

디바운스 기반 자동 검색이 더 이상 필요 없어져 useDebouncedValue,
useUrlSyncedSearchQuery, SEARCH_DEBOUNCE_MS를 제거하고, 정책·커뮤니티가
공유하는 useSubmittableUrlQuery(draft state + 제출 시 URL 반영)로 합친다.

* fix: 검색 전체 초기화 시 setSearchParams 중복 호출로 필터가 안 지워지던 버그 수정

resetFilters({clearQuery:true})/커뮤니티 초기화에서 필터 삭제용 setSearchParams와
검색어 삭제용 setSearchParams를 따로 호출하면, react-router의 setSearchParams가
호출 시점의 동일한 URL 스냅샷을 기준으로 계산하기 때문에 나중 호출이 앞선 호출의
변경을 덮어써 필터가 그대로 남아 있었다. 필터·검색어 삭제를 하나의 setSearchParams
호출로 합치고, 입력창 초기화는 draftQuery만 별도로 비우도록 수정한다.

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
* refac: 마이페이지 최근 본 정책 전체보기 버튼, 위험 설정 구역 타이틀 삭제

Closes #29

* refac: '가중치' 용어를 '맞춤 프로필 정보' 워딩으로 변경

마이페이지에서 이미 쓰는 '맞춤 프로필 정보' 표현과 통일해 사용자에게
기술 용어 대신 자연스러운 워딩을 노출한다.

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
rules.md가 정본 상세 규칙인데도 AGENTS.md에서 마크다운 링크로만 참조돼
Claude Code 세션(@AGENTS.md import)에는 로드되지 않던 문제를 해결한다.
에이전트 하네스 설정과 같은 위치인 .claude/로 옮기고 CLAUDE.md에
@.claude/rules.md import를 추가해 자동 로드되게 하며, 관련 경로/문구
참조를 모두 갱신한다.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat: 정책질문·정책후기 게시글에 정책 첨부 기능 추가

정책 검색 모달로 정책을 선택해 게시글에 첨부하고, 상세 화면에서
기존 정책 상세 모달을 재사용해 바로 확인할 수 있게 한다.

Closes #31

* refac: 첨부 카테고리 상수 통합, EmptyState 재사용, 모달 포커스 트랩

- POLICY_ATTACHABLE_CATEGORIES 중복 정의를 entities/community-post 공개 API로
  추출해 Container/Form이 같은 정의를 참조하도록 통일
- PolicyAttachModal의 빈 검색 결과를 shared EmptyState로 교체
- PolicyAttachModal에 초기 포커스, Tab 순환, 닫힐 때 트리거로 포커스 복원 추가

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
CommunityPost.commentCount는 mock 고정값이라 댓글·답글을 추가/삭제해도
카드·상세 화면 숫자가 갱신되지 않았다. useCommunityCommentCounts로
실제 댓글 수(대댓글 포함)를 조회해 표시값을 덮어쓴다.

Closes #35

Co-authored-by: imi21123 <imi21123@gmail.com>
CompareDockContainer가 홈/검색/추천 페이지에 각각 렌더링되어 페이지 이동마다
언마운트·재마운트되며 슬라이드-인 애니메이션이 반복 재생되던 문제를,
ChatbotContainer처럼 RootLayout에 상시 마운트하는 방식으로 수정.
또한 패널이 접힌 상태에서 정책을 추가해도 펼쳐지지 않던 문제를 고쳐,
접힌 상태에서 담으면 자동으로 펼쳐지도록 함.

Closes #37

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat: 관리자 페이지 공통 인프라 구축

@tanstack/react-table 기반 재사용 DataTable, 관리자 서브 라우팅(/admin/logs/*,
/admin/users 등)과 AdminLayout 사이드 네비게이션, entities/login-history 및
entities/application-log 슬라이스(mock API 포함)를 추가한다. 이후 이슈들이
채울 8개 관리자 섹션은 준비 중 placeholder로 라우팅만 먼저 연결한다.

Closes #39

* fix: DataTable 코드래빗 리뷰 반영

- 정렬 상태를 controlled props(sorting/onSortingChange)로 분리하고
  manualSorting을 켜 페이지 단위 데이터를 클라이언트에서 재정렬하지
  않도록 수정 (서버/쿼리 파라미터 기준 정렬은 호출부 책임)
- 로딩 스켈레톤 행 수를 pageSize에 맞춰 동적으로 렌더링
- 정렬 가능 헤더에 aria-sort 속성 추가

* fix: mock 페이지네이션 page 경계값 clamp 처리

page가 1 미만이거나 마지막 페이지를 넘는 값으로 들어와도 안전하게
[1, pageCount] 범위로 clamp하는 shared/utils/paginate를 추가하고,
login-history/application-log mock API의 중복 slice 로직을 여기로
통합한다.
CRLF/LF 혼용으로 인한 diff 오염을 막기 위해 텍스트 파일을 항상 LF로
고정한다. Biome는 LF로 저장하지만 core.autocrlf=true 환경에서는
checkout 시 CRLF로 강제 변환되어 저장할 때마다 줄바꿈이 뒤바뀌는
문제가 있었다.

Closes #52
* fix: 비로그인 상태에서 관심 버튼 클릭 시 로그인 페이지로 이동

Closes #48

* fix: 로그인 리다이렉트 시 히스토리에 항목 추가되지 않도록 replace 처리

CodeRabbit 리뷰(PR #51) 반영

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
* feat: AuthUser id·CommunityPost authorId 추가로 작성자 식별 기반 마련

내가 작성한 글 목록을 authorName 문자열 매칭이 아닌 안정적인 id로 필터링할 수 있도록
AuthUser.id와 CommunityPost(Dto).authorId를 추가하고 글 작성/검색 전 구간에 반영했다.

* feat: 좋아요한 글·내가 작성한 글 전용 목록 페이지 추가

/my/liked, /my/posts 라우트를 신설하고 두 페이지가 공유하는 CommunityPostListPage
shell(+ 기존 CommunityPostGrid 재사용)로 UI를 통일했다. 마이페이지에는 각 섹션에
전체보기 버튼을 연결하고, 좋아요한 글 섹션은 미리보기 5건으로 제한했으며
"내가 작성한 글" 미리보기 섹션을 새로 추가했다.

Closes #49

* fix: 사용자 id 없을 때 내가 작성한 글 조회에서 전체 글이 노출되던 문제 수정

authorId를 빈 문자열로 넘기면 필터 조건이 무시되어(falsy) 전체 게시글이
반환됐다. useCommunityPostSearchQuery에 enabled 옵션을 추가하고, userId가
없으면 쿼리 자체를 비활성화하도록 고쳤다.

CodeRabbit 리뷰(PR #54) 반영

* refac: 좋아요한 글·내가 작성한 글 미리보기 위젯 공통 shell로 통합

두 위젯의 카드 컨테이너, 헤더(제목+전체보기), 로딩/에러/빈 상태, 리스트 렌더링이
거의 동일해 CommunityPostPreviewList(entities/community-post/ui)로 추출했다.
행 끝 콘텐츠(좋아요 취소 버튼 vs 작성일)만 renderTrailing으로 위젯별로 주입한다.

CodeRabbit 리뷰(PR #54) 반영

---------

Co-authored-by: imi21123 <imi21123@gmail.com>
* feat: 관리자 로그인 로그 뷰어 페이지 추가

/admin/logs/login에 features/admin-login-log를 연결해 LoginHistories
목록을 조회한다. 사용자 ID/기간 필터와 페이지네이션은 URL 쿼리스트링과
동기화되며, entities/login-history의 mock API에 기간(startDate/endDate)
필터를 추가했다. 공용 DEFAULT_ADMIN_PAGE_SIZE 상수와 formatDateTime
유틸을 shared에 추가해 이후 로그/엔티티 관리 페이지에서 재사용한다.

Closes #40

* fix: 로그인 로그 기간 필터가 런타임 로컬 타임존에 의존하던 문제 수정

코드래빗 리뷰 반영. startDate/endDate 경계값을 KST(+09:00) 오프셋을
명시해 생성하도록 고쳐, 서버/브라우저의 로컬 타임존이 KST가 아닐 때
발생할 수 있는 날짜 경계 오차를 없앤다.

* refac: useAdminLoginLogFilters의 userId draft 동기화를 useSubmittableUrlQuery로 교체
* feat: 관리자 애플리케이션 로그(log4j) 뷰어 페이지 추가

/admin/logs/application에 features/admin-application-log를 연결해
ApplicationLogs 목록을 조회한다. 로그 레벨/키워드(메시지·trace id·요청
경로)/기간 필터와 페이지네이션은 URL 쿼리스트링과 동기화되며,
entities/application-log의 mock API에 기간(startDate/endDate) 필터를
추가했다. 스택트레이스·예외 메시지 등 긴 텍스트는 목록 대신 상세보기
모달에서 확인한다.

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40과 동일한 내용으로 이 브랜치에도 추가했다(#40 PR이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #41

* fix: 애플리케이션 로그 기간 필터가 런타임 로컬 타임존에 의존하던 문제 수정

#55에 달린 코드래빗 리뷰가 동일한 패턴(entities/login-history)에 대해
지적한 문제를 이 브랜치의 동일 코드에도 선제적으로 반영한다.
startDate/endDate 경계값에 KST(+09:00) 오프셋을 명시한다.

* fix: 애플리케이션 로그 키워드가 제출 전에도 즉시 필터링되던 문제 수정

#55 리뷰(useSubmittableUrlQuery 재사용 제안)를 반영하는 과정에서 발견:
useAdminApplicationLogFilters가 committed 값을 노출하지 않고 draft
값만 "keyword"로 내보내던 탓에, useAdminApplicationLog의 쿼리 파라미터가
draft(입력 중 값)를 그대로 사용해 키워드 입력 중에도 매 타이핑마다
재조회되고 있었다. "제출했을 때만 반영한다"는 의도(주석)와 어긋난다.

committed keyword와 draftKeyword를 각각 노출하도록 고쳐, 쿼리는
committed 값을, 입력창 표시는 draft 값을 쓰도록 분리했다
(entities/login-history 쪽의 올바른 패턴과 동일하게 맞춤). 브라우저에서
제출 전 타이핑만으로는 목록이 바뀌지 않고, 제출 시에만 필터링되는 것을
확인했다.

* fix: 애플리케이션 로그 상세 모달에 스크롤 잠금/Escape/배경 클릭 닫기 추가

리뷰(사람) 반영. CompareDetailDialog/PolicyAttachModal이 갖추고 있는
useBodyScrollLock + Escape 키 닫기 + 배경 클릭 닫기 패턴이 이 모달에는
빠져 있었다. 같은 패턴을 그대로 적용한다.
* feat: 관리자 사용자 관리 페이지 추가

/admin/users에 features/admin-user를 연결해 Users 목록을 조회한다.
역할/계정 상태/가입경로 필터와 페이지네이션은 URL 쿼리스트링과
동기화된다. 상세보기 모달에서 UserProfiles 온보딩 정보를 함께 보여주고,
역할 변경·탈퇴 처리(soft delete) 관리 액션은 ConfirmDialog 확인 후에만
실행된다.

entities/user에 관리자용 mock API(fetchAdminUsers/fetchAdminUserProfile/
updateAdminUserRole/softDeleteAdminUser)와 model/mapper/query 훅을 추가했다.

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40/#41과 동일한 내용으로 이 브랜치에도 추가했다(선행 PR들이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #42

* fix: 사용자 상세 모달이 필터 조건에서 벗어나면 안내 없이 사라지던 문제 수정

리뷰(사람) 반영. selectedUser를 필터링된 users 목록에서 .find()로
파생하고 있어서, role 변경/탈퇴 처리 성공 후 목록이 refetch될 때 그
사용자가 현재 필터(역할/상태)에서 더 이상 걸리지 않으면 selectedUser가
null이 되어 모달이 갑자기 닫혔다. 브라우저에서 "일반 회원" 필터로
사용자를 관리자로 변경 → 목록에서는 사라지지만 모달은 유지되며 변경된
역할을 보여주는 것을 확인했다.

- selectedUser를 useAdminUser에서 별도 상태로 들고, 목록에서 파생하지
  않는다
- mutation 성공 시 응답으로 받은 최신 사용자 값을 selectedUser에 직접
  반영한다(onUpdated 콜백)
- roleMutation/deleteMutation에 onError 추가, 실패 시 안내 토스트
  (useCommunityCommentMutations 패턴과 동일하게 'warning' 사용)
- AdminUserDetailModal에 useBodyScrollLock + Escape 키 닫기 + 배경 클릭
  닫기 추가(CompareDetailDialog/PolicyAttachModal 패턴)
* feat: 관리자 정책 관리 페이지 추가

/admin/policies에 features/admin-policy를 연결해 Policies 목록을
조회한다. 카테고리/공개상태/신청기간 필터와 페이지네이션은 URL
쿼리스트링과 동기화된다. 상세 모달에서 정책 정보(정책명/주관기관/설명/
대분류/중분류/신청기간/URL)를 수정하고 저장할 수 있으며, PolicyRegions
매핑은 지역 선택 체크박스 UI로 관리한다. 노출 상태(visibility_status)
토글은 즉시 반영되고, soft delete(deleted_at) 처리는 ConfirmDialog
확인 후에만 실행된다.

entities/policy에 관리자용 mock API(fetchAdminPolicies/updateAdminPolicy/
setAdminPolicyVisibility/softDeleteAdminPolicy)와 model/mapper/query
훅을 추가했다. Regions 참조 데이터는 신규 entities/region 슬라이스로
분리했다(Users/Policies 양쪽에서 공유하는 도메인이라 승격).

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40~#42와 동일한 내용으로 이 브랜치에도 추가했다(선행 PR들이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #43

* fix: 정책 상세 모달의 편집 중 draft가 다른 mutation으로 초기화되던 문제 및 필터 이탈 시 모달이 사라지던 문제 수정

리뷰(사람) 반영.

1. useEffect(() => setDraft(policy), [policy])가 policy 객체 참조가
   바뀔 때마다(내용이 같아도) draft를 서버 값으로 덮어썼다. 노출 상태
   토글처럼 폼 제출과 무관한 별도 mutation이 성공해 목록이 refetch되기만
   해도 편집 중이던 입력값이 조용히 사라지는 문제였다. policy.id 기준
   ref로 "다른 정책으로 바뀔 때만" 초기화하도록 고쳤다.

2. #57과 같은 패턴: selectedPolicy를 필터링된 policies 목록에서
   .find()로 파생하고 있어서, 노출 상태 토글/저장/삭제 성공 후 그 정책이
   현재 필터(카테고리/공개상태) 조건에서 빠지면 모달이 안내 없이
   사라졌다. selectedPolicy를 별도 상태로 들고, mutation 성공 시 응답
   값으로 직접 갱신하도록 고쳤다.

브라우저에서 "노출" 필터 상태로 정책명을 수정하다가 "비노출로 전환"을
클릭 → 모달이 유지되고 방금 입력한 수정 내용도 그대로 남아있는 것을
확인했다.

- saveChanges/toggleVisibility/deletePolicy 세 mutation 모두에 onError
  추가, 실패 시 안내 토스트('warning')
- AdminPolicyDetailModal에 useBodyScrollLock + Escape 키 닫기 + 배경
  클릭 닫기 추가(CompareDetailDialog/PolicyAttachModal 패턴)
* feat: 관리자 정책 신청 관리 페이지 추가

/admin/applications에 features/admin-application을 연결해
PolicyApplications 목록을 조회한다. 사용자 ID/정책명(제출형 검색)/상태/
마감일 필터와 페이지네이션은 URL 쿼리스트링과 동기화된다. 상세 모달에서
ApplicationChecklists 항목을 조회하고, 신청 상태 변경은 ConfirmDialog
확인 후에만 실행된다.

신규 entities/policy-application 슬라이스에 관리자용 mock API
(fetchAdminPolicyApplications/fetchApplicationChecklist/
updateAdminPolicyApplicationStatus)와 model/mapper/query 훅을 추가했다.
기존 사용자용 features/policy-tracker(신청관리, policyId 단일 키 모델)와는
별개로 관리자 CRUD용 도메인 모델을 새로 두었다 — ERD 기준 필드(user_id,
policy_id, status, memo, deadline)를 갖춘 구조가 필요해서다.

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40~#43과 동일한 내용으로 이 브랜치에도 추가했다(선행 PR들이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #44

* fix: 정책 신청 상세 모달이 상태 필터에서 벗어나면 안내 없이 사라지던 문제 수정

리뷰(사람) 반영. #57/#58과 같은 패턴: selectedApplication을 필터링된
applications 목록에서 .find()로 파생하고 있어서, 상태 변경 성공 후 그
신청이 현재 상태 필터에서 빠지면 모달이 안내 없이 사라졌다. 브라우저에서
"관심" 필터 상태로 신청 상태를 "종료"로 변경 → 목록에서는 사라지지만
모달은 유지되며 변경된 상태와 체크리스트를 보여주는 것을 확인했다.

- selectedApplication을 useAdminApplication에서 별도 상태로 들고,
  목록에서 파생하지 않는다
- mutation 성공 시 응답으로 받은 최신 값을 selectedApplication에 직접
  반영한다(onUpdated 콜백)
- statusMutation에 onError 추가, 실패 시 안내 토스트('warning')
- AdminApplicationDetailModal에 useBodyScrollLock + Escape 키 닫기 +
  배경 클릭 닫기 추가(CompareDetailDialog/PolicyAttachModal 패턴)
* feat: 관리자 커뮤니티 관리 페이지 추가

/admin/community에 features/admin-community를 연결해 Posts 목록을
조회한다. 카테고리/작성자(제출형 검색)/작성일 필터와 페이지네이션은
URL 쿼리스트링과 동기화된다. 상세 모달에서 Comments 트리(대댓글 포함)와
Attachments 목록을 확인할 수 있고, 게시글/댓글 soft delete(deleted_at)는
각각 ConfirmDialog 확인 후에만 실행된다.

entities/community-post를 확장해 관리자용 mock API
(fetchAdminCommunityPosts/fetchAdminCommunityComments/
fetchAdminCommunityAttachments/softDeleteAdminCommunityPost/
softDeleteAdminCommunityComment)와 model/mapper/query 훅을 추가했다.
기존 features/community-comment(사용자용, CommunityComment 모델)는
feature라 admin에서 직접 import할 수 없어(feature→feature 금지) ERD
기준 deleted_at을 포함한 별도 Admin 모델을 entities/community-post에
두었다. Attachments는 기존에 대응 기능이 없어 새로 추가했다.

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40~#44와 동일한 내용으로 이 브랜치에도 추가했다(선행 PR들이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #45

* fix: 커뮤니티 관리 코드래빗 리뷰 반영

- 댓글/첨부파일 조회 실패(isError)를 노출하지 않아 빈 상태로 오인되던
  문제 수정: isCommentsError/isAttachmentsError와 재조회 함수를 추가하고
  모달에 인라인 에러+재시도 UI를 표시한다
- 게시글/댓글 soft delete mutation에 onError 핸들러 추가, 실패 시 토스트
  안내(프로젝트에 'error' 토스트 타입이 없어 warning으로 처리)
- 목록의 작성일 컬럼이 포맷 없이 원본 값을 노출하던 것을 formatDateTime
  으로 통일해 상세 모달과 표시 형식을 맞춤
- 댓글 트리 들여쓰기의 임의 인라인 px 값(depth * 16)을 Tailwind 표준
  spacing 클래스(ml-0~ml-16, 깊이 4단계 이후 고정)로 교체

* fix: 게시글 작성일 오표시 및 상세 모달 접근성 누락 수정

리뷰(사람) 반영.

1. post.createdAt은 날짜만 있는 값(mock: '2026-07-08')인데 formatDateTime으로
   포맷하면 UTC 자정으로 파싱 후 Asia/Seoul 변환 과정에서 실제로는 없는
   09:00이 모든 게시글에 동일하게 붙어 보였다. 비관리자 커뮤니티 화면
   (CommunityPostCard 등)이 같은 필드를 원본 그대로 표시하는 기존 컨벤션과
   동일하게, 목록 컬럼과 상세 모달 모두 post.createdAt을 가공 없이 표시하도록
   되돌렸다. 댓글 createdAt은 실제 시:분을 가진 전체 타임스탬프라 formatDateTime
   유지.

2. #56~#59에 이어 이 모달에도 배경 스크롤 잠금(useBodyScrollLock), Escape 키
   닫기, 배경 클릭 닫기가 빠져 있어 동일 패턴으로 추가했다.
* feat: 관리자 검색 로그 / 배치 작업 로그 뷰어 페이지 추가

/admin/logs/search에 features/admin-search-log를 연결해 SearchLogs
목록을 조회한다. 키워드(제출형 검색)/기간 필터와 페이지네이션은 URL
쿼리스트링과 동기화된다.

/admin/logs/batch에 features/admin-batch-log를 연결해 BatchJobLogs
목록을 조회한다. 상태(SUCCESS/PARTIAL/FAILED)/기간 필터와 페이지네이션을
지원한다.

기존 features/admin-sync의 동기화 이력(SyncLog/SyncHistoryTable)을
ERD 기준 entities/batch-job-log로 이관하고 새 DataTable로 교체 통합했다.
- SyncHistoryTable.tsx 삭제, SyncLog/SyncStatus 타입 제거
- useSyncHistory.ts를 useSyncSummary.ts로 정리(요약 카드 조회만 남김)
- useRunSync가 이제 entities/batch-job-log의 runBatchJobSync를 호출해
  수동 동기화 시 새 배치 작업 로그 한 건을 생성하고, /admin/logs/batch
  쿼리를 무효화한다
- 대시보드(/admin)에는 인라인 이력 표 대신 "배치 작업 로그 전체 보기"
  링크 카드를 추가했다

entities/search-log, entities/batch-job-log 신규 슬라이스에 mock
API/model/mapper/query 훅을 추가했다.

shared/utils/formatDateTime, shared/constants/DEFAULT_ADMIN_PAGE_SIZE는
#40~#45와 동일한 내용으로 이 브랜치에도 추가했다(선행 PR들이 아직 dev에
머지되지 않아 발생한 임시 중복이며, 먼저 머지되는 쪽 기준으로 자연히
정리된다).

Closes #46

* fix: 검색 로그 키워드 제출 시 page가 초기화되지 않던 문제 수정

리뷰(Kilo Code) 반영. useSubmittableUrlQuery의 submitQuery는 keyword만
갱신하고 page는 그대로 둬서, 같은 화면의 기간 필터·배치 로그 필터와
달리 깊은 페이지에서 검색해도 URL의 page가 남아있었다. keyword 제출과
page 삭제를 한 번의 setSearchParams로 묶어(연속 호출 시 서로 덮어쓰는
문제를 피하기 위해) 처리하도록 고쳤다. 브라우저에서 2페이지 상태로
검색어를 제출하면 URL에서 page 파라미터가 사라지는 것을 확인했다.

* fix: 배치 동기화 수동 실행 실패 시 안내 토스트 누락 수정

리뷰(사람) 반영. useRunSync가 성공 토스트만 처리하고 onError가 없어,
관리자가 명시적으로 트리거하는 수동 동기화가 실패해도 아무 피드백이
없었다. useCommunityCommentMutations 패턴대로 onError에 실패 안내
토스트(warning)를 추가했다.
* feat: 공용 Pagination 컴포넌트 추가 및 주요 목록 화면 적용

메인/정책 찾기/맞춤 목록/커뮤니티/신청관리 화면마다 목록을 한 번에 다 불러와
보여주기만 해서 페이지 이동 UI가 없었다. shared/ui/Pagination과
shared/hooks/usePagination을 추가하고 5개 화면에 적용했다.

Closes #62

* test: 페이지네이션 확인용 mock 데이터 보강

정책 6→15개, 커뮤니티 글 6→16개, 좋아요한 글 2→7개, 신청관리 트래커 3→8개로
늘려 각 화면의 페이지 크기(6/6/6/5)를 넘겨 실제로 2페이지 이상 보이게 한다.
정책 상세를 자유 텍스트 배열(details) 하나로 뭉뚱그려 보여주던 것을
지원내용/신청자격/신청방법/제출서류/심사방법/참여제한사항 등 필드별
섹션으로 분리해 표시한다. 값이 없는 필드는 자동으로 숨긴다.

Closes #64

Co-authored-by: imi21123 <imi21123@gmail.com>
sjungwon03 and others added 26 commits July 23, 2026 01:47
# Conflicts:
#	src/entities/user/model/authStore.test.ts
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
- HomePage의 추천 분기를 HomeRecommendSection 프레젠터로 분리하고
  로딩 조건에서 !profile을 제거해 온보딩 미완료 Empty 분기를 추가
- GuestRecommendCta를 RecommendSetupCta로 이름 변경해 비로그인/온보딩
  미완료 두 경우에 재사용(라벨만 분기)
- RecommendPage의 동일한 !profile → Skeleton 패턴을 EmptyState + 설정 CTA로 교체
- HomeRecommendSection 분기 단위 테스트 추가

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
추천 피드가 FEED_COUNT=8로 목록을 잘라내서, 헤더의 "통합 매칭 수"(119건)와
실제 렌더되는 카드 수(8개)가 어긋나고 나머지 매칭 정책에 접근할 방법이 없었다.
데이터는 이미 전부 클라이언트에 도착해 있으므로 표시 단계만 고친다.

- slice 제거 후 공용 usePagination(recommendations, 8)로 교체
- 그리드 하단에 공용 Pagination 배치, 상단에 총 N건 / 현재·전체 페이지 표시
- Loading/Error/Empty 분기와 Degraded fallback(6건 → 1페이지) 동작 유지
- RecommendationFeed 렌더 테스트 추가

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
여러 요청이 동시에 401을 받으면 요청마다 독립적으로 notifySessionExpired가
호출돼 토스트가 중복으로 쌓였다. 로그인/로그아웃 1회당 한 번만 알리도록
dedupe 가드를 추가하고, 직접 로그아웃할 때는 이미 로그아웃 토스트를 봤으므로
세션 만료 알림을 억제한다.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat: 게시글 상세 정책 위치 변경 및 목록 정책/요약 노출

- CommunityPostDetail: 첨부 정책 카드를 본문 위로 이동
- policyTitle을 DTO/도메인 모델에 보존하고, 목록 카드가 attachedPolicy
  대신 policyTitle로 첨부 정책 배지를 표시하도록 변경(목록 응답에는
  category/deadline이 없어 attachedPolicy가 항상 null이었음)
- 백엔드가 추가한 contentExcerpt(YouthPick/back-end#201)를 매핑해 목록
  카드 본문 요약을 채움
- CommunityPostPreviewList(마이페이지 미리보기)도 동일하게 정책/요약 표시

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: contentExcerpt 미수신 시 content 안전한 기본값 처리

백엔드 배포(YouthPick/back-end#202) 전이나 필드가 빠진 응답에서
dto.contentExcerpt가 undefined면 content도 undefined가 되던 문제를
수정한다. 코드 리뷰 지적 반영.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
백엔드 자격 필터 적용 전에 붙였던 "세부 자격조건 연동 전이라 일부 부적격 정책이
포함될 수 있어요" 고지가 홈 미리보기와 맞춤 정책 페이지에 그대로 남아 있어,
현재 동작과 다른 안내를 하고 추천 신뢰도를 실제보다 낮게 인식시켰다.

- 중복 문구를 RecommendationNotice로 모으고 자격 조건 기반 매칭 안내로 정정
- fetchRecommendations가 isFallback을 함께 반환해 추천 엔진 장애로 최신 정책을
  대신 보여주는 상태에서는 매칭을 단정하지 않도록 분기(규칙 10 Degraded)
- 헤더 메뉴 탭 라벨 맞춤 추천 -> 맞춤 정책, 탭을 지칭하는 문구도 함께 정리

Co-authored-by: imi21123 <imi21123@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: 게스트의 정상 401을 세션 만료로 오인해 토스트 노출하는 문제 수정

홈 화면의 맞춤 추천/내 프로필처럼 로그인 없이도 호출되는 회원 전용 API가
게스트에게 401을 반환하면, refresh 시도가 실패했다는 이유만으로
notifySessionExpired가 무조건 호출돼 "세션이 만료되었습니다" 토스트가 떴다.
이 경우는 세션이 있다가 만료된 게 아니라 애초에 세션이 없었던 정상적인
케이스이므로, refresh 시도 전 has_logged_in_hint 존재 여부로 두 상황을
구분해 원래 세션이 있던 경우에만 notifySessionExpired를 호출한다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: 홈 화면에서 비로그인 사용자는 회원 전용 API를 아예 호출하지 않음

이전 커밋은 401 인터셉터가 세션 만료 토스트를 잘못 띄우는 것만 막았는데,
근본적으로는 로그인하지 않은 사용자에게 회원 전용 API(추천/프로필)를 호출할
필요 자체가 없다. useRecommendations/useMyProfile(Query)에 enabled 옵션을
추가하고 홈 화면에서 isAuthenticated로 게이팅해, 게스트는 이 요청을 아예
보내지 않도록 한다. 로그인 필수 화면(예: /recommend, 마이페이지)은 항상
enabled=true 기본값을 쓰므로 동작이 그대로 유지된다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
- communityCommentApi의 mock CRUD를 apiClient 호출로 교체
- DTO/타입을 백엔드 응답(authorId/authorNickname, parentId)에 맞게 갱신
- 댓글 작성자 판별을 authorEmail 대신 authorId 비교로 변경
- 신규 에러코드(B006/B007/B008) 메시지 매핑 추가
- 게시글 목록의 댓글 수 mock은 백엔드 벌크 조회 API 부재로 별도 스코프 유지

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
runBatchJobSync가 POST /v1/admin/batch-job-logs를 호출했는데, 백엔드
AdminBatchJobLogController에는 이 경로에 GET(목록 조회)만 있고 POST가
없어 버튼을 눌러도 405로 실패했다. 실제 수동 트리거 엔드포인트인
POST /v1/admin/batch/policy-sync(AdminPolicySyncController, body 없음,
비동기 202)로 연동을 바꾼다.

Closes #234

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
/api 프리픽스 밖에 있는 백엔드 springdoc 경로(/swagger-ui/**,
/swagger-ui.html, /v3/api-docs)가 SPA fallback(index.html)으로
떨어져 배포 도메인에서 열리지 않던 문제를 backend로 직접 프록시하여
해결한다.

Closes #244
/v3/api-docs를 정확히 일치(location =)로 매칭해 swagger-ui가 실제
스펙 위치를 알아내려고 요청하는 /v3/api-docs/swagger-config 가
SPA fallback(index.html)으로 떨어졌다. prefix 매칭으로 바꿔
하위 경로도 backend로 프록시되게 한다.

Closes #246
@sjungwon03
sjungwon03 requested a review from imi21123 July 24, 2026 09:01
@sjungwon03 sjungwon03 self-assigned this Jul 24, 2026
@sjungwon03 sjungwon03 added the release 릴리즈 label Jul 24, 2026
@sjungwon03
sjungwon03 merged commit d2d350b into main Jul 24, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release 릴리즈

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants