[feature] 총동연 소개 페이지 UI 개편#1540
Conversation
- 4열 staggered 카드 그리드 레이아웃으로 전환 - 분과별 배경색 및 카드 그라디언트 오버레이 적용 - 분과 아이콘 배경 제거 (별도 SVG 생성) - 이름/직책 배지 가로 정렬, 설명 수동 줄바꿈 처리
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Layer / File(s) | Summary |
|---|---|
Member data schema and color assignment frontend/src/constants/clubUnionInfo.ts |
ClubUnionMember에 bgColor: 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, IntroductionText를 setTypography/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_SIZES로 CLUB_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-unionfeature by modifying the same ClubUnionPage code—especiallyfrontend/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.tsandfrontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts—around member “representative” avatar/icon selection and$bgColor-driven profile card styling (with main PR evolvingProfileCardContainerintoProfileCard). - Moadong/moadong#1138: Main PR and
#1138both directly editfrontend/src/pages/ClubUnionPage/ClubUnionPage.styles.tsto change the profile layout/card styled-components (e.g.,ProfileGridand profile card container/components), while#1138separately 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.
Comment @coderabbitai help to get the list of available commands and usage tips.
✅ UI 변경사항 없음
전체 57개 스토리 · 22개 컴포넌트 |
There was a problem hiding this comment.
🧹 Nitpick comments (8)
frontend/src/pages/ClubUnionPage/ClubUnionPage.styles.ts (4)
161-173: 💤 Low value반응형 타이포그래피 일관성
CardName에서 태블릿 브레이크포인트에 대해서만font-size를 조정하고 있지만,CardRoleBadge와CardDescription은 태블릿과 모바일 모두에서 조정됩니다. 일관된 반응형 패턴을 적용하는 것이 좋습니다.♻️ 일관성 개선 제안
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-height와letter-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 tradeoffSNS 라벨 다국어화 고려
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타입 안전성 개선 고려
bgColor가string으로 타입되어 있어 런타임에 잘못된 색상 값이 전달될 수 있습니다.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
⛔ Files ignored due to path filters (6)
frontend/src/assets/images/icons/category_button/club_union/category_hobby_no_bg.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/category_button/club_union/category_performance_no_bg.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/category_button/club_union/category_religion_no_bg.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/category_button/club_union/category_sport_no_bg.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/category_button/club_union/category_study_no_bg.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/category_button/club_union/category_volunteer_no_bg.svgis excluded by!**/*.svg
📒 Files selected for processing (4)
frontend/docs/features/club-union/README.mdfrontend/src/constants/clubUnionInfo.tsfrontend/src/pages/ClubUnionPage/ClubUnionPage.styles.tsfrontend/src/pages/ClubUnionPage/ClubUnionPage.tsx
suhyun113
left a comment
There was a problem hiding this comment.
카드 너비가 너무 좁은 것 같아서 늘리면 좋을 것 같고, 대표 카드 위치도 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 중복 제거
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
frontend/src/pages/ClubUnionPage/ClubUnionPage.tsx (2)
75-76: ⚡ Quick winSNS 링크는 렌더링 전에 유효성 검증을 적용해주세요.
Line 75-76, 88-89에서 상수를 곧바로
href로 사용하고 있습니다. 공통 유틸(validateSocialLink)을 거쳐 안전한 URL만 노출하도록 맞춰주세요.As per coding guidelines, "Validate SNS links using
validateSocialLink()utility fromsrc/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
📒 Files selected for processing (3)
frontend/docs/features/club-union/README.mdfrontend/src/constants/clubUnionInfo.tsfrontend/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
| const [isMobile, setIsMobile] = useState( | ||
| () => window.matchMedia(MOBILE_BREAKPOINT).matches, | ||
| ); |
There was a problem hiding this comment.
브라우저 전역 객체 직접 접근으로 런타임 크래시가 날 수 있습니다.
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.
| 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; |
There was a problem hiding this comment.
고정 컬럼 합계와 멤버 수가 달라지면 일부 멤버가 누락됩니다.
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 음수값으로 아이콘을 우측 끝으로 밀어냄
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
스타일 개선
문서화