Skip to content

feat: 스터디원 목록, 프로필 개선 및 실명/닉네임 분리#4

Merged
bbbang105 merged 1 commit intodevfrom
feature/member-list-and-profile-enhancements
Feb 23, 2026
Merged

feat: 스터디원 목록, 프로필 개선 및 실명/닉네임 분리#4
bbbang105 merged 1 commit intodevfrom
feature/member-list-and-profile-enhancements

Conversation

@bbbang105
Copy link
Copy Markdown
Owner

@bbbang105 bbbang105 commented Feb 23, 2026

Summary

스터디원 목록 페이지 신규 구현, 프로필 UX 개선, 소셜 링크 추가, 실명/닉네임 필드 분리 등 멤버 관련 전반적인 기능 개선

주요 변경사항

1. 스터디원 목록 페이지 (/members)

  • 사이드바 '큐레이션' 아래에 '스터디원 목록' 메뉴 추가
  • 카드형 그리드 레이아웃 (반응형: 1→2→3 컬럼)
  • 파트별 필터링 버튼 (해당 파트에 멤버가 있는 경우만 표시)
  • 소셜 링크 칩 (블로그/GitHub/LinkedIn/Instagram)
  • 카드 클릭 시 멤버 상세 페이지로 이동

2. 프로필 UX 개선

  • 헤더 드롭다운: '프로필 수정' → '프로필' (조회 페이지로 변경)
  • 프로필 조회 페이지 하단에 '프로필 수정' 버튼 배치
  • Discord username #0 접미사 자동 제거 (저장 시 + 표시 시)

3. 소셜 링크 필드 추가

  • DB 스키마: github_url, linkedin_url, instagram_url 컬럼 추가
  • 온보딩 Step 1에서 선택적 입력 가능
  • 프로필 수정에서 편집 가능
  • 멤버 카드, 상세 페이지, 프로필 페이지에서 칩으로 표시

4. 실명/닉네임 분리

  • 기존 name 컬럼 → nickname으로 리네임
  • name 컬럼 추가 (실명, 필수값, 디폴트 없음)
  • 온보딩: 닉네임 위에 '이름 (실명)' 입력 필드 추가
  • 프로필 수정: '이름 & 닉네임' 카드로 둘 다 편집 가능
  • 표시: 닉네임이 주 표시명, 상세 페이지에서 실명도 함께 표시

5. DiceBear 기본 아바타 & 랜덤 아바타 뽑기

  • 프로필 이미지 없는 멤버에게 DiceBear fun-emoji 스타일 기본 아바타 적용
  • getDefaultAvatar() 유틸 함수 (seed 기반 결정적 생성)
  • 온보딩/프로필 수정에서 '랜덤 아바타 뽑기' 기능
    • 5개 DiceBear 스타일에서 8개 랜덤 생성
    • 클릭으로 선택, '다시 뽑기'로 재생성

변경 파일 (21개)

영역 파일 설명
Schema packages/shared/src/db/schema.ts nickname 컬럼 추가, name 길이 조정, 소셜 링크 컬럼
Bot packages/bot/src/services/member.service.ts nickname 필드 대응
API packages/web/src/app/api/auth/me/route.ts name/nickname 분리 반환
API packages/web/src/app/api/profile/onboarding/route.ts nickname 수신/저장, #0 제거
API packages/web/src/app/api/profile/edit/route.ts name/nickname/소셜 링크 수정 지원
API packages/web/src/app/api/profile/route.ts nickname/소셜 링크 반환
API packages/web/src/app/api/members/route.ts nickname/소셜 링크 반환
API packages/web/src/app/api/members/[id]/route.ts nickname/소셜 링크 반환
API packages/web/src/app/api/ranking/route.ts nickname 반환
API packages/web/src/app/api/admin/members/route.ts nickname 필드 대응
Page packages/web/src/app/(user)/members/page.tsx 신규 스터디원 목록 페이지
Page packages/web/src/app/(user)/members/[id]/page.tsx 소셜 링크, 실명 표시, 기본 아바타
Page packages/web/src/app/(user)/profile/onboarding/page.tsx 실명 입력, 소셜 링크 입력
Page packages/web/src/app/(user)/profile/edit/page.tsx 이름/닉네임 카드, 소셜 링크 편집
Page packages/web/src/app/(user)/profile/page.tsx 닉네임 표시, 실명, 소셜 링크
Page packages/web/src/app/(user)/ranking/page.tsx 닉네임 기반 표시, 기본 아바타
Component packages/web/src/components/avatar-upload.tsx 랜덤 아바타 뽑기 기능
Layout packages/web/src/components/layout/header.tsx '프로필' 메뉴 변경
Layout packages/web/src/components/layout/sidebar.tsx '스터디원 목록' 메뉴 추가
Util packages/web/src/lib/utils.ts getDefaultAvatar() 함수
Docs CLAUDE.md DiceBear 리소스 문서화

DB 마이그레이션 (Supabase에서 직접 실행 완료)

-- 소셜 링크 추가
ALTER TABLE members ADD COLUMN github_url varchar(500);
ALTER TABLE members ADD COLUMN linkedin_url varchar(500);
ALTER TABLE members ADD COLUMN instagram_url varchar(500);

-- name → nickname 리네임, 새 name(실명) 추가
ALTER TABLE members RENAME COLUMN name TO nickname;
ALTER TABLE members ADD COLUMN name varchar(50) NOT NULL DEFAULT '';
UPDATE members SET name = nickname;
ALTER TABLE members ALTER COLUMN name DROP DEFAULT;

Test plan

  • 온보딩 플로우: 실명 + 닉네임 입력 후 완료까지 정상 동작 확인
  • 프로필 조회: 실명/닉네임/소셜 링크 정상 표시
  • 프로필 수정: 이름, 닉네임, 소셜 링크 수정 후 저장
  • 스터디원 목록: 카드 표시, 파트 필터링, 소셜 링크 클릭
  • 멤버 상세: 실명/닉네임/소셜 링크 정상 표시
  • 랭킹: 닉네임 기반 표시, 기본 아바타 정상
  • 랜덤 아바타: 뽑기 → 선택 → 저장 정상 동작
  • 기본 아바타: 프로필 이미지 없는 멤버에게 DiceBear 아바타 표시

🤖 Generated with Claude Code

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

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

vercel bot commented Feb 23, 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 Feb 23, 2026 5:48pm

@bbbang105 bbbang105 added the 🚀 feat 새로운 기능 추가 / 일부 코드 추가 / 일부 코드 수정 (리팩토링과 구분) / 디자인 요소 수정 label Feb 23, 2026
@bbbang105 bbbang105 marked this pull request as ready for review February 23, 2026 17:49
@bbbang105 bbbang105 merged commit 536380a into dev Feb 23, 2026
9 checks passed
@bbbang105 bbbang105 deleted the feature/member-list-and-profile-enhancements branch February 23, 2026 17:50
choihooo added a commit that referenced this pull request Mar 10, 2026
P0 #3 문제 해결 - 출석 상태 업데이트 누락:
- RSS 콜백에서 새 글 생성 후 출석 상태 업데이트
- 회차 마감일 이후 제출 시 지각(LATE) 처리
- 지각 시 벌금 3,000원 자동 부과 및 DM 발송
- 정상 제출 시 SUBMITTED 상태로 변경

P0 #4 문제 해결 - 결석/지각 벌금 콜백 미설정:
- attendanceChecker에 결석 콜백 설정
- 화요일 00:00 결석 판정 시 자동으로 벌금 5,000원 부과
- 결석 벌금 DM 알림 자동 발송

변경 사항:
- scheduler-registry.ts: RSS 콜백에 출석 상태 업데이트 로직 추가
- 지각 판단: item.pubDate > round.endDate (23:59:59.999)
- 결석 콜백: setOnAbsentCallback()으로 벌금 생성 및 알림 발송

동작 흐름:
1. RSS 폴러가 새 글 발견
2. 글 저장 + 출석 상태 업데이트 (SUBMITTED 또는 LATE)
3. 지각인 경우 벌금 생성 + DM 발송
4. 화요일 00:00 출석 체크
5. 결석자 벌금 생성 + DM 발송

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
choihooo added a commit that referenced this pull request Mar 10, 2026
* feat: RSS 수집 시 출석 상태 업데이트 및 결석/지각 벌금 자동 부과

P0 #3 문제 해결 - 출석 상태 업데이트 누락:
- RSS 콜백에서 새 글 생성 후 출석 상태 업데이트
- 회차 마감일 이후 제출 시 지각(LATE) 처리
- 지각 시 벌금 3,000원 자동 부과 및 DM 발송
- 정상 제출 시 SUBMITTED 상태로 변경

P0 #4 문제 해결 - 결석/지각 벌금 콜백 미설정:
- attendanceChecker에 결석 콜백 설정
- 화요일 00:00 결석 판정 시 자동으로 벌금 5,000원 부과
- 결석 벌금 DM 알림 자동 발송

변경 사항:
- scheduler-registry.ts: RSS 콜백에 출석 상태 업데이트 로직 추가
- 지각 판단: item.pubDate > round.endDate (23:59:59.999)
- 결석 콜백: setOnAbsentCallback()으로 벌금 생성 및 알림 발송

동작 흐름:
1. RSS 폴러가 새 글 발견
2. 글 저장 + 출석 상태 업데이트 (SUBMITTED 또는 LATE)
3. 지각인 경우 벌금 생성 + DM 발송
4. 화요일 00:00 출석 체크
5. 결석자 벌금 생성 + DM 발송

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

* fix: 코드 리뷰 사항 반영 (타임존, 주석, import 순서)

Priority 1 & 2 수정 사항:
1. 타임존 명시: roundEndDate 생성 시 KST (+09:00) 명시
2. 중복 방지 로직 주석 추가: markLate/markSubmitted/fineService.create 내부 로직 설명
3. Import 순서 정리: @blog-study/shared/db를 상단으로 이동

기존 로직 확인:
- FineService.create(): 이미 내부에서 getByMemberAndRound() 체크 후 중복 시 기존 벌금 반환
- AttendanceService.markLate/markSubmitted(): 이미 PENDING 상태일 때만 업데이트

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
choihooo added a commit that referenced this pull request Mar 11, 2026
- BottomNav 컴포넌트에 aria-current="page" 추가
- 현재 페이지 여부를 스크린 리더에 명확히 전달
- 모바일 하단 내비게이션 접근성 개선

Refs: docs/26-03-11-web-accessibility-audit.md P1 #4
choihooo added a commit that referenced this pull request Mar 11, 2026
* feat: 큐레이션 데이터 품질 개선 (description, thumbnailUrl 추출)

P1 #8: 큐레이션 봇 크롤러 데이터 품질 개선

주요 변경사항:
- RSS 피드에서 description, thumbnailUrl 필드 추출
- feed-parser 공유 유틸리티 생성 (extractFeedItems, sanitizeDescription, extractOgImage)
- SSRF 보호 강화 (isSafeUrl를 url-validator로 이동)
- HTML 엔티티 디코딩 추가 (html-entities 패키지)
- 제어 문자/유니코드 익스플로잇 제거 로직 개선
- 웹 API와 봇 간 코드 중복 제거

보안 개선:
- extractOgImage 함수에 isSafeUrl SSRF 체크 추가
- sanitizeDescription에 제어 문자/유니코드 익스플로잇 제거 추가
- 내부 URL (localhost, 127.0.0.1, 169.254.169.254 등) 차단

테스트:
- feed-parser.property.test.ts 추가 (20개 테스트 케이스)
- SSRF 보호, XSS 방지, 한글/이모지 처리 등 검증

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

* perf: OG 이미지 추출 병렬 처리로 성능 개선

- 봇 스케줄러: Promise.allSettled로 OG 이미지 동시 추출
- 웹 API: OG 이미지 병렬 추출 후 DB 삽입 (순차 유지)
- 순차 처리(최대 250초) → 병렬 처리(최대 5초)로 대기 시간 단축
- Promise.allSettled로 개별 실패시 전체 프로세스 보호

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

* fix: 타입 안전성 개선 (NormalizedFeedItem 타입 명시)

- 웹 API: NormalizedFeedItem 타입 import 추가
- filter/map 콜백에 명시적 타입 어노테이션 추가
- Promise.allSettled 결과에 타입 가드 추가

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

* fix: PR 빌드 에러 수정 (utils export, isSafeUrl re-export)

- shared/utils: utils namespace export 추가 (import { utils } 패턴 지원)
- rss-detect.ts: isSafeUrl re-export 추가
- 타입 안전성 개선: Promise.allSettled result 변수 분리

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

* test: P2 #15 핸들러 및 스케줄러 테스트 추가

- Handlers 통합 테스트 5개 추가 (모듈 로드, 함수 export 확인)
- Round Service 통합 테스트 15개 추가
  * isDeadlinePassed, isGracePeriod, isGracePeriodEnded 함수 테스트
  * 날짜 파싱 정확성 테스트
  * 일관성 테스트
- 총 145개 테스트 통과

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

* fix: handlers.test.ts mock export 추가로 테스트 수정

- sendFineNotification, sendFineReminder export를 mock에 추가
- 모든 테스트 통과 (153개)

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

* fix: 린트 에러 수정 (unused imports 제거)

- getConfigValue import 제거 (round-integration.test.ts)
- Client import 제거 (handlers.test.ts)
- 에러 0개, warnings 56개 (console.log 관련)

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

* test: PR #29 코드 리뷰 피드백 반영

## 개선사항

### 1. 핸들러 테스트 실제 동작 검증으로 강화
- Mock만 사용하던 테스트를 실제 이벤트 리스너 등록 검증으로 개선
- setupActivityHandler: MessageCreate, MessageReactionAdd 이벤트 등록 확인
- setupDMHandler: InteractionCreate 이벤트 등록 확인
- Property-Based Testing 패턴 유지

### 2. feed-parser 보안 강화 (SSRF)
- extractOgImage()에서 OG 이미지 URL 자체도 안전한지 검증
- 이중 SSRF 보호: 페이지 URL + OG 이미지 URL 모두 검증
- 공격자가 og:image에 내부 URL을 넣는 방어

### 3. 에러 핸들링 개선
- extractOgImage() 에러를 타입별로 분류하여 로그
- timeout, network error, unexpected error 각각 다른 레벨로 처리
- 디버깅 및 운영 모니터링 개선

### 4. 테스트 커버리지 확대
- extractFeedItems() 포맷별 동작 테스트 추가 (RSS, Atom, JSON, RDF)
- Property 17-20: 각 포맷의 파싱 로직 검증
- 총 24개 테스트 통과

### 5. 타입 안정성 개선
- API 라우트에서 불필요한 타입 단언 제거
- 타입 가드를 사용하여 안전하게 필터링

### 6. next-env.d.ts 복구
- 개발 전용 변경을 원래대로 복구
- 프로덕션 빌드 호환성 유지

## 테스트 결과
- 154개 테스트 전체 통과
- 핸들러 테스트: 9개 (이벤트 리스너 등록 검증)
- feed-parser 테스트: 24개 (보안 + 포맷별 동작)

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

* feat(a11y): 테이블 헤더에 scope 속성 추가

- TableHead 컴포넌트에 기본값 scope="col" 추가
- 모든 테이블 헤더의 의미 체계를 스크린 리더에 명확히 전달
- WCAG 2.1 AA 준수

Refs: docs/26-03-11-web-accessibility-audit.md P1 #9

* feat(a11y): 활성 내비게이션에 aria-current 속성 추가

- BottomNav 컴포넌트에 aria-current="page" 추가
- 현재 페이지 여부를 스크린 리더에 명확히 전달
- 모바일 하단 내비게이션 접근성 개선

Refs: docs/26-03-11-web-accessibility-audit.md P1 #4

* feat(a11y): 외부 링크에 스크린 리더용 보조 텍스트 추가

- ExternalLinkIcon 재사용 컴포넌트 생성
- 새 탭에서 열림을 나타내는 sr-only 텍스트 추가
- 랜딩 페이지, 게시판, 큐레이션 외부 링크에 적용
- aria-hidden으로 아이콘 숨김 처리

Refs: docs/26-03-11-web-accessibility-audit.md P1 #10

* feat(a11y): 검색 입력 필드에 접근 가능한 라벨 추가

- sr-only 클래스로 화면에는 보이지 않는 라벨 추가
- htmlFor와 id로 명시적 연결
- 검색 아이콘에 aria-hidden 적용
- 큐레이션, 멤버, 벌금 페이지 검색창 개선

Refs: docs/26-03-11-web-accessibility-audit.md P1 #6

* feat(a11y): 폼 에러 메시지를 입력 필드와 연결

- 에러 메시지에 role="alert", aria-live="assertive" 추가
- 입력 필드에 aria-invalid, aria-describedby 속성 추가
- 필드별 에러를 동적으로 연결하여 스크린 리더에 정확히 전달
- 게시판 작성, 멤버 폼에 적용

Refs: docs/26-03-11-web-accessibility-audit.md P1 #5

* fix(a11y): 사용하지 않는 ExternalLinkIcon import 제거

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🚀 feat 새로운 기능 추가 / 일부 코드 추가 / 일부 코드 수정 (리팩토링과 구분) / 디자인 요소 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant