Conversation
Summary by CodeRabbit릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings. Walkthrough작업 선호도 기반 멤버 카드·목록에 UI 순서 변경, 조건부 렌더링 및 모달 연동을 추가하고 팀 리더 페이지의 디버깅 로그와 정렬 설명을 수정했습니다. Changes
Estimated code review effort🎯 3 (보통) | ⏱️ ~20분
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (3 warnings, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
|
🚀 프리뷰 배포 확인하기 🚀 |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/members/main/MemberCard/WorkPreferneceMemberCard.tsx(2 hunks)src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx(5 hunks)src/components/members/main/MemberList/index.tsx(4 hunks)src/pages/members/team-leaders.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/members/main/MemberList/index.tsx (2)
src/api/endpoint/members/getMemberOfMe.ts (1)
useGetMemberOfMe(30-38)src/constants/generation.ts (1)
LATEST_GENERATION(1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-app / build
- GitHub Check: chromatic-deploy
🔇 Additional comments (11)
src/pages/members/team-leaders.tsx (1)
20-20: 디버그 로그 제거 잘했습니다.프로덕션 코드에서 console.log를 제거한 것은 올바른 조치입니다.
src/components/members/main/MemberCard/WorkPreferneceMemberCard.tsx (2)
9-13: LGTM! 새로운 import가 올바르게 사용되고 있습니다.추가된 import들이 컴포넌트 내에서 모두 적절하게 사용되고 있습니다 (useModalState는 90번 라인, useEventLogger는 91번 라인, MessageModal은 174-189번 라인).
82-88: 배지 순서 변경이 의도된 것인지 확인하세요.
workPreference.workTime이communicationStyle앞으로 이동했습니다. 이는 UI 표시 순서만 변경하는 것이지만, PR 설명에 변경 이유가 명시되어 있지 않습니다.QA 이슈와 관련하여 이 순서 변경이 의도된 것인지 확인하세요.
src/components/members/main/MemberList/index.tsx (3)
11-11: LGTM! 사용자 데이터를 가져오는 로직이 올바릅니다.
useGetMemberOfMe훅을 사용하여 현재 사용자의 멤버 데이터를 가져오는 것이 적절합니다.Also applies to: 93-93
96-96: 최신 기수 확인 로직이 올바릅니다.
memberOfMeData?.generation === LATEST_GENERATION조건은 최신 기수(37기) 멤버에게만 작업 선호도 기능을 보여주도록 올바르게 구현되었습니다. Optional chaining으로 undefined 케이스도 적절히 처리됩니다.
257-261: LGTM! 조건부 렌더링이 모바일과 데스크톱 모두에서 일관되게 적용되었습니다.
canViewWorkPreference플래그를 사용하여 작업 선호도 매칭 목록을 적절히 게이트하고 있으며, 모바일과 데스크톱 뷰 모두에서 일관성 있게 구현되었습니다.Also applies to: 339-343
src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx (5)
3-3: LGTM! 모든 import가 컴포넌트에서 적절히 사용되고 있습니다.새로 추가된 import들이 모두 컴포넌트 내에서 올바르게 사용되고 있습니다.
Also applies to: 8-14
77-104: LGTM! 새로고침 컨트롤의 조건부 렌더링이 적절합니다.
!isEmpty && hasWorkPreference조건으로 새로고침 아이콘과 툴팁을 제어하는 것이 UX 관점에서 올바릅니다. 빈 상태일 때는 새로고침이 의미가 없으므로 숨기는 것이 적절합니다.
106-106: LGTM! 서브타이틀 조건부 렌더링이 일관성 있게 적용되었습니다.새로고침 컨트롤과 동일한 조건(
!isEmpty && hasWorkPreference)을 사용하여 서브타이틀을 표시하는 것이 일관된 UX를 제공합니다.
121-123: LGTM! 빈 상태 메시지가 사용자 상태에 따라 적절히 구분되었습니다.작업 선호도 설정 여부(
hasWorkPreference)에 따라 다른 메시지를 보여주는 것이 사용자 경험 측면에서 적절합니다.
239-239: 빈 상태의 블러 효과 증가가 시각적 계층 구조를 개선합니다.블러 값을 4px에서 6px로 증가시킨 것은 빈 상태 콘텐츠의 가독성을 향상시키는 작은 시각적 개선입니다.
| <Text typography='SUIT_32_B'>37기 앱잼 TL 후보를 만나보세요🔥</Text> | ||
| <Text typography='SUIT_18_M' color={colors.gray200}> | ||
| 정렬 순서는 접속할 때마다 무작위로 바뀌어요. | ||
| 정렬 순서는 이름 기준 가나다 순이에요. |
There was a problem hiding this comment.
UI 텍스트와 실제 정렬 로직이 일치하지 않습니다.
Line 29의 텍스트는 "이름 기준 가나다 순"으로 정렬되어 있다고 사용자에게 알리고 있지만, lines 42-54의 렌더링 로직에는 실제 정렬 구현이 없습니다. 현재 코드는 serviceType으로만 필터링하고 있으며, 백엔드에서 반환하는 순서에 전적으로 의존하고 있습니다.
만약 API가 정렬된 데이터를 반환하더라도, 프론트엔드에서 명시적으로 정렬을 보장하지 않으면:
- API 구현이 변경될 경우 UI가 잘못된 정보를 표시하게 됩니다
- 사용자가 기대하는 일관된 순서가 보장되지 않습니다
다음과 같이 명시적인 정렬 로직을 추가하세요:
<TeamLeaderCardsWrapper>
{tlMemberList
?.filter((tlMember) => tlMember.serviceType === selectedPart)
+ .sort((a, b) => a.name.localeCompare(b.name, 'ko-KR'))
.map((tlMember) => (
<TeamLeaderCard
id={tlMember.id}
profileImageUrl={tlMember.profileImage}
key={tlMember.id}
name={tlMember.name}
university={tlMember.university}
activities={tlMember.activities}
introduction={tlMember.introduction}
/>
))}
</TeamLeaderCardsWrapper>Also applies to: 42-54
🤖 Prompt for AI Agents
In src/pages/members/team-leaders.tsx around lines 29 and 42-54, the UI text
claims the list is "이름 기준 가나다 순" but the rendering logic only filters by
serviceType and relies on backend order; add an explicit client-side sort step
after filtering and before rendering: sort the array by member name using
locale-aware comparison (e.g., nameA.localeCompare(nameB, 'ko') with safe
null/undefined handling and consistent case normalization) so the frontend
guarantees Korean 가나다 순 order regardless of API response; ensure the sort runs
every render/update where the filtered list is computed.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx (1)
31-35:isEmpty계산을 boolean으로 고정해 “빈 리스트인데 빈 상태가 안 뜨는” 케이스를 방지해주세요
현재const isEmpty = data?.recommendations && data.recommendations.length === 0;(Line 33)은recommendations가 falsy/undefined인 경우isEmpty가undefined가 되어, 로딩 이후에도 빈 상태 대신 빈 그리드만 렌더될 여지가 있습니다.예:
- const isEmpty = data?.recommendations && data.recommendations.length === 0; - const hasWorkPreference = data?.hasWorkPreference; + const recommendations = data?.recommendations ?? []; + const isEmpty = recommendations.length === 0; + const hasWorkPreference = !!data?.hasWorkPreference; ... - const recommendations = data?.recommendations || [];Also applies to: 60-60, 108-140
🧹 Nitpick comments (2)
src/components/members/main/MemberList/index.tsx (1)
244-257: 주석 처리된 배너 블록은 feature flag/설정으로 관리하거나 제거 권장
Line 244-257, 330-338처럼 큰 JSX 블록을 주석으로 유지하면 유지보수/리뷰 비용이 커집니다(재활성화 계획이 있으면 토글 플래그로, 없으면 제거).Also applies to: 330-338
src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx (1)
64-76: “37기” 하드코딩은LATEST_GENERATION기반으로 치환 권장 +return null/TODO 정리
타이틀(“나와 37기…”)은 상수화된LATEST_GENERATION과 불일치하기 쉬워 유지보수 리스크가 큽니다. 또한MyPreferenceSubTitle은return <></>대신return null이 더 관용적이고, Line 126의 TODO는 현재 모달 오픈 로직이 들어와서( Line 37-44, 142 ) 의미가 바뀌었으니 정리하는 편이 좋습니다.예:
+ import { LATEST_GENERATION } from '@/constants/generation'; ... - 나와 37기 사람들의 작업 궁합은? + 나와 {LATEST_GENERATION}기 사람들의 작업 궁합은?Also applies to: 21-29, 121-131
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/members/main/MemberCard/WorkPreferneceMemberCard.tsx(3 hunks)src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx(7 hunks)src/components/members/main/MemberList/index.tsx(5 hunks)src/pages/members/team-leaders.tsx(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/components/members/main/MemberCard/WorkPreferneceMemberCard.tsx
- src/pages/members/team-leaders.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
src/components/members/main/MemberList/index.tsx (2)
src/api/endpoint/members/getMemberOfMe.ts (1)
useGetMemberOfMe(30-38)src/constants/generation.ts (1)
LATEST_GENERATION(1-1)
src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx (1)
src/styles/mediaQuery.ts (1)
MOBILE_MEDIA_QUERY(12-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build-app / build
- GitHub Check: chromatic-deploy
🔇 Additional comments (1)
src/components/members/main/MemberList/index.tsx (1)
258-262:canViewWorkPreference로 모바일/데스크톱 모두 조건부 노출 처리 좋습니다
Line 258-262, 340-344에서 동일한 가드로 노출을 제한해 “최신 기수만” 요구사항을 UI 레벨에서 명확히 충족합니다.Also applies to: 340-344
| import { useRouter } from 'next/router'; | ||
| import React, { ChangeEvent, FC, ReactNode, useEffect, useMemo, useState } from 'react'; | ||
|
|
||
| import { useGetMemberOfMe } from '@/api/endpoint/members/getMemberOfMe'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the MemberList component
find . -type f -name "index.tsx" -path "*MemberList*" 2>/dev/null | head -20Repository: sopt-makers/sopt-playground-frontend
Length of output: 130
🏁 Script executed:
# Also search for getMemberOfMe hook
find . -type f -name "*getMemberOfMe*" 2>/dev/nullRepository: sopt-makers/sopt-playground-frontend
Length of output: 123
🏁 Script executed:
# Get general structure
git ls-files | grep -E "(members|MemberList)" | head -20Repository: sopt-makers/sopt-playground-frontend
Length of output: 961
🏁 Script executed:
cat -n ./src/components/members/main/MemberList/index.tsx | head -120Repository: sopt-makers/sopt-playground-frontend
Length of output: 5560
🏁 Script executed:
cat -n ./src/api/endpoint/members/getMemberOfMe.tsRepository: sopt-makers/sopt-playground-frontend
Length of output: 1182
🏁 Script executed:
cat -n ./src/components/members/main/MemberList/index.tsx | sed -n '90,150p'Repository: sopt-makers/sopt-playground-frontend
Length of output: 2849
🏁 Script executed:
# Check if there's more context about isHydrated usage
cat -n ./src/components/members/main/MemberList/index.tsx | grep -A 5 -B 5 "isHydrated"Repository: sopt-makers/sopt-playground-frontend
Length of output: 1077
🏁 Script executed:
cat -n ./src/components/members/main/MemberList/index.tsx | grep -A 10 "setIsHydrated"Repository: sopt-makers/sopt-playground-frontend
Length of output: 830
🏁 Script executed:
# Get the full file to see all useEffect hooks
wc -l ./src/components/members/main/MemberList/index.tsxRepository: sopt-makers/sopt-playground-frontend
Length of output: 134
🏁 Script executed:
cat -n ./src/components/members/main/MemberList/index.tsx | sed -n '150,158p'Repository: sopt-makers/sopt-playground-frontend
Length of output: 352
useGetMemberOfMe() SSR/초기 렌더링 단계에서 불필요한 호출 방지 필요
Line 93에서 useGetMemberOfMe() 훅이 조건 없이 실행되어, SSR 단계와 초기 하이드레이션 중에 불필요한 API 호출이 발생합니다. isHydrated는 Line 154의 useEffect에서 설정되므로, 훅 호출 시점(Line 93)에는 아직 false 상태입니다. 따라서 UI 렌더링을 isHydrated로 막아도(Line 225) 실제 API 요청은 이미 진행된 상태입니다. 특히 인증이 필요한 엔드포인트의 경우 불필요한 에러와 재시도가 발생할 수 있습니다.
해결책:
useGetMemberOfMe를 옵션 파라미터(enabled포함)를 받도록 확장- 호출 시
{ enabled: isHydrated }로 제어
- const { data: memberOfMeData } = useGetMemberOfMe();
+ const { data: memberOfMeData } = useGetMemberOfMe({ enabled: isHydrated });src/api/endpoint/members/getMemberOfMe.ts의 useGetMemberOfMe 함수도 다음처럼 수정:
export const useGetMemberOfMe = (options?: UseQueryOptions) => {
return useQuery({
queryKey: ['getMemberOfMe'],
queryFn: async () => {
const data = await getMemberOfMe.request();
return data;
},
...options,
});
};🤖 Prompt for AI Agents
In src/components/members/main/MemberList/index.tsx around line 11 and use at
line 93, useGetMemberOfMe is called unconditionally causing API calls during
SSR/hydration; update the hook to accept query options and control execution
with an enabled flag, and then call it with { enabled: isHydrated } once
isHydrated is set. Modify src/api/endpoint/members/getMemberOfMe.ts to change
useGetMemberOfMe signature to accept an options parameter (e.g.,
UseQueryOptions) and spread those into the useQuery call so callers can pass
enabled, then in MemberList import and call useGetMemberOfMe({ enabled:
isHydrated }) to prevent requests before hydration.
| {!isEmpty && hasWorkPreference && ( | ||
| <> | ||
| <RefreshIconWrapper> | ||
| <button | ||
| onClick={() => { | ||
| queryClient.invalidateQueries({ queryKey: ['getRecommendations'] }); | ||
| }} | ||
| > | ||
| <StyledRefreshIcon /> | ||
| </button> | ||
| {/* <Responsive only='mobile'> */} | ||
| <MobileTooltipWrapper> | ||
| <Text typography='SUIT_13_M' color={colors.gray50}> | ||
| 더 많은 멤버를 찾아보세요! | ||
| </Text> | ||
| </MobileTooltipWrapper> | ||
| {/* </Responsive> */} | ||
| </RefreshIconWrapper> | ||
|
|
||
| {/* <Responsive only='desktop'> | ||
| <TooltipWrapper> | ||
| <Text typography='SUIT_13_M' color={colors.gray50}> | ||
| 더 많은 멤버를 찾아보세요! | ||
| </Text> | ||
| </TooltipWrapper> | ||
| </Responsive> */} | ||
| </> | ||
| )} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tsconfig noUnusedLocals/noUnusedParameters =="
fd -a 'tsconfig*.json' -x bash -lc 'echo "--- {}"; rg -n "noUnusedLocals|noUnusedParameters" "{}" || true'Repository: sopt-makers/sopt-playground-frontend
Length of output: 427
🏁 Script executed:
#!/bin/bash
echo "== Checking file existence and size =="
if [ -f "src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx" ]; then
wc -l "src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx"
else
echo "File not found"
fiRepository: sopt-makers/sopt-playground-frontend
Length of output: 197
🏁 Script executed:
#!/bin/bash
echo "== TooltipWrapper imports and usage =="
rg -n "TooltipWrapper|MobileTooltipWrapper|Responsive" src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx | head -50Repository: sopt-makers/sopt-playground-frontend
Length of output: 771
🏁 Script executed:
#!/bin/bash
echo "== Reading relevant sections of the file =="
if [ -f "src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx" ]; then
echo "--- Lines 1-30 (imports) ---"
sed -n '1,30p' src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx
echo ""
echo "--- Lines 75-110 (first concern area) ---"
sed -n '75,110p' src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx
echo ""
echo "--- Lines 145-175 (second concern area) ---"
sed -n '145,175p' src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx
echo ""
echo "--- Lines 178-205 (third concern area) ---"
sed -n '178,205p' src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx
fiRepository: sopt-makers/sopt-playground-frontend
Length of output: 4343
🏁 Script executed:
#!/bin/bash
echo "== Full tsconfig.json content =="
cat tsconfig.jsonRepository: sopt-makers/sopt-playground-frontend
Length of output: 894
🏁 Script executed:
#!/bin/bash
echo "== Search for TooltipWrapper usage across entire codebase =="
rg -n "TooltipWrapper" --type typescript --type tsxRepository: sopt-makers/sopt-playground-frontend
Length of output: 192
🏁 Script executed:
#!/bin/bash
echo "== Check if noUnusedLocals is enabled (including extends) =="
rg -A 50 '"compilerOptions"' tsconfig.json | head -80Repository: sopt-makers/sopt-playground-frontend
Length of output: 920
🏁 Script executed:
#!/bin/bash
echo "== Search for TooltipWrapper usage across codebase =="
rg "TooltipWrapper" --glob "*.tsx" --glob "*.ts" -nRepository: sopt-makers/sopt-playground-frontend
Length of output: 1420
🏁 Script executed:
#!/bin/bash
echo "== Check button element more carefully at lines 82-84 =="
sed -n '80,86p' src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsxRepository: sopt-makers/sopt-playground-frontend
Length of output: 403
모바일 툴팁이 데스크톱에서도 렌더되는 문제 + TooltipWrapper 미사용
라인 87-93에서 Responsive 래퍼가 주석 처리돼 MobileTooltipWrapper가 모든 뷰포트에서 노출됩니다. 또한 라인 180에서 정의된 TooltipWrapper는 라인 96-102의 주석 처리된 코드에만 포함되어 실제로는 렌더링되지 않습니다.
권장: (1) 모바일/데스크톱 분기를 위해 Responsive 래퍼를 복구하거나 (2) 하나의 툴팁 컴포넌트로 통합한 후 미사용 TooltipWrapper 정의와 주석 블록을 제거해주세요. 추가로 라인 82의 버튼에 type="button"과 aria-label 속성을 추가하시길 권장합니다.
🤖 Prompt for AI Agents
In src/components/members/main/MemberList/WorkPreferenceMatchedMemberList.tsx
around lines 77-104 (notably lines 82, 87-93 and 96-102) the
MobileTooltipWrapper is rendered for all viewports because the Responsive
wrapper was commented out and the TooltipWrapper (defined at line 180) is never
used; restore proper viewport branching or consolidate into a single Tooltip
component, remove the unused TooltipWrapper definition at line 180 and delete
the commented Responsive/Tooltip blocks, and add type="button" and a descriptive
aria-label on the button at line 82 (e.g., aria-label="refresh recommendations")
so the UI behaves correctly and is accessible.
🤫 쉿, 나한테만 말해줘요. 이슈넘버
🧐 어떤 것을 변경했어요~?
🤔 그렇다면, 어떻게 구현했어요~?
❤️🔥 당신이 생각하는 PR포인트, 내겐 매력포인트.
📸 스크린샷, 없으면 이것 참,, 섭섭한데요?