Conversation
- keepPrevious 옵션 도입: 재검색 시 기존 results를 유지한 채 API 호출 → 글자 입력·Enter·필터·카테고리 변경 시 "검색 중..." 플래시 제거 - Content에 isRefetching prop 추가: 재검색 중 opacity 0.45 fade 처리 - Effect를 C/D로 분리 · Effect C(searchStatus, searchTerm): 검색어·상태 전환 시 호출 · Effect D(selectedFilter, category): 필터·카테고리 변경 시만 호출 - 카운트(전체 N) 초기 로딩 중 "전체 0" 표시 방지 - isEmpty 판단 시 type !== 'searching' 조건 추가
- OtherFeedPage, MyFeedPage 로딩 중 return <></> → LoadingScreen으로 교체 - LoadingScreen에 background-color: #121212 적용해 배경색 일치 - 로딩 중에도 TitleHeader 표시해 뒤로 가기 가능하도록 유지
- RecentSearchTabs에 isLoading prop 추가 - 로딩 중 "최근 검색어를 불러오고 있습니다." 표시 - UserSearch, GroupSearch에서 fetchRecentSearches 로딩 상태 관리 추가
- 검색 중(debounce 대기 or API 응답 전)에 "검색 중..." 표시 - UserSearchResult isEmpty 판단 시 type !== 'searching' 조건 추가 - LoadingMessage 스타일 컴포넌트 추가
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 Walkthrough검색·피드 관련 컴포넌트에 로딩·재검색 상태 플래그( Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant GroupSearch
participant API
participant GroupSearchResult
participant RecentSearchTabs
User->>GroupSearch: 입력/필터 변경 (중간/최종)
GroupSearch->>GroupSearch: set isLoading / decide keepPrevious
GroupSearch->>API: searchFirstPage(term, ..., keepPrevious)
API-->>GroupSearch: results (rooms, total, nextCursor)
GroupSearch->>GroupSearchResult: update props (rooms, isLoading, isRefetching)
GroupSearch->>RecentSearchTabs: update props (recentSearches, isLoading)
GroupSearchResult-->>User: render (opacity changes if isRefetching)
RecentSearchTabs-->>User: render (loading message if isLoading)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pages/groupSearch/GroupSearch.tsx (1)
48-66:⚠️ Potential issue | 🟡 Minor초기 마운트 시 최근 검색어 API 이중 호출
useEffect([], [])(Line 48)와useEffect([searchStatus])(Line 62)가 모두 마운트 시 실행됩니다.searchStatus의 초깃값이'idle'이므로 두 이펙트가 동시에getRecentSearch('ROOM')을 호출하게 되어 불필요한 중복 요청이 발생합니다.Line 48의 IIFE 이펙트를 제거하고
fetchRecentSearches에 위임하는 것을 권장합니다.♻️ 제안 변경
- useEffect(() => { - (async () => { - setIsRecentLoading(true); - try { - const response = await getRecentSearch('ROOM'); - setRecentSearches(response.isSuccess ? response.data.recentSearchList : []); - } catch { - setRecentSearches([]); - } finally { - setIsRecentLoading(false); - } - })(); - }, []); - useEffect(() => { if (searchStatus === 'idle') { fetchRecentSearches(); } }, [searchStatus]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/groupSearch/GroupSearch.tsx` around lines 48 - 66, Remove the IIFE useEffect that directly calls getRecentSearch and instead delegate initial load to the existing fetchRecentSearches helper so you don't double-call the API; delete the useEffect containing the async IIFE that calls getRecentSearch('ROOM') and rely on the useEffect that checks searchStatus === 'idle' to invoke fetchRecentSearches (ensure fetchRecentSearches still calls getRecentSearch, updates setRecentSearches and setIsRecentLoading and handles errors).
🧹 Nitpick comments (7)
src/pages/feed/MyFeedPage.styled.ts (1)
10-16:LoadingScreen의 기본 레이아웃 스타일 중복 및 파일 위치 문제
LoadingScreen은Container와 동일한min-width,max-width,margin선언을 그대로 반복하고 있습니다. 또한 이 컴포넌트는OtherFeedPage.tsx에서도MyFeedPage.styled.ts로부터 임포트되어 사용되므로,MyFeedPage전용 스타일 파일에 두는 것은 아키텍처 상 부적절합니다.Emotion의 컴포넌트 확장 패턴을 사용하면 중복을 없앨 수 있으며, 공유 컴포넌트는 공용 파일로 이동하는 것이 적합합니다.
♻️ 제안: Container 확장 및 공유 위치로 이동
LoadingScreen을MyFeedPage.styled.ts에서 독립된 공유 파일(예:src/pages/feed/FeedPage.styled.ts)로 이동하고,Container를 확장하여 중복을 제거합니다:-export const LoadingScreen = styled.div` - min-width: 320px; - max-width: 767px; - margin: 0 auto; - min-height: 100dvh; - background-color: ${colors.black.main}; -`; +export const LoadingScreen = styled(Container)` + min-height: 100dvh; + background-color: ${colors.black.main}; +`;이후
MyFeedPage.tsx와OtherFeedPage.tsx모두 공유 파일에서 임포트합니다:// 예: src/pages/feed/FeedPage.styled.ts 에 Container, LoadingScreen 통합 import { Container, LoadingScreen } from './FeedPage.styled';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/feed/MyFeedPage.styled.ts` around lines 10 - 16, LoadingScreen duplicates Container layout and lives in a page-specific file while also being imported by OtherFeedPage; fix by moving LoadingScreen into a shared feed styles file (e.g., create FeedPage.styled.ts) and implement it by extending the existing Container using Emotion's extension (styled(Container)`min-height: 100dvh; background-color: ${colors.black.main};`) so only the differing rules remain; update MyFeedPage.tsx and OtherFeedPage.tsx to import LoadingScreen (and Container if needed) from the new shared file and remove the duplicate declarations from MyFeedPage.styled.ts.src/pages/feed/OtherFeedPage.tsx (1)
11-11:MyFeedPage.styled로부터의 교차 임포트 및 두 페이지 컴포넌트 간 코드 중복
OtherFeedPage.tsx가MyFeedPage.styled.ts에서 직접LoadingScreen을 가져오는 것은 페이지 간 순환적 의존 위험을 내포하며, 파일 이름만으로는 공유 컴포넌트임을 알기 어렵습니다.또한
MyFeedPage.tsx와OtherFeedPage.tsx는 다음 요소가 완전히 동일합니다:
- 상태 변수(
feedData,profileData,loading,error)useEffect내 데이터 페치 로직 (Promise.all, 에러 처리,finally)handleBackClick- 로딩/에러 분기 렌더링
OtherFeed에 전달하는 props(isMyFeed,isMyself,showFollowButton)만 다릅니다. 공유 데이터 페치 로직은 커스텀 훅으로 추출하고,LoadingScreen은 공용 스타일 파일로 이동하는 것을 권장합니다.♻️ 제안: 커스텀 훅으로 공통 로직 추출
// src/hooks/useUserFeedData.ts import { useState, useEffect } from 'react'; import { getOtherFeed, type OtherFeedItem } from '@/api/feeds/getOtherFeed'; import { getOtherProfile } from '@/api/users/getOtherProfile'; import type { OtherProfileData } from '@/types/profile'; export function useUserFeedData(userId: string | undefined) { const [feedData, setFeedData] = useState<OtherFeedItem[]>([]); const [profileData, setProfileData] = useState<OtherProfileData | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { const loadOtherData = async () => { if (!userId) { setError('사용자 ID가 없습니다.'); setLoading(false); return; } try { setLoading(true); const [feedResponse, profileResponse] = await Promise.all([ getOtherFeed(Number(userId)), getOtherProfile(Number(userId)), ]); setFeedData(feedResponse.data.feedList); setProfileData(profileResponse.data); setError(null); } catch (err) { console.error('다른 사용자 데이터 로드 실패:', err); setError('사용자 정보를 불러오는데 실패했습니다.'); } finally { setLoading(false); } }; loadOtherData(); }, [userId]); return { feedData, profileData, loading, error }; }이후 각 페이지 컴포넌트에서:
const { feedData, profileData, loading, error } = useUserFeedData(userId);그리고
LoadingScreen은src/pages/feed/FeedPage.styled.ts(또는 동등한 공유 파일)에서 임포트합니다.Also applies to: 55-64
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/feed/OtherFeedPage.tsx` at line 11, OtherFeedPage imports LoadingScreen from MyFeedPage.styled causing cross-file coupling and duplicates data-fetching logic (feedData, profileData, loading, error, useEffect, handleBackClick) between MyFeedPage and OtherFeedPage; extract the shared fetch logic into a custom hook (e.g., useUserFeedData(userId) that returns {feedData, profileData, loading, error}) and update both pages to call that hook, move LoadingScreen into a shared styled module (e.g., FeedPage.styled) and change imports in OtherFeedPage and MyFeedPage to import LoadingScreen from that shared file, and remove duplicated useEffect/Promise.all/error/finally code and any duplicate state variables from OtherFeedPage so it only handles page-specific props (isMyFeed, isMyself, showFollowButton) and local UI handlers like handleBackClick.src/pages/feed/UserSearchResult.tsx (2)
25-40:onLoadMoreprop 변경 시마다 IntersectionObserver가 재등록되는 문제
onLoadMore이 의존성 배열에 포함되어 있어, 부모 컴포넌트가useCallback없이 함수를 내려보내면 매 렌더링마다 새로운 참조가 생성되고 observer가disconnect→ 재등록됩니다. 이 방식은 의존성 배열에 상태값을 넣어 최신값을 확보할 수는 있지만, 매번 Observer API를 등록/해제하는 것은 이상적이지 않습니다.콜백을
useRef에 저장하면 observer 인스턴스를 안정적으로 유지하면서도 최신onLoadMore를 항상 참조할 수 있습니다.♻️ 제안: onLoadMore를 ref로 관리하여 observer 재등록 방지
+ const onLoadMoreRef = useRef(onLoadMore); + useEffect(() => { + onLoadMoreRef.current = onLoadMore; + }, [onLoadMore]); + useEffect(() => { const observer = new IntersectionObserver( entries => { if (entries[0].isIntersecting && hasMore && !loading && onLoadMore) { - onLoadMore(); + onLoadMoreRef.current?.(); } }, { threshold: 0.1 }, ); if (observerRef.current) { observer.observe(observerRef.current); } return () => observer.disconnect(); - }, [hasMore, loading, onLoadMore]); + }, [hasMore, loading]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/feed/UserSearchResult.tsx` around lines 25 - 40, The current useEffect re-creates the IntersectionObserver whenever the onLoadMore prop identity changes, causing unnecessary disconnects; fix by storing onLoadMore in a ref (e.g., onLoadMoreRef = useRef(onLoadMore)), update onLoadMoreRef.current inside a separate effect whenever prop changes, and change the observer callback to call onLoadMoreRef.current() instead of onLoadMore; keep the IntersectionObserver creation effect dependent only on stable values (hasMore, loading) so the observer instance (observerRef) is not re-registered on every onLoadMore prop change and still invokes the latest callback.
45-45: 빈 fragment<></>대신&&조건부 렌더링 사용 권장♻️ 제안: 불필요한 빈 fragment 제거
- {type === 'searching' ? <></> : <ResultHeader>전체 {searchedUserList.length}</ResultHeader>} + {type !== 'searching' && <ResultHeader>전체 {searchedUserList.length}</ResultHeader>}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/feed/UserSearchResult.tsx` at line 45, In UserSearchResult, replace the ternary that returns an empty fragment when type === 'searching' with short-circuit rendering: instead of "{type === 'searching' ? <></> : <ResultHeader>전체 {searchedUserList.length}</ResultHeader>}", use a conditional that only renders ResultHeader when type is not 'searching' (e.g., "type !== 'searching' && <ResultHeader>전체 {searchedUserList.length}</ResultHeader>") so you remove the unnecessary empty fragment while keeping the same behavior; update the JSX where ResultHeader and searchedUserList are referenced.src/pages/groupSearch/GroupSearch.tsx (1)
351-352: 로딩 조건 단순화 제안두 조건 모두
rooms.length === 0을 공유하므로 인수분해하면 가독성이 향상됩니다.♻️ 제안 변경
- {(isLoading && rooms.length === 0) || - (searchStatus === 'searching' && rooms.length === 0) ? ( + {rooms.length === 0 && (isLoading || searchStatus === 'searching') ? ( <LoadingMessage>검색 중...</LoadingMessage>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/groupSearch/GroupSearch.tsx` around lines 351 - 352, The render condition duplicates rooms.length === 0; refactor the JSX conditional in GroupSearch (where isLoading, searchStatus and rooms are used) by extracting the shared check and combining the rest with OR — e.g., replace "(isLoading && rooms.length === 0) || (searchStatus === 'searching' && rooms.length === 0)" with "rooms.length === 0 && (isLoading || searchStatus === 'searching')" so the intention is clearer and the expression is shorter.src/components/search/GroupSearchResult.tsx (1)
93-94: 가독성 개선 제안:isLoading && !isRefetching표현식 단순화
isRefetching = isLoading && mapped.length > 0이므로,isLoading && !isRefetching은isLoading && mapped.length === 0과 동치입니다. 중간 변수isRefetching을 거치지 않고 직접 표현하면 의도가 더 명확하게 전달됩니다.♻️ 제안 변경
- {/* 재검색 중엔 이전 카운트를 유지하고, 초기 검색 완료 시 카운트를 표시 */} - <GroupNum>{isLoading && !isRefetching ? '' : `전체 ${mapped.length}`}</GroupNum> + {/* 초기 로딩 중(이전 결과 없음)에는 카운트 숨기고, 재검색·완료 시 표시 */} + <GroupNum>{isLoading && mapped.length === 0 ? '' : `전체 ${mapped.length}`}</GroupNum>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/search/GroupSearchResult.tsx` around lines 93 - 94, The conditional rendering for GroupNum is using the redundant boolean expression isLoading && !isRefetching; replace it with the simpler equivalent isLoading && mapped.length === 0 to improve clarity: locate the JSX line that sets GroupNum (using isLoading, isRefetching, mapped.length) and change the condition to directly check mapped.length === 0 when isLoading, leaving the displayed string `전체 ${mapped.length}` unchanged for all other cases.src/pages/feed/UserSearch.styled.ts (1)
29-36:font-size디자인 토큰 사용 권장다른 styled 파일들(예:
GroupSearchResult.styled.ts)은${typography.fontSize.sm}같은 타이포그래피 토큰을 사용하는데, 여기는16px가 하드코딩되어 있어 디자인 시스템과 일관성이 없습니다.♻️ 제안 변경
-import { colors } from '@/styles/global/global'; +import { colors, typography } from '@/styles/global/global'; export const LoadingMessage = styled.div` display: flex; justify-content: center; align-items: center; padding: 40px 20px; color: ${colors.white}; - font-size: 16px; + font-size: ${typography.fontSize.md}; `;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/pages/feed/UserSearch.styled.ts` around lines 29 - 36, The LoadingMessage styled component uses a hardcoded font-size (16px); replace it with the typography token (e.g., typography.fontSize.sm) and ensure the typography tokens are imported at top of the file; update the font-size line in LoadingMessage to use that token so it matches other components like GroupSearchResult.styled.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/pages/feed/UserSearch.tsx`:
- Around line 30-38: fetchRecentSearches currently sets loading true and awaits
getRecentSearch('USER') but doesn't check response.isSuccess and lacks a catch,
which can cause TypeErrors or unhandled rejections; update fetchRecentSearches
to wrap the await in a try/catch, in the try assert response.isSuccess (or that
response.data is defined) before calling
setRecentSearches(response.data.recentSearchList), call setRecentSearches only
on success, handle/log errors in the catch (so they don't bubble as unhandled
rejections), and keep setIsRecentLoading(false) in the finally block; reference
the fetchRecentSearches function, getRecentSearch call, setRecentSearches, and
setIsRecentLoading when making the changes.
In `@src/pages/feed/UserSearchResult.tsx`:
- Line 21: The empty-state branch in UserSearchResult uses a loading ternary
that can never be true given how UserSearch (parent) conditionally renders: when
type === 'searched' and searchedUserList.length === 0, loading is guaranteed
false; remove the unreachable loading branch inside the EmptyWrapper (the
ternary that returns '사용자 찾는 중...' vs '찾는 사용자가 없어요.') and always render the "찾는
사용자가 없어요." message for the isEmpty case; keep the isEmpty computation
(searchedUserList.length === 0 && type !== 'searching') and only change the
EmptyWrapper rendering logic in UserSearchResult.
---
Outside diff comments:
In `@src/pages/groupSearch/GroupSearch.tsx`:
- Around line 48-66: Remove the IIFE useEffect that directly calls
getRecentSearch and instead delegate initial load to the existing
fetchRecentSearches helper so you don't double-call the API; delete the
useEffect containing the async IIFE that calls getRecentSearch('ROOM') and rely
on the useEffect that checks searchStatus === 'idle' to invoke
fetchRecentSearches (ensure fetchRecentSearches still calls getRecentSearch,
updates setRecentSearches and setIsRecentLoading and handles errors).
---
Nitpick comments:
In `@src/components/search/GroupSearchResult.tsx`:
- Around line 93-94: The conditional rendering for GroupNum is using the
redundant boolean expression isLoading && !isRefetching; replace it with the
simpler equivalent isLoading && mapped.length === 0 to improve clarity: locate
the JSX line that sets GroupNum (using isLoading, isRefetching, mapped.length)
and change the condition to directly check mapped.length === 0 when isLoading,
leaving the displayed string `전체 ${mapped.length}` unchanged for all other
cases.
In `@src/pages/feed/MyFeedPage.styled.ts`:
- Around line 10-16: LoadingScreen duplicates Container layout and lives in a
page-specific file while also being imported by OtherFeedPage; fix by moving
LoadingScreen into a shared feed styles file (e.g., create FeedPage.styled.ts)
and implement it by extending the existing Container using Emotion's extension
(styled(Container)`min-height: 100dvh; background-color: ${colors.black.main};`)
so only the differing rules remain; update MyFeedPage.tsx and OtherFeedPage.tsx
to import LoadingScreen (and Container if needed) from the new shared file and
remove the duplicate declarations from MyFeedPage.styled.ts.
In `@src/pages/feed/OtherFeedPage.tsx`:
- Line 11: OtherFeedPage imports LoadingScreen from MyFeedPage.styled causing
cross-file coupling and duplicates data-fetching logic (feedData, profileData,
loading, error, useEffect, handleBackClick) between MyFeedPage and
OtherFeedPage; extract the shared fetch logic into a custom hook (e.g.,
useUserFeedData(userId) that returns {feedData, profileData, loading, error})
and update both pages to call that hook, move LoadingScreen into a shared styled
module (e.g., FeedPage.styled) and change imports in OtherFeedPage and
MyFeedPage to import LoadingScreen from that shared file, and remove duplicated
useEffect/Promise.all/error/finally code and any duplicate state variables from
OtherFeedPage so it only handles page-specific props (isMyFeed, isMyself,
showFollowButton) and local UI handlers like handleBackClick.
In `@src/pages/feed/UserSearch.styled.ts`:
- Around line 29-36: The LoadingMessage styled component uses a hardcoded
font-size (16px); replace it with the typography token (e.g.,
typography.fontSize.sm) and ensure the typography tokens are imported at top of
the file; update the font-size line in LoadingMessage to use that token so it
matches other components like GroupSearchResult.styled.ts.
In `@src/pages/feed/UserSearchResult.tsx`:
- Around line 25-40: The current useEffect re-creates the IntersectionObserver
whenever the onLoadMore prop identity changes, causing unnecessary disconnects;
fix by storing onLoadMore in a ref (e.g., onLoadMoreRef = useRef(onLoadMore)),
update onLoadMoreRef.current inside a separate effect whenever prop changes, and
change the observer callback to call onLoadMoreRef.current() instead of
onLoadMore; keep the IntersectionObserver creation effect dependent only on
stable values (hasMore, loading) so the observer instance (observerRef) is not
re-registered on every onLoadMore prop change and still invokes the latest
callback.
- Line 45: In UserSearchResult, replace the ternary that returns an empty
fragment when type === 'searching' with short-circuit rendering: instead of
"{type === 'searching' ? <></> : <ResultHeader>전체
{searchedUserList.length}</ResultHeader>}", use a conditional that only renders
ResultHeader when type is not 'searching' (e.g., "type !== 'searching' &&
<ResultHeader>전체 {searchedUserList.length}</ResultHeader>") so you remove the
unnecessary empty fragment while keeping the same behavior; update the JSX where
ResultHeader and searchedUserList are referenced.
In `@src/pages/groupSearch/GroupSearch.tsx`:
- Around line 351-352: The render condition duplicates rooms.length === 0;
refactor the JSX conditional in GroupSearch (where isLoading, searchStatus and
rooms are used) by extracting the shared check and combining the rest with OR —
e.g., replace "(isLoading && rooms.length === 0) || (searchStatus ===
'searching' && rooms.length === 0)" with "rooms.length === 0 && (isLoading ||
searchStatus === 'searching')" so the intention is clearer and the expression is
shorter.
- fetchRecentSearches에 isSuccess 확인 추가: API 실패 응답 시 response.data 접근으로 발생하는 TypeError 방지 - fetchRecentSearches에 catch 블록 추가: 네트워크 오류 등 예외가 unhandled rejection으로 전파되지 않도록 처리 - UserSearchResult의 isEmpty 조건 내 loading 분기 제거: 부모에서 loading=true일 때 LoadingMessage가 렌더링되므로 해당 경로는 도달 불가능한 데드 코드
ljh130334
left a comment
There was a problem hiding this comment.
수고하셨습니다! 검색 플로우 전반에 걸쳐서 자잘한 버거들 한 번에 정리해주셔서 좋았습니다!! 👍🏻 💯
| opacity: ${({ isRefetching }) => (isRefetching ? 0.45 : 1)}; | ||
| transition: opacity 0.15s ease; |
There was a problem hiding this comment.
오호 opacity로 전환되는 패턴 너무 좋은 것 같아요! 👍🏻
|
boolean 조합을 Union 상태로 정리해서 상태 전이 모호성을 없앤 점이 좋았습니다! |
#️⃣연관된 이슈
개요
모임 검색, 사용자 검색, 피드 페이지에서 발생하던 UX 버그들을 수정합니다.
작업 내용
모임 검색 (GroupSearch / GroupSearchResult)
keepPrevious옵션 도입으로 리스트 초기화 타이밍 조정사용자 검색 (UserSearch / UserSearchResult)
isEmpty조건에type !== 'searching'가드 추가피드 페이지 (MyFeedPage / OtherFeedPage)
LoadingScreen+ 헤더만 렌더링최근 검색어 (RecentSearchTabs)
isLoadingprop 추가 → 로딩 중 "불러오고 있습니다" 표시영향 범위
GroupSearch.tsx,GroupSearchResult.tsx,GroupSearchResult.styled.tsRecentSearchTabs.tsxMyFeedPage.tsx,MyFeedPage.styled.ts,OtherFeedPage.tsxUserSearch.tsx,UserSearch.styled.ts,UserSearchResult.tsxhttps://github.com/THIP-TextHip/THIP-Web/wiki/%EA%B2%80%EC%83%89-UX-%EA%B0%9C%EC%84%A0
Summary by CodeRabbit
새로운 기능
개선 사항