Skip to content

fix: D-Day 라벨 off-by-one 수정 + 주간랭킹 수동부여 점수 포함#73

Closed
bbbang105 wants to merge 84 commits intomainfrom
dev
Closed

fix: D-Day 라벨 off-by-one 수정 + 주간랭킹 수동부여 점수 포함#73
bbbang105 wants to merge 84 commits intomainfrom
dev

Conversation

@bbbang105
Copy link
Copy Markdown
Owner

Summary

  • D-Day 라벨: days <= 1days <= 0 (마감 하루 전에 D-Day로 표시되던 버그 수정)
  • 주간랭킹 Discord 메시지: webActivityScoreADMIN_MANUAL 타입 추가 (수동부여 점수가 활동 점수 괄호 안에 표시)

Test plan

  • 마감일 하루 전 대시보드에서 D-1 표시 확인
  • 마감 당일 D-Day 표시 확인
  • 수동부여 점수가 있는 멤버의 주간랭킹 활동 점수에 포함 확인

🤖 Generated with Claude Code

bbbang105 and others added 30 commits February 23, 2026 16:17
* docs: 프로젝트 README 및 CLAUDE.md를 작성한다

- README.md: 프로젝트 소개, 기술 스택, 시작 가이드, 문서 링크
- CLAUDE.md: Claude Code 프로젝트 설정, 코딩 컨벤션, 개발 커맨드

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

* style: 웹 UI를 Vercel 스타일로 전면 리디자인한다

- globals.css: sky-blue 디자인 토큰 (light/dark)
- layout.tsx: Pretendard 폰트, ThemeProvider 다크모드
- tailwind.config.ts: success, warning, sidebar 토큰 추가
- sidebar: 접기/펼치기, sky-blue 활성 인디케이터, 모바일 드로어
- header: DarkModeToggle, 페이지 타이틀 자동 매핑
- footer: 미니멀 1줄 푸터
- badge: success/warning variant를 디자인 토큰으로 변경
- landing: Vercel 스타일 히어로 + 6개 기능 카드
- login: BS 로고, 디자인 토큰 기반 폼
- dashboard: stat 카드 + 활동 피드 리디자인
- posts: 컴팩트 테이블, 미니멀 페이지네이션
- ranking: 포디움 카드, 고스트 소트 토글
- curation: 고스트 필터, 소프트 카테고리 뱃지
- profile: 플랫 카드, 아이콘 필, 토큰 기반 뱃지
- admin dashboard: 디자인 토큰 통일, 고스트 퀵 액션

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

* refactor: 봇 커맨드 파일명을 한글에서 영문으로 변경한다

- 핑.ts → ping.ts, 참가.ts → join.ts, 탈퇴.ts → leave.ts
- 참가자목록.ts → member-list.ts, 내정보.ts → my-info.ts
- 랭킹.ts → ranking.ts, 현황.ts → status.ts, 통계.ts → stats.ts
- 관심분야.ts → interests.ts
- admin/휴면.ts → dormant.ts, 휴면해제.ts → dormant-release.ts
- admin/벌금전체.ts → fine-all.ts, 벌금면제.ts → fine-exempt.ts
- admin/벌금현황.ts → fine-status.ts, 출석수정.ts → attendance-edit.ts
- admin/설정.ts → settings.ts, 큐레이션소스.ts → curation-source.ts
- index.ts barrel export 경로 업데이트

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

* fix: 코드 스멜 및 미사용 코드를 정리한다

봇 패키지:
- getMembersToPolll 타이포 수정 → getMembersToPoll
- isGracePeriodEnded_ 트레일링 언더스코어 제거
- JSDoc 주석 /* → /** 수정
- dm-handler type: string → 'late' | 'absent' 유니온 타입
- round-reporter _client → client 네이밍 정리
- ephemeral: false 불필요한 명시적 기본값 20건 제거
- STUDY_ROLE_NAME, getRankEmoji 중복 제거 → constants.ts 통합
- dead middleware 디렉터리 제거

웹 패키지:
- register, verify-email: 하드코딩 컬러 → 디자인 토큰
- admin/attendance, settings, curation, fines, members: 디자인 토큰 통일
- profile/edit: text-green-600 → text-success

shared 패키지:
- types/index.ts 중복 enum 정의 제거 (schema.ts as const와 충돌)

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

* chore: 기존 프로젝트 기반 코드를 추가한다

- 루트: pnpm workspace 설정, ESLint, Prettier, TypeScript 설정
- packages/bot: Discord 봇 (커맨드, 서비스, 스케줄러, 핸들러, 테스트)
- packages/shared: DB 스키마 (Drizzle), 설정, 유틸리티, 마이그레이션
- packages/web: Next.js 대시보드 (API 라우트, UI 컴포넌트, 미들웨어)
- 불필요 파일 제거: 루트 Dockerfile.bot, railway.json, debug-test.mjs, package-lock.json

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

* ci: 깃허브 세팅

* chore: CI 워크플로우 추가 및 safe 의존성 버전 업데이트

- GitHub Actions CI 파이프라인 추가 (lint, typecheck, test, build)
- pnpm + Node 22 기반 워크플로우 구성
- typescript ^5.9.3, discord.js ^14.25.1, Radix UI 등 safe 범위 버전 업데이트

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

* chore: pnpm lockfile 갱신

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

* fix: 테스트 파일 미사용 import 제거

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

* fix: CI 파이프라인 수정

- typecheck 전 shared 패키지 빌드 추가 (dist 의존성 해결)
- test에서 web 제외 (테스트 파일 없음)
- 미사용 변수 제거 (rss-poller.property.test.ts)

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

* fix: CI 타입체크 및 테스트 오류 수정

- fine-reminder.ts: fine.type을 'late' | 'absent'로 타입 캐스팅
- rss-poller.property.test.ts: getMembersToPolll 오타를 getMembersToPoll로 수정
- CI test job에 shared 빌드 스텝 추가 (bot 테스트가 shared/db 의존)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* feat: Supabase Auth + Discord OAuth로 인증 시스템 전환

커스텀 JWT(bcrypt + jsonwebtoken + jose) 인증을 Supabase Auth + Discord OAuth로 교체.

- Supabase SSR 클라이언트 헬퍼 추가 (client, server, middleware)
- OAuth 콜백 라우트 추가 (/auth/callback)
- 로그인 페이지를 Discord OAuth 버튼으로 교체
- 모든 API 라우트를 Supabase Auth 기반으로 리팩토링
- 미들웨어를 Supabase 세션 검증으로 교체
- admin 라우트에 Discord ID 기반 권한 체크 추가
- DB 스키마에서 users/sessions 테이블 제거
- Transaction pooler 지원 (prepare: false)
- 오픈 리다이렉트, profileImageUrl 검증 등 보안 강화
- 불필요한 파일 삭제 (auth.ts, email.ts, register, verify-email 등)

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

* fix: 코드 리뷰 반영 - 인증 패턴 통일, ID 추출 버그 수정, 미사용 스키마 제거

- admin API(settings, curation, curation/[id])에 withAdminAuth 래퍼 적용
- members/[id] URL 파싱 시 쿼리파라미터 버그 수정 (new URL().pathname 사용)
- middleware.ts 관리자 ID 파싱 로직 중복 제거 → isAdminDiscordId() 재사용
- env.ts에서 미사용 authEnvSchema(JWT), emailEnvSchema(Resend) 제거
- CLAUDE.md 최신화

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Root Directory가 packages/web로 설정된 환경에서
outputDirectory가 packages/web/.next로 이중 경로가 되는 문제 수정

Co-Authored-By: Claude <noreply@anthropic.com>
* feat: Supabase Auth 연동 및 코드 정리

- Discord OAuth 콜백 라우트 추가
- /api/auth/me에 userId 반환 추가
- 온보딩 API 검증 로직 강화
- 파트 설정 모듈 분리 (part-config.ts)
- 미사용 봇 커맨드 정리

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

* feat: Supabase Storage 프로필 이미지 업로드

- 아바타 업로드 유틸리티 추가 (2MB, JPG/PNG/WebP 제한)
- AvatarUpload 컴포넌트 추가 (드래그&드롭, 미리보기, 로딩)
- 온보딩 Step 3 URL 입력 → 파일 업로드로 교체
- 프로필 수정 페이지 URL 입력 → 파일 업로드로 교체
- 프로필 수정에 파트 변경, 자기소개(100~200자), 관심사(3~6개) 통일
- 프로필 수정 API에 파트 필드 및 자기소개 100자 검증 추가

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

* feat: 헤더 사용자 메뉴 및 사이드바 UI 개선

- 헤더 우측 아바타 클릭 시 드롭다운 메뉴 (프로필 수정, 로그아웃)
- 로고를 사이드바에서 헤더로 이동, 클릭 시 대시보드 이동
- 모바일 사이드바에도 로고 표시
- 사이드바에서 프로필 메뉴 제거 (드롭다운으로 대체)
- 사이드바 폰트 크기 및 간격 확대
- 프로필 페이지 이메일 미인증 표시 제거
- 한줄 소개 → 자기소개 명칭 통일
- Tailwind content에 lib 경로 추가 (파트 칩 색상 적용)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
- 사이드바에 '스터디원 목록' 메뉴 추가 및 카드형 멤버 목록 페이지 구현
- 파트별 필터링, 소셜 링크 칩, DiceBear 기본 아바타 적용
- 헤더 드롭다운 '프로필 수정' → '프로필' 조회 페이지로 변경
- 소셜 링크(GitHub/LinkedIn/Instagram) 필드 추가 (스키마, API, UI)
- 랜덤 아바타 뽑기 기능 (DiceBear 5개 스타일, 8개 생성)
- name/nickname 분리: 기존 name → nickname 리네임, 새 name(실명) 필드 추가
- 온보딩/프로필 수정에서 실명(필수) + 닉네임 입력 지원
- Discord username #0 접미사 제거 처리

Co-authored-by: Claude <noreply@anthropic.com>
* chore: Next.js 16.1 + React 19.2 업그레이드

- next 14.2 → 16.1.6, react 18.3 → 19.2.4
- middleware.ts → proxy.ts 리네임 (Next.js 16 컨벤션)
- next lint → eslint CLI 마이그레이션 (eslint.config.mjs 추가)
- React 19 ESLint 규칙 대응:
  - header: useSyncExternalStore로 mounted 패턴 교체
  - sidebar: SidebarContent 컴포넌트 외부 추출
  - input: 빈 인터페이스 → type alias
- layout.tsx에 data-scroll-behavior="smooth" 추가

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

* chore: Tailwind CSS v3 → v4.2 업그레이드

- tailwindcss v3.4 → v4.2.1, postcss 플러그인 → @tailwindcss/postcss
- tailwindcss-animate → tw-animate-css 교체
- tailwind.config.ts 삭제 → globals.css @theme 지시어로 마이그레이션
- @tailwind 지시어 → @import "tailwindcss" 변경
- autoprefixer 제거 (v4에 내장)
- 컴포넌트 Tailwind v4 클래스명 마이그레이션 (outline-none → outline-hidden 등)
- components.json tailwind config 경로 비우기

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

* chore: next-env.d.ts 경로 업데이트 (Next.js 16 dev 디렉토리)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* refactor: rss-parser → feedsmith 마이그레이션

- rss-parser(deprecated) 제거, feedsmith(TypeScript 네이티브) 추가
- RssService: axios fetch + parseFeed 분리 구조로 변경
- RSS/Atom/JSON/RDF 전체 피드 포맷 지원 (extractFeedItems 헬퍼)

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

* refactor: node-cron → pg-boss 마이그레이션

- node-cron 제거, pg-boss(PostgreSQL 기반 잡 큐) 추가
- 5개 스케줄러에서 cron 의존성 제거, 비즈니스 로직만 유지
- pg-boss 싱글톤 모듈 (job-queue.ts) 생성
- 7개 잡 레지스트리 (scheduler-registry.ts) 생성
- RSS 폴링 → PostService.create → NotificationService 파이프라인 연결
- DATABASE_URL_DIRECT 환경변수 추가 (pg-boss LISTEN/NOTIFY용)

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

* feat: 스케줄러 연결 (pg-boss 잡 큐 → index.ts 통합)

- index.ts에서 pg-boss 시작 + 7개 잡 등록 연결
- graceful shutdown에 pg-boss 정리 추가 (onShutdown 콜백)
- RSS 폴링, 출석체크, 벌금알림, 회차리포트, 큐레이션 모두 활성화

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

* docs: CLAUDE.md 기술 스택 및 핵심 파일 최신화

- Bot 기술 스택: feedsmith, pg-boss로 업데이트
- 핵심 파일에 job-queue.ts, scheduler-registry.ts 추가
- 환경변수에 DATABASE_URL_DIRECT 추가

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

* fix: rss-poller 테스트에서 제거된 stop() 호출 수정

- afterEach에서 poller.stop() → vi.clearAllMocks()로 변경
- node-cron 제거 후 stop() 메서드가 없어 발생한 테스트 실패 수정

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

* fix: 모노레포 환경에서 .env.local 로딩 경로 수정

- dotenv 로딩 시 cwd + 상위 디렉토리까지 탐색하도록 변경
- packages/* 에서 실행해도 루트 .env.local을 찾을 수 있음

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

* feat: 큐레이션 시스템 개선 — RSS 크롤링, 태그, 수동 크롤 지원

- curation_sources에 tags, rss_url 컬럼 추가
- INTEREST_OPTIONS 상수를 shared 패키지로 추출
- POST/PATCH API에 tags, rssUrl 필드 지원 + RSS URL 자동 감지
- 수동 크롤링 API 엔드포인트 추가 (POST /api/admin/curation/crawl)
- 관리자 페이지에 태그 선택기, RSS URL 입력, 크롤링 실행 버튼 추가
- 봇 curation-crawl 스케줄 KST 8시로 변경 (cron: 0 23 * * *)
- 봇 CurationCrawler에 feedsmith 기반 RSS crawlFunction 연결

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

* fix: Velog URL 패턴 수정, RSS 감지, 대시보드/멤버 API 개선

- Velog /posts 경로 지원 (url-validator, rss.service)
- 웹 RSS URL 자동 감지 유틸 추가 (rss-detect.ts)
- 대시보드 API memberName → memberNickname 수정
- 멤버 API 정렬/필터 개선
- 포스트 목록 페이지 개선
- RSS 테스트/폴링 스크립트 추가

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

* feat: 큐레이션 아이템 관리 뷰, 크롤링 진행 모달, 페이지네이션

- 관리자 아이템 목록 API/페이지 추가 (카드 그리드, 필터, 삭제)
- 크롤링 API를 SSE 스트리밍으로 전환, 진행률 모달 구현
- 크롤링 시 기간 필터(since) 지원
- 사용자 큐레이션 페이지에 12개씩 페이지네이션 + 번호 UI
- 태그 색상을 프라이머리 단일 색상으로 통일 (admin/user 공통)
- 태그 필터를 INTEREST_OPTIONS 공유 상수 기반으로 변경
- shadcn Dialog/Progress 컴포넌트 추가

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* refactor: 보안 강화, 반응형 개편, 모듈화 및 로그인 수정

- 세션 갱신 proxy.ts 생성 (Next.js 16 convention) → 로그인 간헐 실패 근본 수정
- 보안: Open Redirect 방지, SSRF 차단 (IPv4/IPv6), API 인증 체크 6건, 보안 헤더
- 모듈화: PageLoading/PageError, PartBadge, TagList, MEMBER_STATUS_CONFIG 공유 컴포넌트 추출
- 큐레이션 god component 분해 (1,087줄 → 220줄 + 3개 컴포넌트)
- 반응형: 전 페이지 모바일 카드뷰, 레이아웃 오버플로우 수정, 사이드바 상태 리팩터링
- 코드 품질: MutationObserver 제거, stale closure 수정, dead code 정리

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

* fix: 스터디원 목록 <a> 중첩 hydration 에러 수정

Link 컴포넌트 내부의 소셜 링크를 <a> → <span role="link">으로 변경하여
HTML spec 위반(<a> 안에 <a>) 및 hydration 에러 해결

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* feat: 활동 점수 시스템 + 랭킹 게이미피케이션 구축

- 활동 점수(activity_scores) 스키마 및 봇 핸들러/서비스 추가
- 랭킹 페이지: 포디움 + Top 10 + 탭별 동적 수치 (총점/포스트/활동)
- 탭별 회차 기반 등락 폭(rank delta) 계산
- 관리자 점수 관리 페이지 (부여/조회/요약 API)
- 보안 강화: IDOR 방지, 원자적 CTE, 입력 검증, sanitize 유틸
- Drizzle SQL 마이그레이션 gitignore 처리

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

* feat: 관리자 목록 조회 + 토글 기능 (env + config 병합)

- admin.ts: getAdminDiscordIds async 변환, env + config 테이블 병합
- settings API: adminMembers, currentUserDiscordId 반환
- settings 페이지: 관리자 뱃지 리스트 + 셀프 토글 버튼
- scores route: isAdminDiscordId await 적용

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
- 글 조회 시 활동 점수 부여 (2점, 일 5회, 중복/본인 글 제외)
- post_views 테이블 + POST_VIEW 점수 타입 추가
- RSS 자동 수집 동의 토글 (온보딩/프로필 편집)
- RSS 비동의 시 수동 글 등록 (URL → OG 크롤링, 실패 시 제목 직접 입력)
- 관리자 점수 내역 삭제 기능 (커스텀 확인 다이얼로그)
- 관리자 멤버 목록에 RSS ON/OFF 배지 표시
- 대시보드 최근 포스트 클릭 시에도 조회 점수 부여
- Serena 설정 추가 (.serena/)

Co-authored-by: Claude <noreply@anthropic.com>
* feat: MemberStatus enum에 pending_approval, inactive, ob 상태 추가

회원 승인 시스템 기반 작업. 기존 active/dormant/withdrawn에
pending_approval(승인대기), inactive(비활성), ob(OB) 상태를 추가.

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

* feat: member-config에 신규 상태 설정 추가

pending_approval(승인대기/warning), inactive(비활성/destructive),
ob(OB/outline) 상태 및 Badge variant 매핑 추가.

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

* feat: /api/auth/me 응답에 멤버 status 필드 추가

프론트엔드에서 멤버 상태(pending_approval, active, inactive 등)에 따라
리다이렉트 처리를 할 수 있도록 status 필드를 응답에 포함

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

* feat: 온보딩 신규 유저 status를 pending_approval로 변경

신규 유저가 온보딩을 완료하면 즉시 active가 아닌 pending_approval 상태로
설정하여 관리자 승인 후에만 대시보드에 접근할 수 있도록 함
기존 유저의 UPDATE 케이스는 변경하지 않음

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

* feat: OAuth 콜백에 멤버 상태별 리다이렉트 추가

로그인 시 멤버 status를 조회하여 상태에 따라 적절한 페이지로 리다이렉트:
- pending_approval → /pending (승인 대기 페이지)
- inactive → /inactive (비활성 안내 페이지)
- active → /dashboard (기존 동작 유지)

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

* feat: UserLayout에 상태 기반 리다이렉트 추가

pending_approval, inactive 상태의 유저를 각각 /pending, /inactive
페이지로 리다이렉트. 차단 페이지 자체는 예외 처리하여 무한 루프 방지.

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

* feat: /pending 승인대기 페이지 생성

pending_approval 상태 유저가 보는 대기 화면.
스카이블루 Clock 아이콘, 안내 메시지, 로그아웃 버튼 포함.

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

* feat: /inactive 비활성 계정 페이지 생성

inactive 상태 유저가 보는 차단 화면.
레드 ShieldX 아이콘, 안내 메시지, 로그아웃 버튼 포함.

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

* feat: 관리자 API 6개 상태 지원 확장 + 프로필 필드 추가

- GET /api/admin/members: grouped/counts에 pending_approval, inactive, ob 추가
- GET /api/admin/members: 응답에 nickname, interests, resolution, onboardingCompleted 추가
- PUT /api/admin/members/[id]: validStatuses에 6개 상태 전체 포함
- 그룹핑 로직을 동적 키 방식으로 리팩토링

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

* feat: 관리자 멤버 페이지 UI 개편 + 승인대기 카드 + 상태 편집

- Task 11: 4개 통계 카드 → 6개 상태 필터 탭 (pill 버튼) 교체
  - pending_approval, active, ob, dormant, inactive 탭 추가
  - 승인대기 탭 선택 시 카드 그리드 뷰로 전환
  - Member 인터페이스에 nickname, bio, interests, resolution,
    onboardingCompleted 필드 추가
  - MemberCounts/MembersData에 6개 상태 모두 포함
  - handleApproveMember / handleRejectMember 핸들러 추가

- Task 12: PendingMemberCard 컴포넌트 신규 생성
  - 프로필 이미지, 이름, 닉네임, 파트, 블로그 링크 표시
  - bio, interests, resolution 선택적 렌더링
  - 승인(활성/OB 선택) / 거절 2단계 액션

- Task 14: MemberFormDialog에 상태 편집 필드 추가
  - 편집 모드에서만 상태 select 표시
  - MEMBER_STATUS_CONFIG 기반 6개 상태 옵션
  - PUT 요청 body에 status 포함

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

* fix: groupedMembers 타입을 명시적 객체로 변경 (빌드 에러 수정)

Record<string, ...> 대신 개별 키를 as 타입 단언으로 선언하여
TypeScript strict 모드에서 undefined 가능성 제거.

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

* fix: 차단 상태 리다이렉트 시 콘텐츠 깜빡임 방지

리다이렉트 중에도 finally에서 onboardingChecked가 true로 설정되어
children이 잠깐 렌더링되던 문제 수정. 리다이렉트 시에는 로딩 화면을
유지하도록 setOnboardingChecked(true)를 성공 경로에서만 호출.

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

* fix: 페이지 이동 시 onboardingChecked 리셋하여 콘텐츠 깜빡임 방지

pathname 변경 시 onboardingChecked를 false로 리셋하여
API 체크 완료 전까지 로딩 화면 유지.

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

* fix: lint error - useEffect 내 동기 setState 제거

onboardingChecked boolean 대신 checkedPathname으로 변경하여
pathname 변경 시 자동으로 로딩 상태가 되도록 개선.
react-hooks/set-state-in-effect 룰 위반 해결.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* feat: 게시판 DB 스키마 추가 (board_posts, board_comments)

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

* chore: Tiptap 에디터 의존성 추가

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

* feat: 게시판 인증 헬퍼 (getBoardAuth)

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

* feat: 게시글 목록/작성 API (GET/POST /api/board)

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

* feat: 게시글 상세/수정/삭제 API (GET/PATCH/DELETE /api/board/[id])

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

* feat: 댓글 CRUD API (작성/수정/삭제)

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

* feat: Tiptap 에디터 + 렌더러 컴포넌트

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

* feat: 사이드바 게시판 메뉴 + board-config + UI 컴포넌트 추가

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

* feat: 게시판 목록 페이지

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

* feat: 게시글 작성 페이지 (Tiptap 에디터)

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

* feat: 게시글 상세 페이지 + 댓글 컴포넌트

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

* feat: 게시글 수정 페이지

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

* fix: API 라우트 타입 에러 수정 (unused params, nullable count)

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

* fix: Tiptap SSR hydration 에러 수정 + CLAUDE.md 업데이트

- Tiptap useEditor에 immediatelyRender: false 추가 (SSR 호환)
- CLAUDE.md에 env 파일 위치, DB 마이그레이션 명령어, 멤버 상태 규칙 추가

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

* feat: 관리자 페이지 접근 거부 시 모달 안내 추가

리다이렉트만 하던 방식에서 AlertDialog 모달로 "관리자만 접근할 수 있습니다" 안내 후 확인 버튼으로 이동하도록 변경

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

* refactor: 전체 로딩 상태를 스켈레톤 UI로 리팩터링

- shadcn Skeleton 컴포넌트 추가
- PageLoading(스피너+텍스트)을 각 페이지별 맞춤 스켈레톤으로 교체
- 대시보드, 게시판, 랭킹, 프로필, 멤버, 포스트, 관리자 페이지 전체 적용
- 레이아웃(user/admin) 로딩도 스켈레톤으로 변경
- 점수 관리 내부 "불러오는 중..." 텍스트도 스켈레톤으로 교체

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

* fix: 게시판 행 클릭 이동 + 프로필 이미지 클릭 시 멤버 상세보기

- 공지/일반 글 행 전체 클릭 시 게시글 상세로 이동 (TableRow onClick)
- 프로필 이미지(아바타) 클릭 시 /members/[id]로 이동 (stopPropagation)
- 비밀글 마스킹된 경우 아바타 링크 비활성화

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

* feat: MemberAvatar 컴포넌트 + 관리자 뱃지 + 비밀댓글 답글 제한

- MemberAvatar 재사용 컴포넌트 생성 (프로필 사진+닉네임 클릭 시 멤버 상세)
- 관리자 칩 표시 (게시판 목록/상세/댓글)
- 비밀댓글 대댓글: 댓글작성자/글작성자/관리자만 가능, 강제 비밀 적용
- 중첩 <a> 태그 hydration 에러 수정

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

* feat: 댓글 삭제 시 확인 모달 추가

- 삭제 버튼 클릭 시 AlertDialog로 확인 후 삭제
- 삭제 중 로딩 상태 표시 + 버튼 비활성화

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

* feat: Tiptap 에디터 개선 - typography 플러그인 + 코드블록 언어 선택 + 링크 다이얼로그

- @tailwindcss/typography 설치하여 prose 클래스 정상 동작
- 코드블록에 언어 선택 드롭다운 추가 (25개 언어, 구문 강조)
- GitHub 스타일 syntax highlighting (라이트/다크 모드)
- window.prompt → shadcn Dialog로 링크 입력 UI 개선
- 에디터 focus outline 제거 + 커서 색상 설정
- 에디터 최소 높이 200px → 400px 확대

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

* docs: CLAUDE.md 최신화 - 기술 스택 버전 + 게시판 핵심 파일 + 컨벤션 보강

- 기술 스택: Next.js 16, React 19, Tailwind v4, Tiptap 추가
- 핵심 파일: board-auth, api-error, member-avatar, tiptap-editor 추가
- 다이얼로그 컨벤션: window.prompt() 금지 추가

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

* fix: tiptap-editor lint 에러 수정 (no-explicit-any)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* feat: curationItems에 description, thumbnailUrl 컬럼 및 publishedAt 인덱스 추가

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

* feat: 크롤링 시 RSS description 저장 + og:image 추출

- NormalizedFeedItem에 description 필드 추가
- 각 피드 포맷(Atom/RSS/JSON/RDF)에서 description 추출
- sanitizeDescription: HTML 태그 제거 + 300자 truncate
- extractOgImage: 블로그 URL에서 og:image 메타태그 추출 (5초 타임아웃)
- curation_items INSERT 시 description, thumbnailUrl 저장

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

* feat: 큐레이션 API cursor 기반 페이지네이션 + description/thumbnailUrl 반환

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

* feat: 큐레이션 유틸리티 (그라디언트 플레이스홀더, 상대시간, scrollbar-hide)

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

* feat: 큐레이션 페이지 무한스크롤 + 반응형 레이아웃 리디자인

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

* fix: 큐레이션 cursor 페이지네이션 중복 문제 수정

- 단일 publishedAt cursor → 복합 cursor (publishedAt|id)로 변경
- 동일 publishedAt 아이템 간 id로 정확한 경계 구분
- publishedAt NULL 케이스 처리
- ORDER BY에 NULLS LAST 추가

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

* fix: cursor SQL에서 Date 객체 → ISO 문자열 캐스팅 수정

Drizzle sql 템플릿에서 Date 객체를 직접 바인딩하면
ERR_INVALID_ARG_TYPE 에러 발생. ISO 문자열 + ::timestamptz 캐스팅으로 수정.

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

* feat: 큐레이션 2차 개선 (맞춤 탭, 검색, 접근성, 보안 강화)

- 맞춤(recommended) 탭: 사용자 관심사 기반 태그 겹침 점수 정렬
- 검색 기능: 제목/설명 ILIKE 검색 + 300ms debounce
- 태그 # 접두사 + 카드 태그 칩 표시 + 4개 제한 제거
- 모바일 태그 토글 (접기/펼치기)
- 보안: ILIKE 와일드카드 이스케이프, 입력 검증, UUID 포맷 체크
- 접근성: aria-pressed/expanded/live, focus-visible, motion-safe
- 코드 품질: BASE_SELECT/serializeItem 추출, cursorRef 패턴
- 컨퍼런스 소스 시드 스크립트 추가

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

* fix: Thumbnail useEffect setState → 렌더 중 상태 조정 패턴으로 변경

react-hooks/set-state-in-effect 린트 에러 수정

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
* refactor: 미사용 코드 정리 및 API 응답 패턴 통일

- recharts 미사용 의존성 제거
- api/stats, api/keywords: 미사용 NextRequest import 제거
- api/dashboard: NextResponse.json → successResponse/Errors 헬퍼 통일
- auth/callback: console.log 12개 → dev 환경 가드 2개로 정리

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

* chore: AI/OpenAI 참조 제거 및 프로젝트 정보 현행화

- .env.example: OPENAI_API_KEY 항목 삭제
- README.md: AI/pgvector 텍스트 제거, 기술 스택 현행화 (Next.js 16, React 19, Tailwind v4)
- .gitignore: scripts/ 디렉토리 추가

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

* docs: 전체 문서 최신화 + 파일명 컨벤션 적용

- CLAUDE.md: AI 참조 제거, 절대경로→상대경로, 문서명 컨벤션 반영
- ARCHITECTURE.md: AI 파이프라인 제거, ER 다이어그램 전체 테이블 반영, 의존성 버전 추가
- TECH-DECISIONS: AI ADR 삭제, Next.js 16 반영
- DEVELOPMENT: OpenAI/pgvector 참조 제거
- CHECKLIST: Phase 2(pgvector)/5(AI) 삭제, Phase 6 현행화
- schema-summary: BoardCategory 값 수정, curation 컬럼 추가
- docs/ 파일명 yy-mm-dd 컨벤션 적용 (ARCHITECTURE.md 제외)
- docs/plans/ 파일명 2026- → 26- 변환
- 보안: plans 내 로컬 절대경로 → 상대경로 변환

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

* fix: 벌금 페이지 다이얼로그 패턴 수정 + 랜딩 페이지 AI 텍스트 제거

- window.confirm/alert → AlertDialog + 인라인 토스트로 교체 (컨벤션 준수)
- 랜딩 페이지 AI 요약 & 추천 → 활동 점수 & 랭킹으로 변경
- hero 서브텍스트에서 AI 참조 제거
- 저작권 연도 2025 → 2026 수정

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

* chore: 웹 개발 서버 포트 3300으로 변경

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

* feat: PWA 홈 화면 추가 지원 (manifest + 아이콘)

- manifest.json, 앱 아이콘(SVG/192/512) 추가
- layout.tsx에 PWA 메타 태그 (apple-web-app, theme-color)
- Service Worker 없는 가벼운 PWA (홈 화면 추가 전용)

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

* chore: lockfile 갱신 (recharts 제거 반영)

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

* remove: 스크립트 제거

* feat: CODEOWNERS & Auto Assign 추가

---------

Co-authored-by: Claude <noreply@anthropic.com>
- 디스코드 채널 구조 설계 및 생성 (5카테고리 13채널 + 운영 4채널)
- 회차 시작/종료 알림을 #공지사항(notice_channel)으로 분리
- 회차 시작 시 active 멤버만 개별 멘션
- notice_channel 미설정 시 announcement_channel로 폴백
- 채널 세팅 가이드 문서 추가
- CLAUDE.md, ARCHITECTURE.md 최신화

Co-authored-by: Claude <noreply@anthropic.com>
* feat: 공지 배너 + 풀투리프레시 + UX 개선 (#17)

- 랜딩 페이지 인증 리다이렉트 (로그인 시 /dashboard 자동 이동)
- 글로벌 공지 배너 (관리자 설정, 접기/닫기 localStorage 유지)
- Pull-to-refresh (커스텀 터치 제스처, PWA 최적화)
- 하단 탭 바 사용자/관리자 모드 분리
- 사이드바/헤더 관리자 토글 텍스트 통일
- 게시판 API 보안 강화 (트랜잭션, 카테고리 검증, 관리자 권한 체크)
- proxy.ts 관리자 인증 await 누락 수정
- 문서 최신화 (CLAUDE.md, 스키마, 패턴, 아키텍처)

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

* refactor: 사이드바 하단 사용자/관리자 토글 제거

프로필 드롭다운에서만 전환 가능하도록 통일.
Separator, 토글 링크 제거. 접기 버튼만 유지.

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

* fix: 관리자 모드 하단 탭 바 제거

관리자 페이지에서는 모바일 하단 탭 바를 표시하지 않음.
사용자 모드 5개 메뉴만 유지.

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

* Revert "fix: 관리자 모드 하단 탭 바 제거"

This reverts commit fecc8a4.

* fix: 관리자 대시보드 Quick Actions 링크 섹션 제거

사이드바에서 동일 메뉴 접근 가능하므로 중복 제거.

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

* fix: UI 워딩/표시 수정 4건

- 글 목록 → 포스트 (사이드바, 하단 탭)
- 관리자 페이지에서 공지 배너 미표시
- 참가자 관리 → 멤버 관리 (관리자 페이지 타이틀)
- 점수 관리 페이지 Star 아이콘 제거

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

* fix: 공지 배너에 제목+내용 미리보기 표시, 접기→닫기 통일

- API에서 contentText 추가 반환
- 배너에 제목 + 내용 80자 미리보기 표시
- 3단계 상태(open/collapsed/closed) → 2단계(open/closed)로 단순화
- X 버튼으로 닫으면 완전히 숨김 (새 공지 시 자동 리셋)

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

* fix: 공지 배너 접기 시 제목만 표시

- open: 제목 + 내용 미리보기
- collapsed: 제목만 (컴팩트)
- closed: 완전 숨김 (X 버튼)

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

* fix: 공지 배너 접힘 시 제목 폰트 크기 통일 (text-sm)

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

* feat: Tiptap 에디터 툴바에 H1-H3, 구분선, 본문 버튼 추가

- 본문(T): 일반 텍스트로 변환
- H1, H2, H3: 제목 토글
- 구분선(―): 수평선 삽입

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

* fix: Tiptap 에디터 한글 자소분리 문제 수정

compositionstart/end 이벤트로 IME 조합 중 onUpdate 호출을 차단.
조합 완료 후 rAF로 deferred update 실행.

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

* fix: 공지 배너 API 캐시 제거

max-age=60 캐시가 새 공지 반영을 지연시키는 문제 수정.
no-store로 변경하여 항상 최신 데이터 반환.

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

* fix: Pull-to-refresh를 window.location.reload()로 변경

router.refresh()는 서버 컴포넌트만 리페치하므로
클라이언트 데이터 갱신이 안 되는 문제 수정.

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

* docs: CLAUDE.md + patterns.md 최신화

- bottom-nav: 관리자 모드 미표시 반영
- 사이드바: 모드 전환 프로필 드롭다운 전용
- 공지 배너: 제목+내용 미리보기, no-store 캐시, 관리자 미표시
- PTR: window.location.reload() 반영
- Tiptap: H1-H3/구분선 툴바, 한글 IME 패턴 추가

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
- Dialog/AlertDialog: grid → flex flex-col, translate 센터링 → inset-y-0 my-auto
- overflow-y-auto 추가로 콘텐츠 클리핑 대신 스크롤 활성화
- slide 애니메이션 origin 48% → 제거 (새 센터링 방식과 불일치)
- WebkitOverflowScrolling 제거 (iOS 13 이후 no-op)
- globals.css: body[data-scroll-locked] overscroll-behavior-y: auto 추가
- use-pull-to-refresh: 다이얼로그 열림 시 제스처 비활성화
- crawl-modal: base DialogContent에서 flex flex-col 상속으로 중복 제거

Co-Authored-By: Claude <noreply@anthropic.com>
fix: Safari PWA 다이얼로그 스크롤 수정
- 모든 API 라우트를 Errors/successResponse/errorResponse 표준 패턴으로 통일
- withCache() 유틸 추가 (members 60s, ranking 30s Cache-Control)
- sonner 토스트 시스템 통합 (inline 토스트 제거)
- sanitizeTiptapContent()로 게시판 XSS 방어 (javascript:/data:/vbscript: 차단)
- isSafeUrl() SSRF 방어 (posts/manual, curation/crawl)
- CSP 헤더 설정 (next.config.js)
- 에러 바운더리 추가 (user/admin error.tsx)
- 커스텀 404 페이지 추가
- 미인증 API 엔드포인트 수정 (rounds, rounds/[id])
- posts/page.tsx 중복 fetchPosts를 useCallback으로 통합

Co-Authored-By: Claude <noreply@anthropic.com>
feat: 웹 API 표준화 + 보안 강화 + 에러 처리 개선
* feat: 랜딩 페이지 리디자인 + 커스텀 로고/파비콘

- Linear 스타일 다크 모드 원페이지 (7섹션: Nav/Hero/Stats/Bento/HowItWorks/Marquee/CTA)
- 큐시즘 블루 그라디언트 (#0091FF → #004DFF) 전면 적용
- Framer Motion 풀 애니메이션 (FadeUp, StaggerContainer, CountUp, DrawLine)
- 커스텀 SVG 픽토그램 로고 (펜촉+화살표) + PWA 아이콘 (192/512, maskable)
- DB 스탯 ISR 60s (활성 멤버수, 포스트수, 회차)
- DiceBear fun-emoji 아바타 마키 소셜 프루프
- prefers-reduced-motion 접근성 대응
- themeColor 라이트/다크 분기, statusBarStyle black-translucent

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

* style: 벤토 그리드 → 균등 3x2 카드 그리드로 변경

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

* style: 로고 리디자인 — K 레터마크 + 펜 스트로크 모티프

큐시즘 블루 그라디언트 (#0091FF → #004DFF)
볼드 지오메트릭 K + 하단 대각선에 펜촉 디테일

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

* style: 레트로 터미널 K 로고 + 큐스팅 4th 브랜딩 + 푸터 크레딧

- 로고를 레트로 터미널 K 레터마크로 리디자인 (CRT 스캔라인, 커서 블록, 프롬프트 마커)
- 서비스명 '큐스팅 4th'로 통일 (Nav, Header, Footer)
- 푸터에 개발자 크레딧 추가 (@bbbang105 & @choihooo)
- PWA 아이콘 재생성 (icon-192.png, icon-512.png)

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
- 블로그 스터디 / 블로그 글쓰기 스터디 → 큐스팅 4th 일괄 변경
- 로그인 페이지 BS 텍스트 → icon.svg 로고로 교체
- title, description, manifest, logo.svg, header, footer, dashboard 등 전체 반영

Co-Authored-By: Claude <noreply@anthropic.com>
- 로컬 .claude/skills 디렉토리에 10개 전문 스킬 추가
- discord-js-skills 패키지 구조로 코드 템플릿 제공
- 검증 스크립트로 스킬 품질 관리
- TypeScript 기반 완전한 구현 예시 포함

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- 삭제: commands-manager (슬래시 커맨드 시스템)
- 삭제: discord-client (이미 완성된 설정 존재)
- 삭제: music-bot (프로젝트 범위 범위 벗어남)
- 삭제: channel-manager (setup-channels.ts 스크립트로 대체)
- 삭제: guild-manager (단일 서버 전용, 웹에서 관리)
- 삭제: webhooks-manager (현재 요구사항 없음)
- 삭제: moderation-tools (웹 관리자 페이지에서 처리)
- 삭제: message-handler (activity-handler.ts로 충분)

보관:
- events-handler (새로운 이벤트 핸들러 구현 시 필수)
- member-manager (멤버 관련 고급 기능 구현 시 유용)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
삭제:
- member-manager (DB 중심 설계와 상충)

단순화:
- events-handler: 복잡한 클래스 패턴 → 실용적 참고 문서로 변경

신규 추가:
- scheduler-reference: pg-boss 스케줄러 구현 참고
  * Singleton 패턴, isRunning 플래그
  * Cron 잡 등록, Worker 등록
  * Discord 클라이언트 연동 예시
  * 프로젝트 기존 스케줄러 참고

- service-pattern: DB 서비스 계층 구현 참고
  * Singleton 패턴, CRUD 기본 패턴
  * 에러 처리, 커스텀 에러 클래스
  * 복잡한 쿼리 (조인, 집계, 그룹화)
  * 트랜잭션 패턴
  * Property-Based Test 예시

최종 보관 스킬 (3개):
1. events-handler: 새로운 이벤트 핸들러 구현 참고
2. scheduler-reference: 새로운 스케줄러 구현 참고
3. service-pattern: 새로운 서비스 구현 참고

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor: 스킬 시스템 정리 및 프로젝트 참고용 스킬 추가
* feat: 주간 랭킹 자동 발송 스케줄러 구현

- WeeklyRanking 스케줄러 추가 (매주 일요일 22:00 KST)
- RankingService 추가 (활동 점수 기반 랭킹 계산)
- Property-Based Test 15개 통과
- Discord Embed 메시지 포맷 (포디엄 + TOP 15)
- KST 날짜 처리 (월요일 00:00 ~ 일요일 23:59)

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

* fix: pg-boss 큐 초기화 문제 해결

- boss.createQueue() 내부 API로 큐 명시적 생성
- weekly-ranking 스케줄러 활성화 (8개 스케줄러 정상 등록)
- job-queue.ts에 pg-boss 설정 추가 (_uuid: 'v1')
- scheduler-registry.ts에 큐 생성 로직 추가

문제: boss.schedule()은 큐를 자동 생성하지 않음
해결: boss.work() 전에 boss.createQueue()로 큐 먼저 생성

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

* docs: 주간 랭킹 구현 플랜 및 pg-boss 문제 해결 가이드 추가

- 26-03-09-weekly-ranking-implementation.md: 구현 플랜 (상태: 완료)
- 26-03-09-pg-boss-queue-troubleshooting.md: 문제 해결 가이드

pg-boss 문제 해결 과정:
1. boss.work() 후 boss.schedule() 순서 변경 (실패)
2. 대기 시간 추가 (실패)
3. boss.send()로 더미 잡 전송 (실패)
4. pg-boss 설정 변경 (실패)
5. boss.createQueue() 내부 API 사용 (성공)

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

* fix: 린트 에러 수정

- @ts-ignore@ts-expect-error로 변경
- 사용하지 않는 discordIdArb 변수 제거

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

* fix: CI 타입 체크 에러 수정

- PgBoss 생성자 두 번째 인자 제거 (_uuid 설정)
- 사용하지 않는 @ts-expect-error 지시자 제거

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

* fix: 주간 랭킹 스케줄러 코드 리뷰 수정 사항 반영

## 수정 내용

### 🔴 심각한 이슈
- **KST 날짜 처리 불일치 수정**: `formatKSTDate()` 헬퍼 함수 추가로 KST 시간에서 직접 날짜 포맷팅
- **활동 점수 쿼리 타입 안전성 개선**: 하드코딩된 문자열을 `ActivityScoreType` enum 상수로 변경

### 🟡 중간 우선순위 이슈
- **경쟁 조건 해결**: `isRunning` 플래그를 Promise 기반 뮤텍스(`runningLock`)로 대체하여 동시 실행 문제 해결
- **빈 랭킹 경고 추가**: `WeeklyRankingResult`에 `warnings` 필드 추가

### 🟢 낮은 우선순위 이슈
- **매직 넘버 제거**: 포스트 점수 상수 `BLOG_POST_SCORE_POINTS = 30` 추출
- 테스트 코드에서도 동일한 상수 사용

## 테스트 결과
- ✅ 타입 체크 통과
- ✅ 모든 테스트 통과 (133개 테스트)
- ✅ 린트 통과

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
bbbang105 and others added 14 commits March 23, 2026 01:04
- 블로그 원문 보기 + 큐스팅 웹에서 보기 버튼 추가
- 웹 수동등록 알림과 동일한 embed + button 형태로 통일

Co-authored-by: Claude <noreply@anthropic.com>
기존 PollEditor의 polls.length >= 1 제한을 제거하고,
투표가 이미 있어도 "투표 추가" 버튼으로 폼을 열 수 있도록 변경.

Co-authored-by: Claude <noreply@anthropic.com>
투표하지 않은 사용자에게 득표수/퍼센트/프로그레스바/투표자 목록을
숨기고, 투표 후 또는 마감 후에만 결과를 표시하도록 변경.

Co-authored-by: Claude <noreply@anthropic.com>
수동 등록 + RSS 수집 모두에서 새 글 등록 시 작성자 본인을 제외한
active/OB/dormant 멤버에게 FCM 푸시 알림을 발송한다.

- shared: NotificationType에 NEW_POST 추가
- web: 내부 API `/api/internal/new-post-push` (Bearer 인증, timing-safe 비교, rate limit 20/min, UUID/길이 검증)
- web: 수동 등록 시 별도 after() 블록에서 sendPushToMembers 호출 (Discord 토글과 독립)
- bot: RSS 새 글 감지 시 웹 내부 API fire-and-forget 호출
- web: 알림 설정 UI에 '새 글 알림' 토글 추가
- web: 테스트 푸시에 new_post 타입 추가
- web: decodeHtmlEntities 유틸 추가 (RSS 제목 HTML 엔티티 디코딩)
- docs: CLAUDE.md, ARCHITECTURE.md 최신화

Co-authored-by: Claude <noreply@anthropic.com>
- 새 글 DB 저장 완료 로그 추가
- 블로그 포스트 점수 부여 로그 추가
- 푸시 알림 성공/스킵 로그 추가
- '포스트 알림' → '디스코드 알림'으로 로그 메시지 명확화

Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
* feat(web/bot): 랭킹에서 관리자 및 지정 멤버 제외

- 웹 랭킹 API: 관리자 Discord ID + config `ranking_excluded_ids` 기반 필터링
- 봇 주간 랭킹: env `ADMIN_DISCORD_IDS` + config `ranking_excluded_ids` 기반 필터링
- config 테이블로 제외 대상 관리 (코드 변경 없이 DB에서 추가/제거 가능)

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

* feat(web): 프로필 페이지 UX 개선

- 프로필 수정 버튼을 헤더 우측으로 이동 (하단 → 상단)
- 푸시 알림 미허용 시 유도 말풍선 표시 (벨 펄스 애니메이션 + 알림 켜기 CTA)
- 알림 켜기 성공 시 상세 설정 페이지로 자동 이동

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
statusBarStyle을 black-translucent에서 default로 변경하여
콘텐츠가 iOS 상태바(시간/배터리) 영역 뒤로 올라가는 문제 해결.
Toaster offset도 하단 내비 safe area를 고려하도록 추가.

Co-Authored-By: Claude <noreply@anthropic.com>
- RSS 폴링 시 멤버별 배치 URL 중복 체크 (IN query 1개로 변경)
  - 기존: 피드 아이템별 개별 SELECT (~344개/cycle)
  - 개선: 멤버당 1개 IN 쿼리 (29개/cycle)
- pino logger에 Error serializer 등록 (error: {} → 구조화된 출력)
- 에러 로깅 시 raw Error 객체 직접 전달 (스택 트레이스 보존)

Co-authored-by: Claude <noreply@anthropic.com>
- 투표 미참여자 관리: 참여/미참여 탭, 관리자 수동 DM 발송 (개별/전체)
- 마감 리마인더: D-2/D-1/D-day 미제출자 DM (자동 + 수동)
- 게시판 이미지: 읽기 모드에서 편집 UI 제거, 클릭 시 라이트박스 (확대/축소)
- 불릿 리스트 간격 축소 (prose li > p margin 제거)
- 봇 동작 제어: deadline-reminder 큐 생성 추가
- 코드 리뷰 반영: debug log 제거, null memberId 필터, race condition 수정

Co-authored-by: Claude <noreply@anthropic.com>
* feat(shared): add boardPostReactions table for emoji reactions

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

* feat(web): add board post reaction toggle API

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

* feat(web): include reactions data in board post GET response

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

* feat(web): add ReactionBar component and integrate into board detail page

- ReactionBar: emoji pill buttons with toggle, hover/long-press member popover
- Integrated into board detail page with reactions state and refresh
- Fixed TypeScript errors in reactions API route and board GET route

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

* fix(web): reaction API race condition + ReactionBar cleanup

- Wrap toggle in transaction to prevent race condition
- Add timer cleanup on component unmount
- Add aria-label and aria-pressed for accessibility
- Check deletedAt on post existence query

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

* feat(web): add emoji reactions to posts + ReactionBar UX improvements

- Add postReactions table and API (GET/POST /api/posts/[id]/reactions)
- Integrate ReactionBar into post detail page
- Add reaction chips with member popover to post list cards
- Add SmilePlus picker button to board ReactionBar
- Include reaction count in popular post scoring
- Add board notice content preview in Discord notifications (500 chars)

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

* fix(web): ReactionBar apiPath prop + fetchReactions order fix

- Add apiPath prop to ReactionBar (board/posts) to fix wrong API path
- Move fetchReactions before useEffect to fix declaration order
- Move REACTION_EMOJIS to module scope to avoid per-render allocation

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

* docs: update CLAUDE.md with emoji reactions conventions

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: D-Day 계산을 KST 캘린더 날짜 기준으로 수정

Math.ceil(timeDiff)에서 KST midnight 기준 날짜 차이로 변경하여
마감일 당일이 D-Day(0), 하루 전이 D-1로 정확히 표시되도록 수정.
dashboard, admin/dashboard, rounds API + shared 유틸 모두 통일.

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

* fix: 비밀답글 부모 댓글 작성자 가시성 추가

비밀 답글(isSecret)을 부모 댓글 작성자도 볼 수 있도록
게시판/포스트 댓글 API 마스킹 로직에 parentId 체크 추가.

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

* style(web): 랭킹 포디움 포부 텍스트 모바일 2줄 표시

truncate → line-clamp-2로 변경하여 모바일에서도
포부(resolution)가 2줄까지 보이도록 수정.

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

* feat: Discord 알림 로그 시스템 추가

봇/웹에서 Discord 채널 및 DM으로 보내는 모든 알림의
성공/실패를 DB에 기록하고 관리자 페이지에서 조회.

- discord_notification_logs 테이블 추가 (shared 스키마)
- 봇: 채널 알림 6종 + DM 5종 로깅 (notification-logger 헬퍼)
- 웹: discord-notify 반환타입 확장 + 호출자 로깅
- 관리자 API (GET /api/admin/bot-logs) + UI (탭 추가)
- 타입/소스/대상/상태별 필터 + 무한 스크롤

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

* fix(web): 리뷰 피드백 반영 — bot-logs 캐시 제거, cursor 검증, UI 접근성

- bot-logs API: withCache 제거 (관리자 민감 데이터), cursor 유효성 검증 추가
- notification-logs: IntersectionObserver deps 최적화 (isLoadingMore ref 전환)
- 필터 Select 반응형 너비 (모바일 대응)
- 상태 도트 크기 확대 + ring + aria-label 접근성 개선

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

* docs: CLAUDE.md 최신화 — 알림 로그, D-Day 수정, 비밀답글 가시성

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
- D-Day 라벨: days <= 1 → days <= 0 (하루 전 D-Day 표시 버그 수정)
- 주간랭킹: webActivityScore에 ADMIN_MANUAL 타입 추가

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
study-admin-web Ready Ready Preview, Comment Apr 5, 2026 2:19am

@github-actions github-actions Bot requested a review from choihooo April 5, 2026 02:18
@bbbang105 bbbang105 added ⚒️ chore 빌드 부분 혹은 패키지 매니저 수정 사항 ✂️ remove 패키지 혹은 폴더, 클래스 삭제 ✅ test 테스트 코드 🌱 style 코드 의미에 영향을 주지 않는 변경사항 (코드 포맷팅, 오타 수정, 변수명 변경, 에셋 추가) 💫 release 릴리즈 📄 docs 문서 추가 및 수정 🔄 refactor 코드 리팩토링 🔧 ci CI/CD 파이프라인 변경 🚀 feat 새로운 기능 추가 / 일부 코드 추가 / 일부 코드 수정 (리팩토링과 구분) / 디자인 요소 수정 🚨 fix 버그 수정 / 에러 해결 labels Apr 5, 2026
@bbbang105 bbbang105 closed this Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚒️ chore 빌드 부분 혹은 패키지 매니저 수정 사항 🔧 ci CI/CD 파이프라인 변경 📄 docs 문서 추가 및 수정 🚀 feat 새로운 기능 추가 / 일부 코드 추가 / 일부 코드 수정 (리팩토링과 구분) / 디자인 요소 수정 🚨 fix 버그 수정 / 에러 해결 🔄 refactor 코드 리팩토링 💫 release 릴리즈 ✂️ remove 패키지 혹은 폴더, 클래스 삭제 🌱 style 코드 의미에 영향을 주지 않는 변경사항 (코드 포맷팅, 오타 수정, 변수명 변경, 에셋 추가) ✅ test 테스트 코드

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants