Skip to content
Merged

fix QA #2077

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { IconSend, IconUser } from '@sopt-makers/icons';
import { m } from 'framer-motion';
import { useRouter } from 'next/router';

import useModalState from '@/components/common/Modal/useModalState';
import ResizedImage from '@/components/common/ResizedImage';
import Text from '@/components/common/Text';
import useEventLogger from '@/components/eventLogger/hooks/useEventLogger';
import MessageModal, { MessageCategory } from '@/components/members/detail/MessageSection/MessageModal';
import { useVisibleBadges } from '@/components/members/main/hooks/useVisibleBadges';
import { LATEST_GENERATION } from '@/constants/generation';
import useModalState from '@/components/common/Modal/useModalState';
import MessageModal, { MessageCategory } from '@/components/members/detail/MessageSection/MessageModal';
import useEventLogger from '@/components/eventLogger/hooks/useEventLogger';

interface Activity {
id: number;
Expand Down Expand Up @@ -81,9 +81,9 @@ const WorkPreferenceMemberCard = ({

const workPreferenceBadges = [
workPreference.ideationStyle,
workPreference.workTime,
workPreference.communicationStyle,
workPreference.workPlace,
workPreference.workTime,
workPreference.feedbackStyle,
];

Expand Down Expand Up @@ -166,9 +166,7 @@ const WorkPreferenceMemberCard = ({
{isLoading ? (
<LoadingTextField />
) : (
<MessageButton onClick={onOpenMessageModal}>
우리 μΉœν•΄μ Έμš” <IconSend style={{ width: '16px', height: '16px', marginLeft: '4px' }} />
</MessageButton>
<MessageButton onClick={onOpenMessageModal}>κ°€λ³κ²Œ 인사해 λ³ΌκΉŒμš”?</MessageButton>
)}
</MotionMemberCard>
{isOpenMessageModal && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';
import { fonts } from '@sopt-makers/fonts';
import { Button } from '@sopt-makers/ui';
import { useQueryClient } from '@tanstack/react-query';

import { useGetRecommendations } from '@/api/endpoint/members/getRecommendations';
import { useGetMyWorkPreference } from '@/api/endpoint/members/getWorkPreference';
import useModalState from '@/components/common/Modal/useModalState';
import Responsive from '@/components/common/Responsive/Responsive';
import Text from '@/components/common/Text';
import { convertWorkPreferenceToHashtags } from '@/components/matchmember/constant';
import { useMatchMemberEvent } from '@/components/matchmember/hooks/useMatchMemberEvent';
import MatchMemberModal from '@/components/matchmember/MatchMemberModal';
import { DESKTOP_ONE_MEDIA_QUERY, DESKTOP_TWO_MEDIA_QUERY } from '@/components/members/main/contants';
import WorkPreferenceMemberCard from '@/components/members/main/MemberCard/WorkPreferneceMemberCard';
import { mockRecommendationsResponse } from '@/components/members/main/MemberList/constants';
import RefreshIcon from '@/public/icons/icon_refresh.svg';
import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery';
import MatchMemberModal from '@/components/matchmember/MatchMemberModal';
import { useMatchMemberEvent } from '@/components/matchmember/hooks/useMatchMemberEvent';
import useModalState from '@/components/common/Modal/useModalState';
import { fonts } from '@sopt-makers/fonts';
import { useGetMyWorkPreference } from '@/api/endpoint/members/getWorkPreference';
import { convertWorkPreferenceToHashtags } from '@/components/matchmember/constant';

const MyPreferenceSubTitle = () => {
const { data: myData, isLoading: myLoading } = useGetMyWorkPreference();
Expand All @@ -32,6 +32,7 @@ const WorkPreferenceMatchedMemberList = () => {
const { data, isLoading } = useGetRecommendations();
const isEmpty = data?.recommendations && data.recommendations.length === 0;
const hasWorkPreference = data?.hasWorkPreference;

const queryClient = useQueryClient();
const { canOpenModal, handleCloseForToday } = useMatchMemberEvent();
const { isOpen, onOpen, onClose } = useModalState();
Expand Down Expand Up @@ -73,31 +74,36 @@ const WorkPreferenceMatchedMemberList = () => {
λ‚˜μ™€ 37κΈ° μ‚¬λžŒλ“€μ˜ μž‘μ—… ꢁ합은?
</Text>
</Responsive>
<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>
{!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> */}
</>
)}
Comment on lines +77 to +104
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

🧩 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"
fi

Repository: 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 -50

Repository: 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
fi

Repository: sopt-makers/sopt-playground-frontend

Length of output: 4343


🏁 Script executed:

#!/bin/bash
echo "== Full tsconfig.json content =="
cat tsconfig.json

Repository: 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 tsx

Repository: 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 -80

Repository: 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" -n

Repository: 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.tsx

Repository: 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.

</TitleWrapper>
<MyPreferenceSubTitle />
{!isEmpty && hasWorkPreference && <MyPreferenceSubTitle />}
</TitleContainer>
{isEmpty ? (
<EmptyStateWrapper>
Expand All @@ -113,7 +119,7 @@ const WorkPreferenceMatchedMemberList = () => {
style={{ textAlign: 'center', whiteSpace: 'pre-line' }}
>
{hasWorkPreference
? '아직 ꢁ합이 λ§žλŠ” 멀버가 μ—†μ–΄μš”.'
? '아직 λ‚˜μ™€ λ§žλŠ” 멀버가 λ‚˜νƒ€λ‚˜μ§€ μ•Šμ•˜μ–΄μš”. \n λ‹€λ₯Έ 멀버듀을 μ‚΄νŽ΄λ³ΌκΉŒμš”?'
: 'λ‚˜μ˜ μž‘μ—… μŠ€νƒ€μΌμ„ 5μ΄ˆλ§Œμ— μ•Œμ•„λ³΄κ³ \nμ°°λ–‘ μΌ€λ―Έ μ•±μžΌ 멀버 ν™•μΈν•΄μš”!'}
</Text>

Expand Down Expand Up @@ -157,7 +163,7 @@ const MobileTooltipWrapper = styled.div`
left: 50%;
transform: translate(-50%, -5%);
background-color: ${colors.gray600};
width: 20px;
width: 16px;
height: 14px;
clip-path: polygon(0 0, 50% 100%, 100% 0);
content: '';
Expand Down Expand Up @@ -213,10 +219,10 @@ const StyledContainer = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 90px;
margin-top: 78px;
width: 100%;
@media ${MOBILE_MEDIA_QUERY} {
margin-top: 24px;
margin-top: 54px;
padding: 12px 0;
}
`;
Expand All @@ -230,7 +236,7 @@ const WorkPreferenceMemberListWrapper = styled.div<{ isEmpty: boolean }>`
height: 190px;
overflow-y: hidden;
scrollbar-width: none;
filter: ${({ isEmpty }) => (isEmpty ? 'blur(4px)' : 'none')};
filter: ${({ isEmpty }) => (isEmpty ? 'blur(6px)' : 'none')};

@media ${DESKTOP_ONE_MEDIA_QUERY} {
grid-template-columns: repeat(3, minmax(10px, 319px));
Expand Down
29 changes: 18 additions & 11 deletions src/components/members/main/MemberList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Link from 'next/link';
import { useRouter } from 'next/router';
import React, { ChangeEvent, FC, ReactNode, useEffect, useMemo, useState } from 'react';

import { useGetMemberOfMe } from '@/api/endpoint/members/getMemberOfMe';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the MemberList component
find . -type f -name "index.tsx" -path "*MemberList*" 2>/dev/null | head -20

Repository: sopt-makers/sopt-playground-frontend

Length of output: 130


🏁 Script executed:

# Also search for getMemberOfMe hook
find . -type f -name "*getMemberOfMe*" 2>/dev/null

Repository: sopt-makers/sopt-playground-frontend

Length of output: 123


🏁 Script executed:

# Get general structure
git ls-files | grep -E "(members|MemberList)" | head -20

Repository: sopt-makers/sopt-playground-frontend

Length of output: 961


🏁 Script executed:

cat -n ./src/components/members/main/MemberList/index.tsx | head -120

Repository: sopt-makers/sopt-playground-frontend

Length of output: 5560


🏁 Script executed:

cat -n ./src/api/endpoint/members/getMemberOfMe.ts

Repository: 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.tsx

Repository: 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 μš”μ²­μ€ 이미 μ§„ν–‰λœ μƒνƒœμž…λ‹ˆλ‹€. 특히 인증이 ν•„μš”ν•œ μ—”λ“œν¬μΈνŠΈμ˜ 경우 λΆˆν•„μš”ν•œ μ—λŸ¬μ™€ μž¬μ‹œλ„κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν•΄κ²°μ±…:

  1. useGetMemberOfMeλ₯Ό μ˜΅μ…˜ νŒŒλΌλ―Έν„°(enabled 포함)λ₯Ό 받도둝 ν™•μž₯
  2. 호좜 μ‹œ { 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.

import { Profile } from '@/api/endpoint_LEGACY/members/type';
import BottomSheetSelect from '@/components/coffeechat/upload/CoffeechatForm/BottomSheetSelect';
import EmptyView from '@/components/common/EmptyView';
Expand Down Expand Up @@ -89,9 +90,10 @@ const MemberList: FC<MemberListProps> = ({ banner }) => {
const { addQueryParamsToUrl } = usePageQueryParams({
skipNull: true,
});
const { data: memberOfMeData } = useGetMemberOfMe();

const isEmpty = memberProfileData?.pages[0].members.length === 0;

const canViewWorkPreference = memberOfMeData?.generation === LATEST_GENERATION;
const profiles = useMemo(
() =>
memberProfileData?.pages.map((page) =>
Expand Down Expand Up @@ -239,7 +241,8 @@ const MemberList: FC<MemberListProps> = ({ banner }) => {
onReset={handleSearchReset}
/>

<BannerWrapper>
{/* TODO: TL리슀트 μΆ”ν›„ 배포 */}
{/* <BannerWrapper>
<Banner
src={'/icons/img/banner_TL_list_tablet.png'}
alt='TL List Link'
Expand All @@ -250,11 +253,13 @@ const MemberList: FC<MemberListProps> = ({ banner }) => {
alt='TL List Link'
onClick={() => router.push(playgroundLink.teamLeaderList())}
/>
</BannerWrapper>
</BannerWrapper> */}

<Responsive only='mobile'>
<WorkPreferenceMatchedMemberList />
</Responsive>
{canViewWorkPreference && (
<Responsive only='mobile'>
<WorkPreferenceMatchedMemberList />
</Responsive>
)}
<StyledMobileFilterWrapper>
<BottomSheetSelect
options={GENERATION_OPTIONS}
Expand Down Expand Up @@ -322,19 +327,21 @@ const MemberList: FC<MemberListProps> = ({ banner }) => {
)}
</Responsive>
</div>
<Responsive asChild only='desktop'>
{/* <Responsive asChild only='desktop'>
<BannerWrapper>
<Banner
src={'/icons/img/banner_TL_list_desktop.png'}
alt='TL List Link'
onClick={() => router.push(playgroundLink.teamLeaderList())}
/>
</BannerWrapper>
</Responsive>
</Responsive> */}
<StyledMain>
<Responsive only='desktop'>
<WorkPreferenceMatchedMemberList />
</Responsive>
{canViewWorkPreference && (
<Responsive only='desktop'>
<WorkPreferenceMatchedMemberList />
</Responsive>
)}
{banner && (
<Responsive
only='desktop'
Expand Down
8 changes: 4 additions & 4 deletions src/pages/members/team-leaders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const cardComponentWidth = 316;

const TeamLeadersPage = () => {
const { data: tlMemberList } = useGetTLMember();
console.log(tlMemberList);

const [selectedPart, setSelectedPart] = useState<SelectedPart>('APP');
return (
<AuthRequired>
Expand All @@ -26,7 +26,7 @@ const TeamLeadersPage = () => {
<TitleWrapper>
<Text typography='SUIT_32_B'>37κΈ° μ•±μžΌ TL 후보λ₯Ό λ§Œλ‚˜λ³΄μ„Έμš”πŸ”₯</Text>
<Text typography='SUIT_18_M' color={colors.gray200}>
μ •λ ¬ μˆœμ„œλŠ” 접속할 λ•Œλ§ˆλ‹€ λ¬΄μž‘μœ„λ‘œ λ°”λ€Œμ–΄μš”.
μ •λ ¬ μˆœμ„œλŠ” 이름 κΈ°μ€€ κ°€λ‚˜λ‹€ μˆœμ΄μ—μš”.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

</Text>
</TitleWrapper>
<ChipWrapper>
Expand All @@ -38,7 +38,7 @@ const TeamLeadersPage = () => {
</Chip>
</ChipWrapper>

<TeamLeaderCardsWrapper>
{/* <TeamLeaderCardsWrapper>
{tlMemberList
?.filter((tlMember) => tlMember.serviceType === selectedPart)
.map((tlMember) => (
Expand All @@ -52,7 +52,7 @@ const TeamLeadersPage = () => {
introduction={tlMember.introduction}
/>
))}
</TeamLeaderCardsWrapper>
</TeamLeaderCardsWrapper> */}
</StyledMain>
</StyledContainer>
</AuthRequired>
Expand Down
Loading