Skip to content

[feature] 총동연 소개 페이지 UI 개편#1540

Open
seongwon030 wants to merge 4 commits into
develop-fefrom
feature/#1539-club-union-v2-MOA-845
Open

[feature] 총동연 소개 페이지 UI 개편#1540
seongwon030 wants to merge 4 commits into
develop-fefrom
feature/#1539-club-union-v2-MOA-845

Conversation

@seongwon030
Copy link
Copy Markdown
Member

@seongwon030 seongwon030 commented May 13, 2026

#️⃣연관된 이슈

#1539

📝작업 내용

  • 4열 staggered 카드 그리드 레이아웃으로 전환
  • 분과별 배경색 및 카드 그라디언트 오버레이 적용
  • 분과 아이콘 배경 제거 (별도 SVG 생성)
  • 이름/직책 배지 가로 정렬, 설명 수동 줄바꿈 처리
  • 모바일(≤500px) 전용 멤버 노출 순서 추가: 회장/부회장 → 임원진 → 봉사 → 종교 → 취미 → 학술 → 운동 → 공연

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • 스타일 개선

    • 총동아리연합회 페이지 레이아웃 재구성: 4열 스태거드 그리드(태블릿 2열, 모바일 1열), 반응형 타이포그래피/여백 최적화
    • 프로필 카드 재설계: 멤버별 배경색·그라데이션 오버레이 적용, 내용과 일러스트 분리된 카드 레이아웃, 카드 높이·간격 개선
    • 카테고리·부서 아이콘 업데이트(배경 없는 SVG 사용) 및 아바타 스타일 조정
    • SNS 링크 레이블/스타일 개선(Instagram, 💬 kakaotalk)
  • 문서화

    • 총동아리연합회 페이지 구성 및 스타일 가이드 문서 추가

Review Change Stack

- 4열 staggered 카드 그리드 레이아웃으로 전환
- 분과별 배경색 및 카드 그라디언트 오버레이 적용
- 분과 아이콘 배경 제거 (별도 SVG 생성)
- 이름/직책 배지 가로 정렬, 설명 수동 줄바꿈 처리
@seongwon030 seongwon030 self-assigned this May 13, 2026
@seongwon030 seongwon030 added ✨ Feature 기능 개발 💻 FE Frontend labels May 13, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

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

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview, 💬 9 unresolved
✅ 3 resolved
May 19, 2026 3:17pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6acf379c-7340-41e6-9a05-812169fb9fbe

📥 Commits

Reviewing files that changed from the base of the PR and between a73aa6c and 59308b4.

📒 Files selected for processing (1)
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts

Walkthrough

ClubUnionPage 문서 추가, 멤버 데이터에 bgColor 도입(일부 아이콘을 배경 없는 SVG로 교체), 4열 staggered 컬럼 분배 로직과 새 ProfileCard 컴포넌트·토큰 기반 스타일이 추가되어 컬럼 단위로 멤버를 렌더링합니다.

Changes

Club Union Member Page Redesign

Layer / File(s) Summary
Member data schema and color assignment
frontend/src/constants/clubUnionInfo.ts
ClubUnionMemberbgColor: string 추가; 카테고리별 *_no_bg.svg 자산 임포트 및 MEMBER_AVATARS 매핑 갱신; CLUB_UNION_MEMBERS 항목들을 멀티라인 description과 테마 팔레트(colors.accent/colors.secondary[...].tag)에서 소싱한 bgColor로 업데이트.
Page typography and SNS section styling
frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts
Title, IntroductionTextsetTypography/typography·테마 색상으로 전환; SnsLinkContainer 모바일 gap 조정; SnsLink의 배경/호버/아이콘 크기 및 타이포그래피 재구성.
Grid layout and column distribution
frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts, frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
ProfileGrid/ProfileColumn을 4열 staggered 레이아웃으로 재설계($staggered로 상단 패딩 조건부 적용, 태블릿 2열·모바일 1열 반응형 전환); COLUMN_SIZESCLUB_UNION_MEMBERS를 열 배열로 분배하도록 로직 추가.
Profile card component structure
frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts
ProfileCard$bgColor 배경 및 ::before 그라데이션 오버레이를 사용하도록 재설계; CardContent, CardTitleRow, CardName, CardRoleBadge, CardDescription, CardIllustrationWrap, CardIllustration 도입으로 카드 내부 레이아웃과 반응형 스타일 정의.
Component template updates and label changes
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
SNS 라벨 변경(Instagram → instagram, KakaoTalk → 💬 kakaotalk); 프로필 렌더링을 컬럼 기반으로 전환하고 각 멤버를 새 ProfileCard 구조로 렌더.
Design documentation
frontend/docs/features/club-union/README.md
ClubUnionPage 레이아웃(4열 staggered, 반응형), ProfileCard 구성(::before 그라데이션, 일러스트 절대위치 등), 카테고리별 색상 매핑 및 관련 파일/자산 경로를 설명하는 README 추가.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Moadong/moadong#663: Both PRs implement the /club-union feature by modifying the same ClubUnionPage code—especially frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts (profile-card/layout styled-components) and the underlying member data used to render those cards.
  • Moadong/moadong#1356: Both PRs modify the same ClubUnion implementation—frontend/src/constants/clubUnionInfo.ts and frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts—around member “representative” avatar/icon selection and $bgColor-driven profile card styling (with main PR evolving ProfileCardContainer into ProfileCard).
  • Moadong/moadong#1138: Main PR and #1138 both directly edit frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts to change the profile layout/card styled-components (e.g., ProfileGrid and profile card container/components), while #1138 separately focuses on SNS/tracking and member-content reshaping.

Suggested labels

🎨 Design

Suggested reviewers

  • lepitaaar
  • suhyun113
  • oesnuj
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 총동연 소개 페이지의 UI 개편이라는 주요 변화를 명확하게 요약하며, 변경 사항 전체(레이아웃 재구성, 카드 스타일, 아이콘, 멤버 정보)를 포괄적으로 나타냅니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#1539-club-union-v2-MOA-845

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

✅ UI 변경사항 없음

구분 링크
📖 Storybook https://67904e61c16daa99a63b44a7-urbyngisms.chromatic.com/

전체 57개 스토리 · 22개 컴포넌트

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (8)
frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts (4)

161-173: 💤 Low value

반응형 타이포그래피 일관성

CardName에서 태블릿 브레이크포인트에 대해서만 font-size를 조정하고 있지만, CardRoleBadgeCardDescription은 태블릿과 모바일 모두에서 조정됩니다. 일관된 반응형 패턴을 적용하는 것이 좋습니다.

♻️ 일관성 개선 제안
 export const CardName = styled.p`
   font-size: 1.25rem;
   font-weight: 600;
   color: ${colors.base.black};
   letter-spacing: -0.4px;
   line-height: 1.4;
   margin: 0;
   white-space: nowrap;

   ${media.tablet} {
     font-size: 1rem;
   }
+
+  ${media.mobile} {
+    font-size: 0.875rem;
+  }
 `;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts` around lines 161 -
173, CardName only adjusts font-size at ${media.tablet} while CardRoleBadge and
CardDescription adjust at both tablet and mobile; update CardName to follow the
same responsive pattern by adding a ${media.mobile} block (matching the mobile
font-size used by CardRoleBadge/CardDescription) so typography is consistent
across components, keeping the existing desktop size and the current
${media.tablet} override intact.

19-25: ⚡ Quick win

타이포그래피 유틸리티 사용 후 속성 재정의

setTypography를 사용한 직후 line-heightletter-spacing을 재정의하고 있습니다. 이는 타이포그래피 시스템의 일관성을 저해할 수 있습니다. 커스텀 타이포그래피 변형이 필요하다면 테마에 추가하는 것을 고려하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts` around lines 19 -
25, IntroductionText currently calls setTypography(typography.paragraph.p1) and
then immediately overrides line-height and letter-spacing, which breaks the
typography utility's consistency; either remove those inline overrides from the
IntroductionText styled component so it relies entirely on
setTypography(typography.paragraph.p1), or add a new typography variant in your
theme (e.g., typography.paragraph.p1Custom or similar) with the desired
line-height and letter-spacing and use
setTypography(typography.paragraph.p1Custom) in IntroductionText instead of
redefining styles locally.

57-57: ⚡ Quick win

하드코딩된 hover 색상을 테마 색상으로 대체

#e8e8e8가 하드코딩되어 있습니다. 테마의 색상 토큰(예: colors.gray[200])을 사용하면 디자인 시스템 일관성이 향상됩니다.

♻️ 제안된 수정
   &:hover {
-    background-color: `#e8e8e8`;
+    background-color: ${colors.gray[200]};
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts` at line 57, 파일의
하드코딩된 배경색 "background-color: `#e8e8e8`"을 테마 색상 토큰으로 교체하세요; 즉
ClubUnionPage.styles.ts에서 해당 CSS 규칙을 찾아 직접 숫자값 대신 프로젝트의 테마 색상(예:
theme.colors.gray[200] 또는 colors.gray[200] 토큰)을 사용하도록 수정하고, 필요하면
styled-components/Emotion의 theme 프로퍼티를 import/참조하여 테마 타입을 유지하도록 적용하세요.

95-95: 💤 Low value

매직 넘버를 상수로 추출

125px 엇갈림 패딩 값이 하드코딩되어 있습니다. 이 값을 파일 상단에 명명된 상수로 추출하면 유지보수성이 향상됩니다.

♻️ 제안된 개선
+const STAGGER_OFFSET = '125px';
+
 export const ProfileColumn = styled.div<{ $staggered: boolean }>`
   display: flex;
   flex: 1 0 0;
   flex-direction: column;
   gap: 22px;
-  padding-top: ${({ $staggered }) => ($staggered ? '125px' : '0')};
+  padding-top: ${({ $staggered }) => ($staggered ? STAGGER_OFFSET : '0')};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts` at line 95, Extract
the hard-coded "125px" magic number into a named constant (e.g.,
STAGGERED_PADDING = '125px') declared at the top of the module and replace the
literal in the styled rule that uses the $staggered prop (the expression
padding-top: ${({ $staggered }) => ($staggered ? '125px' : '0')};) so it becomes
padding-top: ${({ $staggered }) => ($staggered ? STAGGERED_PADDING : '0')}; to
improve readability and maintainability.
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx (2)

17-21: ⚡ Quick win

컬럼 분배 로직을 메모이제이션

columns 배열이 매 렌더링마다 재계산됩니다. CLUB_UNION_MEMBERS가 정적 데이터이므로 useMemo를 사용하여 불필요한 재계산을 방지할 수 있습니다.

⚡ 성능 개선 제안
+import { useMemo } from 'react';
+
 const ClubUnionPage = () => {
   useTrackPageView(PAGE_VIEW.CLUB_UNION_PAGE);
   const trackEvent = useMixpanelTrack();

-  const columns = Array.from({ length: COLUMN_COUNT }, (_, colIdx) =>
+  const columns = useMemo(() =>
+    Array.from({ length: COLUMN_COUNT }, (_, colIdx) =>
-    CLUB_UNION_MEMBERS.filter(
-      (_, memberIdx) => memberIdx % COLUMN_COUNT === colIdx,
-    ),
-  );
+      CLUB_UNION_MEMBERS.filter(
+        (_, memberIdx) => memberIdx % COLUMN_COUNT === colIdx,
+      ),
+    ),
+  []);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` around lines 17 - 21,
columns is recomputed on every render even though CLUB_UNION_MEMBERS is static;
wrap the columns calculation in React's useMemo so it only recalculates when
CLUB_UNION_MEMBERS or COLUMN_COUNT change. Replace the direct Array.from(...)
assignment for the columns variable with a useMemo(() => Array.from({ length:
COLUMN_COUNT }, (_, colIdx) => CLUB_UNION_MEMBERS.filter((_, memberIdx) =>
memberIdx % COLUMN_COUNT === colIdx)), [CLUB_UNION_MEMBERS, COLUMN_COUNT]) to
memoize the result and avoid unnecessary work.

45-45: ⚖️ Poor tradeoff

SNS 라벨 다국어화 고려

SNS 라벨이 소문자 영문("instagram", "kakaotalk")과 이모지로 하드코딩되어 있습니다. 다국어 지원이 필요한 경우 i18n 리소스로 관리하는 것이 좋습니다.

Also applies to: 57-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` at line 45, The SNS
labels "instagram" and "kakaotalk" (and their emojis) in the ClubUnionPage
component are hardcoded; replace them with i18n resource keys and use the
translation hook/function (e.g., t('social.instagram'), t('social.kakaotalk'))
wherever those literals appear in ClubUnionPage.tsx (lines around the
instagram/kakaotalk JSX). Move emoji or localized text into the i18n resources
so different locales can provide appropriate labels, and ensure any UI element
using those labels (buttons, aria-labels, tooltips) references the translated
string rather than the hardcoded literal.
frontend/src/constants/clubUnionInfo.ts (2)

39-41: 💤 Low value

개발자 가이드 주석 업데이트 필요

주석에 "30자 이내" 권장사항이 명시되어 있지만, 실제 데이터에는 줄바꿈 문자(\n)가 포함되어 있어 문자 수 계산이 명확하지 않습니다. 줄당 문자 수 제한으로 변경하는 것을 고려하세요.

💡 개선된 가이드 제안
-// 개발자 가이드: description 필드는 UI가 깨지지 않도록 글자 수를 제한합니다.
-// (권장) 데스크톱: 30자 이내
+// 개발자 가이드: description 필드는 UI가 깨지지 않도록 줄당 글자 수를 제한합니다.
+// (권장) 데스크톱: 각 줄당 15자 이내, 최대 3줄
 export const CLUB_UNION_MEMBERS: ClubUnionMember[] = [
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/constants/clubUnionInfo.ts` around lines 39 - 41, The developer
guide comment near CLUB_UNION_MEMBERS currently recommends "30자 이내" but doesn't
account for embedded line breaks; update the comment to specify a per-line
character limit (e.g., "각 줄당 최대 30자") and note that descriptions may contain
'\n' so length should be validated per line rather than total characters;
reference the description field of ClubUnionMember in the comment so consumers
know to enforce per-line limits when rendering or validating.

17-17: ⚡ Quick win

타입 안전성 개선 고려

bgColorstring으로 타입되어 있어 런타임에 잘못된 색상 값이 전달될 수 있습니다. colors.accent 또는 colors.secondary의 특정 타입을 사용하면 더 안전합니다.

♻️ 제안된 타입 개선
+type AccentColor = typeof colors.accent[1][700];
+type SecondaryTagColor = typeof colors.secondary[1]['tag'] | typeof colors.secondary[2]['tag'] | typeof colors.secondary[3]['tag'] | typeof colors.secondary[4]['tag'] | typeof colors.secondary[5]['tag'] | typeof colors.secondary[6]['tag'];
+type MemberBgColor = AccentColor | SecondaryTagColor;
+
 export interface ClubUnionMember {
   id: number;
   name: string;
   role: string;
   description: string;
   imageSrc: string;
   type: keyof typeof MEMBER_AVATARS;
-  bgColor: string;
+  bgColor: MemberBgColor;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/constants/clubUnionInfo.ts` at line 17, 현재 bgColor 속성이 string으로
선언되어 있어 잘못된 색상 값이 들어올 수 있습니다; bgColor의 타입을 string에서 colors.accent 또는
colors.secondary에서 허용하는 구체적 타입(예: colors.accent | colors.secondary 또는 colors 객체의
키를 나타내는 타입)으로 좁히고, bgColor를 사용하는 컴포넌트/함수(예: bgColor 프로퍼티를 읽는 곳)에서 전달 값이 이 새로운
타입을 따르도록 수정하세요. 또한 타입 변경에 따라 발생하는 타입 에러들을 찾아서 colors의 유효한 값만 전달하거나 적절한 매핑/검증 로직을
추가해 주세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@frontend/src/constants/clubUnionInfo.ts`:
- Around line 39-41: The developer guide comment near CLUB_UNION_MEMBERS
currently recommends "30자 이내" but doesn't account for embedded line breaks;
update the comment to specify a per-line character limit (e.g., "각 줄당 최대 30자")
and note that descriptions may contain '\n' so length should be validated per
line rather than total characters; reference the description field of
ClubUnionMember in the comment so consumers know to enforce per-line limits when
rendering or validating.
- Line 17: 현재 bgColor 속성이 string으로 선언되어 있어 잘못된 색상 값이 들어올 수 있습니다; bgColor의 타입을
string에서 colors.accent 또는 colors.secondary에서 허용하는 구체적 타입(예: colors.accent |
colors.secondary 또는 colors 객체의 키를 나타내는 타입)으로 좁히고, bgColor를 사용하는 컴포넌트/함수(예:
bgColor 프로퍼티를 읽는 곳)에서 전달 값이 이 새로운 타입을 따르도록 수정하세요. 또한 타입 변경에 따라 발생하는 타입 에러들을 찾아서
colors의 유효한 값만 전달하거나 적절한 매핑/검증 로직을 추가해 주세요.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts`:
- Around line 161-173: CardName only adjusts font-size at ${media.tablet} while
CardRoleBadge and CardDescription adjust at both tablet and mobile; update
CardName to follow the same responsive pattern by adding a ${media.mobile} block
(matching the mobile font-size used by CardRoleBadge/CardDescription) so
typography is consistent across components, keeping the existing desktop size
and the current ${media.tablet} override intact.
- Around line 19-25: IntroductionText currently calls
setTypography(typography.paragraph.p1) and then immediately overrides
line-height and letter-spacing, which breaks the typography utility's
consistency; either remove those inline overrides from the IntroductionText
styled component so it relies entirely on
setTypography(typography.paragraph.p1), or add a new typography variant in your
theme (e.g., typography.paragraph.p1Custom or similar) with the desired
line-height and letter-spacing and use
setTypography(typography.paragraph.p1Custom) in IntroductionText instead of
redefining styles locally.
- Line 57: 파일의 하드코딩된 배경색 "background-color: `#e8e8e8`"을 테마 색상 토큰으로 교체하세요; 즉
ClubUnionPage.styles.ts에서 해당 CSS 규칙을 찾아 직접 숫자값 대신 프로젝트의 테마 색상(예:
theme.colors.gray[200] 또는 colors.gray[200] 토큰)을 사용하도록 수정하고, 필요하면
styled-components/Emotion의 theme 프로퍼티를 import/참조하여 테마 타입을 유지하도록 적용하세요.
- Line 95: Extract the hard-coded "125px" magic number into a named constant
(e.g., STAGGERED_PADDING = '125px') declared at the top of the module and
replace the literal in the styled rule that uses the $staggered prop (the
expression padding-top: ${({ $staggered }) => ($staggered ? '125px' : '0')};) so
it becomes padding-top: ${({ $staggered }) => ($staggered ? STAGGERED_PADDING :
'0')}; to improve readability and maintainability.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx`:
- Around line 17-21: columns is recomputed on every render even though
CLUB_UNION_MEMBERS is static; wrap the columns calculation in React's useMemo so
it only recalculates when CLUB_UNION_MEMBERS or COLUMN_COUNT change. Replace the
direct Array.from(...) assignment for the columns variable with a useMemo(() =>
Array.from({ length: COLUMN_COUNT }, (_, colIdx) =>
CLUB_UNION_MEMBERS.filter((_, memberIdx) => memberIdx % COLUMN_COUNT ===
colIdx)), [CLUB_UNION_MEMBERS, COLUMN_COUNT]) to memoize the result and avoid
unnecessary work.
- Line 45: The SNS labels "instagram" and "kakaotalk" (and their emojis) in the
ClubUnionPage component are hardcoded; replace them with i18n resource keys and
use the translation hook/function (e.g., t('social.instagram'),
t('social.kakaotalk')) wherever those literals appear in ClubUnionPage.tsx
(lines around the instagram/kakaotalk JSX). Move emoji or localized text into
the i18n resources so different locales can provide appropriate labels, and
ensure any UI element using those labels (buttons, aria-labels, tooltips)
references the translated string rather than the hardcoded literal.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 514fed1a-5da2-4a58-a8b5-7d35c7ff1df1

📥 Commits

Reviewing files that changed from the base of the PR and between ff0802b and e129ce9.

⛔ Files ignored due to path filters (6)
  • frontend/src/assets/images/icons/category_button/club_union/category_hobby_no_bg.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/category_button/club_union/category_performance_no_bg.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/category_button/club_union/category_religion_no_bg.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/category_button/club_union/category_sport_no_bg.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/category_button/club_union/category_study_no_bg.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/category_button/club_union/category_volunteer_no_bg.svg is excluded by !**/*.svg
📒 Files selected for processing (4)
  • frontend/docs/features/club-union/README.md
  • frontend/src/constants/clubUnionInfo.ts
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx

Copy link
Copy Markdown
Collaborator

@suhyun113 suhyun113 left a comment

Choose a reason for hiding this comment

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

카드 너비가 너무 좁은 것 같아서 늘리면 좋을 것 같고, 대표 카드 위치도 3열에 2개가 오도록 위치를 이동시키는게 좋을 것 같아여
아이콘도 더 키우고 약간 잘리게 하면 좀 더 디자인과 동일할 것 같아요~

@seongwon030
Copy link
Copy Markdown
Member Author

카드 너비가 너무 좁은 것 같아서 늘리면 좋을 것 같고, 대표 카드 위치도 3열에 2개가 오도록 위치를 이동시키는게 좋을 것 같아여 아이콘도 더 키우고 약간 잘리게 하면 좀 더 디자인과 동일할 것 같아요~

너비는 max-width가 적용되어 있어서 그런 것 같네요 피그마 시안 보고 다시 맞춰보겠습니다~
나머지 부분도 수정해보겠습니다

- COLUMN_COUNT + modulo → COLUMN_SIZES([3,3,4,3]) + slice 방식으로 변경
- ProfileGrid 좌우 padding 제거, PageContainer에 위임
- 멤버 배열 순서 피그마 시안에 맞게 조정
- CLUB_UNION_MEMBERS_MOBILE 상수 추가 (회장/부회장 → 임원진 → 봉사 → 종교 → 취미 → 학술 → 운동 → 공연 순)
- 모바일(≤500px)에서 matchMedia 기반 감지 후 단일 컬럼으로 flat 렌더링
- ProfileCard 컴포넌트 분리로 JSX 중복 제거
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx (2)

75-76: ⚡ Quick win

SNS 링크는 렌더링 전에 유효성 검증을 적용해주세요.

Line 75-76, 88-89에서 상수를 곧바로 href로 사용하고 있습니다. 공통 유틸(validateSocialLink)을 거쳐 안전한 URL만 노출하도록 맞춰주세요.

As per coding guidelines, "Validate SNS links using validateSocialLink() utility from src/utils/".

Also applies to: 88-89

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` around lines 75 - 76, The
CLUB_UNION_SNS constants are being passed directly into anchor hrefs in the
ClubUnionPage component; wrap each use with the shared validator
validateSocialLink (import from src/utils/) and only set href (or render the
<a>) when validateSocialLink(CLUB_UNION_SNS.whatever) returns a truthy/valid
URL; update both places where CLUB_UNION_SNS.instagram (and the other SNS
constant) are used so they call validateSocialLink before assignment, and ensure
the import of validateSocialLink is added to ClubUnionPage.

18-18: ⚡ Quick win

반응형 브레이크포인트를 공통 토큰으로 통일해주세요.

Line 18/42/46에서 '(max-width: 500px)'를 하드코딩하면 다른 페이지와 기준이 어긋나기 쉽습니다. src/styles/mediaQuery.ts의 공통 브레이크포인트를 참조하도록 바꿔주세요.

As per coding guidelines, "Apply responsive breakpoints from src/styles/mediaQuery.ts (mini_mobile: 375px, mobile: 500px, tablet: 700px, laptop: 1280px)".

Also applies to: 42-43, 46-46

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` at line 18, Replace the
hardcoded '(max-width: 500px)' occurrences in ClubUnionPage.tsx with the shared
breakpoint token from src/styles/mediaQuery.ts: import the exported mobile (or
the media-query map) and use that symbol instead of defining MOBILE_BREAKPOINT
locally; update the top-level MOBILE_BREAKPOINT constant (and any inline strings
in styled components or CSS-in-JS at the same file) to reference the imported
mobile token (e.g., mediaQuery.mobile or mobile) so all breakpoints use the
central tokens (mini_mobile, mobile, tablet, laptop) from
src/styles/mediaQuery.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx`:
- Around line 52-61: The current columns computation slices CLUB_UNION_MEMBERS
using a fixed COLUMN_SIZES total, which drops any members beyond that sum;
update the logic in the columns initializer (the reduce over COLUMN_SIZES that
produces cols and offset) to account for CLUB_UNION_MEMBERS.length by either
appending any remaining members to the last column or recalculating column sizes
proportionally from CLUB_UNION_MEMBERS.length so no items are lost; adjust the
reducer to check remainingCount = CLUB_UNION_MEMBERS.length - offset and, when
on the last size or remainingCount < size, slice offset..offset+remainingCount
(or expand the last slice) instead of assuming the fixed size.
- Around line 41-43: The state initialization for isMobile uses
window.matchMedia(MOBILE_BREAKPOINT) which will crash in non-browser
environments; change ClubUnionPage.tsx to guard access to window (e.g.,
initialize isMobile via a safe check like typeof window !== 'undefined' ?
window.matchMedia(MOBILE_BREAKPOINT).matches : false) and then update isMobile
inside a useEffect that runs on mount (and subscribes to matchMedia changes) so
server-side rendering and tests don't access window during initialization;
reference the isMobile/setIsMobile state and MOBILE_BREAKPOINT constants when
implementing this change.

---

Nitpick comments:
In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx`:
- Around line 75-76: The CLUB_UNION_SNS constants are being passed directly into
anchor hrefs in the ClubUnionPage component; wrap each use with the shared
validator validateSocialLink (import from src/utils/) and only set href (or
render the <a>) when validateSocialLink(CLUB_UNION_SNS.whatever) returns a
truthy/valid URL; update both places where CLUB_UNION_SNS.instagram (and the
other SNS constant) are used so they call validateSocialLink before assignment,
and ensure the import of validateSocialLink is added to ClubUnionPage.
- Line 18: Replace the hardcoded '(max-width: 500px)' occurrences in
ClubUnionPage.tsx with the shared breakpoint token from
src/styles/mediaQuery.ts: import the exported mobile (or the media-query map)
and use that symbol instead of defining MOBILE_BREAKPOINT locally; update the
top-level MOBILE_BREAKPOINT constant (and any inline strings in styled
components or CSS-in-JS at the same file) to reference the imported mobile token
(e.g., mediaQuery.mobile or mobile) so all breakpoints use the central tokens
(mini_mobile, mobile, tablet, laptop) from src/styles/mediaQuery.ts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ee251608-1427-458f-8f81-913f0134aab2

📥 Commits

Reviewing files that changed from the base of the PR and between 035939f and a73aa6c.

📒 Files selected for processing (3)
  • frontend/docs/features/club-union/README.md
  • frontend/src/constants/clubUnionInfo.ts
  • frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
✅ Files skipped from review due to trivial changes (1)
  • frontend/docs/features/club-union/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/constants/clubUnionInfo.ts

Comment on lines +41 to +43
const [isMobile, setIsMobile] = useState(
() => window.matchMedia(MOBILE_BREAKPOINT).matches,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

브라우저 전역 객체 직접 접근으로 런타임 크래시가 날 수 있습니다.

Line 41-43의 초기화 시점 window.matchMedia(...)는 브라우저가 아닌 환경(SSR/일부 테스트)에서 window is not defined를 유발할 수 있습니다. 안전 가드로 초기값을 계산해주세요.

수정 예시
-  const [isMobile, setIsMobile] = useState(
-    () => window.matchMedia(MOBILE_BREAKPOINT).matches,
-  );
+  const [isMobile, setIsMobile] = useState(() => {
+    if (typeof window === 'undefined') return false;
+    return window.matchMedia(MOBILE_BREAKPOINT).matches;
+  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` around lines 41 - 43, The
state initialization for isMobile uses window.matchMedia(MOBILE_BREAKPOINT)
which will crash in non-browser environments; change ClubUnionPage.tsx to guard
access to window (e.g., initialize isMobile via a safe check like typeof window
!== 'undefined' ? window.matchMedia(MOBILE_BREAKPOINT).matches : false) and then
update isMobile inside a useEffect that runs on mount (and subscribes to
matchMedia changes) so server-side rendering and tests don't access window
during initialization; reference the isMobile/setIsMobile state and
MOBILE_BREAKPOINT constants when implementing this change.

Comment on lines +52 to +61
const columns = COLUMN_SIZES.reduce<{
cols: (typeof CLUB_UNION_MEMBERS)[];
offset: number;
}>(
({ cols, offset }, size) => ({
cols: [...cols, CLUB_UNION_MEMBERS.slice(offset, offset + size)],
offset: offset + size,
}),
{ cols: [], offset: 0 },
).cols;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

고정 컬럼 합계와 멤버 수가 달라지면 일부 멤버가 누락됩니다.

Line 52-61은 COLUMN_SIZES 총합(13명)까지만 슬라이스합니다. 멤버가 늘면 뒤쪽 데이터가 렌더링되지 않습니다. 잔여 멤버를 마지막 컬럼에 붙이거나, 데이터 길이 기반으로 동적 분배해주세요.

수정 예시 (잔여 멤버 보존)
-  const columns = COLUMN_SIZES.reduce<{
+  const { cols, offset } = COLUMN_SIZES.reduce<{
     cols: (typeof CLUB_UNION_MEMBERS)[];
     offset: number;
   }>(
     ({ cols, offset }, size) => ({
       cols: [...cols, CLUB_UNION_MEMBERS.slice(offset, offset + size)],
       offset: offset + size,
     }),
     { cols: [], offset: 0 },
-  ).cols;
+  );
+
+  const columns =
+    offset < CLUB_UNION_MEMBERS.length
+      ? [...cols, CLUB_UNION_MEMBERS.slice(offset)]
+      : cols;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx` around lines 52 - 61, The
current columns computation slices CLUB_UNION_MEMBERS using a fixed COLUMN_SIZES
total, which drops any members beyond that sum; update the logic in the columns
initializer (the reduce over COLUMN_SIZES that produces cols and offset) to
account for CLUB_UNION_MEMBERS.length by either appending any remaining members
to the last column or recalculating column sizes proportionally from
CLUB_UNION_MEMBERS.length so no items are lost; adjust the reducer to check
remainingCount = CLUB_UNION_MEMBERS.length - offset and, when on the last size
or remainingCount < size, slice offset..offset+remainingCount (or expand the
last slice) instead of assuming the fixed size.

- CardIllustration 크기 상향 (desktop 160→210px, tablet 120→150px, mobile 80→130px)
- CardIllustrationWrap width 60→65%, right 음수값으로 아이콘을 우측 끝으로 밀어냄
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants