Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Caution Review failedThe pull request is closed. Walkthrough이번 변경에서는 사용자 검색 기능이 새롭게 도입되고, 사용자 프로필 관련 타입과 목업 데이터가 분리 관리됩니다. 여러 컴포넌트에서 라우팅 경로가 일관되게 수정되었으며, 검색 바와 사용자 프로필 아이템의 UI 및 제어 흐름이 개선되었습니다. 또한, 검색 결과 및 최근 검색어 관리 기능이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant FeedPage
participant MainHeader
participant Router
participant UserSearch
User->>FeedPage: 페이지 접속
FeedPage->>MainHeader: 렌더링
User->>MainHeader: 검색 버튼 클릭
MainHeader->>FeedPage: leftButtonClick 호출
FeedPage->>Router: /feed/search로 이동
Router->>UserSearch: UserSearch 컴포넌트 렌더링
User->>UserSearch: 검색어 입력 및 검색
UserSearch->>UserSearch: 최근 검색어 관리 및 결과 표시
Estimated code review effort3 (30–60분) Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (4)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
src/pages/feed/FollowerListPage.tsx (1)
24-30: 타입 캐스팅 개선을 제안합니다.type을
as UserProfileType로 캐스팅하고 있는데, 이미 useParams에서 타입이 지정되어 있으므로 더 안전한 방법을 고려해보세요.다음과 같이 개선할 수 있습니다:
- type={type as UserProfileType} + type={type!}또는 타입 가드를 사용하여 더 안전하게 처리할 수 있습니다:
+ if (!type) return null; // 또는 적절한 에러 처리 return ( <Wrapper> {/* ... */} {mockUserList.map((user, index) => ( <UserProfileItem key={index} {...user} - type={type as UserProfileType} + type={type} isLast={index === mockUserList.length - 1} /> ))}src/data/userData.ts (1)
3-38: mock 데이터 중앙화가 잘 구현되었습니다.사용자 데이터를 중앙화하여 여러 컴포넌트에서 재사용할 수 있도록 한 것이 좋은 접근입니다. 데이터 구조도 일관성 있게 구성되어 있습니다.
다음과 같은 개선사항을 고려해보세요:
- 환경별 이미지 URL 관리: placeholder 이미지 URL을 상수로 분리
- 데이터 일관성: 모든 사용자에게 followerCount가 있는지 확인
+const PLACEHOLDER_IMAGE = 'https://placehold.co/36x36'; + export const mockUserList: UserProfileItemProps[] = [ { - profileImgUrl: 'https://placehold.co/36x36', + profileImgUrl: PLACEHOLDER_IMAGE, userName: 'ThipOther', // ... }, // ... ];src/pages/feed/UserSearchResult.tsx (2)
20-20: 빈 JSX Fragment를 개선하세요.빈 JSX Fragment
<></>는 불필요합니다.- {type === 'searching' ? <></> : <ResultHeader>전체 {searchedUserList.length}</ResultHeader>} + {type === 'searched' && <ResultHeader>전체 {searchedUserList.length}</ResultHeader>}
23-23: 빈 컨테이너에 의미있는 내용을 추가하세요.EmptyWrapper가 완전히 비어있습니다. 사용자에게 검색 결과가 없음을 알리는 메시지나 아이콘을 추가하는 것이 좋겠습니다.
- <EmptyWrapper></EmptyWrapper> + <EmptyWrapper> + <EmptyMessage>검색 결과가 없습니다.</EmptyMessage> + </EmptyWrapper>src/pages/feed/UserSearch.tsx (2)
19-25: 하드코딩된 초기 검색어를 개선하세요.초기 최근 검색어가 하드코딩되어 있습니다. localStorage나 다른 영속 저장소를 사용하는 것을 고려해보세요.
- const [recentSearches, setRecentSearches] = useState<string[]>([ - '닉네임', - '작가', - '하위', - 'Thip', - '책벌레', - ]); + const [recentSearches, setRecentSearches] = useState<string[]>(() => { + const saved = localStorage.getItem('recentSearches'); + return saved ? JSON.parse(saved) : []; + });
37-40: 최근 검색어 관리 로직을 localStorage와 연동하세요.최근 검색어를 업데이트할 때 localStorage에도 저장하면 사용자 경험이 개선됩니다.
setRecentSearches(prev => { const filtered = prev.filter(t => t !== term); - return [term, ...filtered].slice(0, 5); + const newSearches = [term, ...filtered].slice(0, 5); + localStorage.setItem('recentSearches', JSON.stringify(newSearches)); + return newSearches; });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/components/feed/FollowList.tsx(1 hunks)src/components/feed/MyFollower.tsx(1 hunks)src/components/feed/UserProfileItem.tsx(3 hunks)src/components/search/SearchBar.tsx(2 hunks)src/data/userData.ts(1 hunks)src/pages/feed/Feed.tsx(2 hunks)src/pages/feed/FollowerListPage.tsx(2 hunks)src/pages/feed/UserSearch.tsx(1 hunks)src/pages/feed/UserSearchResult.tsx(1 hunks)src/pages/index.tsx(2 hunks)src/types/user.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/pages/feed/UserSearch.tsx (4)
src/components/common/Wrapper.tsx (1)
Wrapper(3-14)src/pages/feed/UserSearchResult.tsx (1)
UserSearchResult(11-37)src/data/userData.ts (1)
mockUserList(3-38)src/styles/global/global.ts (1)
colors(4-53)
src/pages/feed/FollowerListPage.tsx (2)
src/types/user.ts (1)
UserProfileType(1-1)src/data/userData.ts (1)
mockUserList(3-38)
src/data/userData.ts (1)
src/types/user.ts (1)
UserProfileItemProps(3-13)
src/pages/feed/UserSearchResult.tsx (2)
src/types/user.ts (1)
UserProfileItemProps(3-13)src/styles/global/global.ts (2)
typography(56-76)colors(4-53)
src/components/feed/UserProfileItem.tsx (1)
src/types/user.ts (1)
UserProfileItemProps(3-13)
🔇 Additional comments (21)
src/types/user.ts (2)
1-1: 타입 정의가 명확하고 적절합니다.
UserProfileType이 'followlist'와 'followerlist'로 구분된 것은 PR 목표에서 언급된 컴포넌트 재사용성 개선과 일치합니다.
3-13: 인터페이스 설계가 잘 구성되어 있습니다.사용자 프로필 아이템에 필요한 속성들이 논리적으로 구성되어 있고, 선택적 필드들의 구분도 적절합니다.
type필드를 통해 조건부 렌더링을 제어할 수 있어 컴포넌트 재사용성이 향상됩니다.src/components/feed/MyFollower.tsx (1)
73-73: 라우팅 경로 업데이트가 적절합니다.'/follow/followerlist'로 변경된 경로는 다른 컴포넌트들과 일관된 라우팅 구조를 유지하며, PR 목표의 전체적인 네비게이션 개선과 일치합니다.
src/components/feed/FollowList.tsx (1)
35-35: 일관된 라우팅 구조 적용이 적절합니다.'/follow/followlist'로 변경된 경로는 MyFollower 컴포넌트의 변경사항과 일치하며, 전체적인 라우팅 일관성을 유지합니다.
src/components/search/SearchBar.tsx (2)
56-56: 패딩 축소로 더 컴팩트한 디자인 적용.패딩을
16px 20px에서8px 12px로 줄여 검색바가 더 효율적인 공간 활용을 할 수 있게 되었습니다.
78-78: 텍스트 커서 색상 개선으로 UX 향상.
caret-color를 네온그린으로 설정하여 입력 시 커서의 가시성이 향상되고 브랜드 컬러와 일치합니다.src/pages/index.tsx (2)
23-23: UserSearch 컴포넌트 import가 적절합니다.새로운 사용자 검색 기능을 위한 컴포넌트 import가 올바르게 추가되었습니다.
39-39: 사용자 검색 라우트 추가가 적절합니다."feed/search" 경로는 기존 feed 관련 라우트들과 논리적으로 일치하며, PR 목표인 사용자 검색 화면 구현을 지원합니다.
src/pages/feed/FollowerListPage.tsx (2)
6-7: 데이터 중앙화와 타입 안전성 개선이 좋습니다.mock 데이터를 외부 모듈로 분리하고 적절한 타입을 import한 것은 코드 재사용성과 유지보수성을 높이는 좋은 접근입니다.
11-11: 타입 안전성을 위한 명시적 타입 지정이 적절합니다.useParams에 UserProfileType을 명시적으로 지정하여 타입 안전성을 확보한 것이 좋습니다.
src/pages/feed/Feed.tsx (3)
10-10: 적절한 네비게이션 훅 import입니다.사용자 검색 기능을 위한 useNavigate 훅 추가가 적절합니다.
24-26: 간결하고 명확한 네비게이션 핸들러입니다.검색 버튼 클릭 시
/feed/search라우트로 이동하는 핸들러가 간결하고 명확하게 구현되었습니다.
34-34: MainHeader 컴포넌트 통합이 깔끔합니다.leftButtonClick prop을 통해 검색 기능을 연결한 것이 컴포넌트 재사용성 측면에서 좋은 접근입니다.
src/data/userData.ts (1)
1-2: 적절한 타입 import입니다.UserProfileItemProps 타입을 import하여 타입 안전성을 확보한 것이 좋습니다.
src/pages/feed/UserSearchResult.tsx (1)
6-9: 타입 정의가 명확합니다.UserSearchResultProps 인터페이스가 명확하게 정의되어 있어 컴포넌트의 사용법을 이해하기 쉽습니다.
src/pages/feed/UserSearch.tsx (2)
57-62: useEffect 로직이 적절합니다.searchTerm이 비어있을 때 상태를 초기화하는 로직이 잘 구현되어 있습니다.
125-133: 고정 검색바 스타일링이 적절합니다.SearchBarContainer의 fixed positioning과 스타일링이 적절하게 구현되어 있습니다.
src/components/feed/UserProfileItem.tsx (4)
1-1: import 변경사항이 리팩토링 목표와 잘 일치합니다.
useLocation제거와 중앙화된 타입 정의 사용이 적절합니다. 경로 기반 조건부 렌더링에서 prop 기반 로직으로의 전환을 잘 지원합니다.Also applies to: 5-5
16-16: type prop 추가가 컴포넌트 재사용성을 향상시킵니다.경로 기반 조건부 로직을 prop 기반으로 전환하여 컴포넌트의 유연성과 테스트 가능성이 개선되었습니다. 기본값 설정으로 하위 호환성도 유지됩니다.
48-58: 팔로워 리스트 버튼의 클릭 기능이 제거되었습니다.조건부 렌더링 로직은 개선되었지만, 팔로워 리스트 버튼에서
handleFollowerListClick함수와onClick핸들러가 제거되어 클릭 기능이 없어졌습니다. 이것이 의도된 변경인지 확인해주세요.만약 클릭 기능이 필요하다면 다음과 같이 복원할 수 있습니다:
+ const handleFollowerListClick = (e: React.MouseEvent) => { + e.stopPropagation(); + navigate(`/follow/${userId}/follower`); + }; {type === 'followerlist' && ( - <div className="followlistbutton"> + <div className="followlistbutton" onClick={handleFollowerListClick}> <div>{followerCount}명이 띱하는 중</div> <img src={rightArrow} /> </div> )}
22-22: 라우트 설정이 올바르게 구성되었습니다
src/pages/index.tsx43번째 줄에서<Route path="otherfeed/:userId" element={<OtherFeedPage />} />로otherfeed경로가 정의되어 있습니다.UserProfileItem,FollowList,PostHeader등 해당 경로로의navigate호출도 모두 일치합니다.위 내용을 확인했으므로 변경을 승인합니다.
| const handleSearch = (term: string) => { | ||
| if (!term.trim()) return; | ||
| setIsSearching(true); | ||
| setIsSearched(true); | ||
| setRecentSearches(prev => { | ||
| const filtered = prev.filter(t => t !== term); | ||
| return [term, ...filtered].slice(0, 5); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
검색 로직에 실제 필터링을 구현하세요.
현재 검색 기능이 mock 데이터를 그대로 표시하고 있습니다. 실제 검색어에 따른 필터링 로직이 필요합니다.
검색 결과를 필터링하는 로직을 추가하세요:
+ const getFilteredUsers = (term: string) => {
+ if (!term.trim()) return [];
+ return mockUserList.filter(user =>
+ user.userName.toLowerCase().includes(term.toLowerCase()) ||
+ user.userTitle.toLowerCase().includes(term.toLowerCase())
+ );
+ };
const handleSearch = (term: string) => {
if (!term.trim()) return;
+ const filteredUsers = getFilteredUsers(term);
setIsSearching(true);
setIsSearched(true);
setRecentSearches(prev => {
const filtered = prev.filter(t => t !== term);
return [term, ...filtered].slice(0, 5);
});
};Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/feed/UserSearch.tsx around lines 33 to 41, the handleSearch
function currently updates recent searches but does not filter the displayed
data based on the search term. Implement filtering logic that uses the search
term to filter the actual data source or mock data before updating the displayed
search results. Ensure the filtered results are set in the component state to
reflect the search query accurately.
| <> | ||
| ( | ||
| {isSearched ? ( | ||
| <UserSearchResult | ||
| type={'searched'} | ||
| searchedUserList={mockUserList} | ||
| ></UserSearchResult> | ||
| ) : ( | ||
| <UserSearchResult | ||
| type={'searching'} | ||
| searchedUserList={mockUserList} | ||
| ></UserSearchResult> | ||
| )} | ||
| ) | ||
| </> |
There was a problem hiding this comment.
JSX 문법 오류를 수정하세요.
조건부 렌더링에서 불필요한 괄호가 있어 문법 오류를 일으킬 수 있습니다.
{isSearching ? (
- <>
- (
+ <>
{isSearched ? (
<UserSearchResult
type={'searched'}
- searchedUserList={mockUserList}
- ></UserSearchResult>
+ searchedUserList={getFilteredUsers(searchTerm)}
+ />
) : (
<UserSearchResult
type={'searching'}
- searchedUserList={mockUserList}
- ></UserSearchResult>
+ searchedUserList={[]}
+ />
)}
- )
- </>
+ </>
) : (Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/feed/UserSearch.tsx between lines 82 and 96, remove the unnecessary
parentheses surrounding the JSX conditional rendering block, as these extra
parentheses cause a JSX syntax error. Ensure the conditional rendering is
directly enclosed within a fragment or a single parent element without
extraneous characters.
| const isEmptySearchedUserList = () => { | ||
| if (searchedUserList.length === 0) return true; | ||
| else return false; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
불필요하게 복잡한 함수를 단순화하세요.
isEmptySearchedUserList 함수가 과도하게 복잡합니다.
다음과 같이 단순화할 수 있습니다:
- const isEmptySearchedUserList = () => {
- if (searchedUserList.length === 0) return true;
- else return false;
- };
+ const isEmptySearchedUserList = searchedUserList.length === 0;또는 인라인으로 직접 사용하는 것이 더 간단합니다:
- {isEmptySearchedUserList() ? (
+ {searchedUserList.length === 0 ? (📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const isEmptySearchedUserList = () => { | |
| if (searchedUserList.length === 0) return true; | |
| else return false; | |
| }; | |
| const isEmptySearchedUserList = searchedUserList.length === 0; |
🤖 Prompt for AI Agents
In src/pages/feed/UserSearchResult.tsx around lines 12 to 15, the function
isEmptySearchedUserList is unnecessarily complex by using an if-else statement
to return a boolean. Simplify it by directly returning the boolean expression
searchedUserList.length === 0 without the if-else, or consider inlining this
expression where the function is used for cleaner and more concise code.
| {isEmptySearchedUserList() ? ( | ||
| <EmptyWrapper></EmptyWrapper> | ||
| ) : ( | ||
| searchedUserList.map((user, index) => ( | ||
| <UserProfileItem | ||
| key={user.userId} | ||
| {...user} | ||
| type="followerlist" | ||
| isLast={index === searchedUserList.length - 1} | ||
| /> | ||
| )) | ||
| )} | ||
| </List> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
하드코딩된 type prop을 동적으로 변경하세요.
type prop이 "followerlist"로 하드코딩되어 있습니다. 이는 컴포넌트의 재사용성을 제한합니다.
UserSearchResult 컴포넌트가 받는 type을 UserProfileItem에 전달하거나, 별도의 prop으로 받도록 개선하세요:
interface UserSearchResultProps {
type: 'searching' | 'searched';
searchedUserList: UserProfileItemProps[];
+ profileType?: UserProfileType;
}
-export function UserSearchResult({ type, searchedUserList }: UserSearchResultProps) {
+export function UserSearchResult({ type, searchedUserList, profileType = 'followerlist' }: UserSearchResultProps) {
// ...
<UserProfileItem
key={user.userId}
{...user}
- type="followerlist"
+ type={profileType}
isLast={index === searchedUserList.length - 1}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| {isEmptySearchedUserList() ? ( | |
| <EmptyWrapper></EmptyWrapper> | |
| ) : ( | |
| searchedUserList.map((user, index) => ( | |
| <UserProfileItem | |
| key={user.userId} | |
| {...user} | |
| type="followerlist" | |
| isLast={index === searchedUserList.length - 1} | |
| /> | |
| )) | |
| )} | |
| </List> | |
| interface UserSearchResultProps { | |
| type: 'searching' | 'searched'; | |
| searchedUserList: UserProfileItemProps[]; | |
| profileType?: UserProfileType; | |
| } | |
| export function UserSearchResult({ | |
| type, | |
| searchedUserList, | |
| profileType = 'followerlist', | |
| }: UserSearchResultProps) { | |
| // ... | |
| return ( | |
| <List> | |
| {isEmptySearchedUserList() ? ( | |
| <EmptyWrapper></EmptyWrapper> | |
| ) : ( | |
| searchedUserList.map((user, index) => ( | |
| <UserProfileItem | |
| key={user.userId} | |
| {...user} | |
| type={profileType} | |
| isLast={index === searchedUserList.length - 1} | |
| /> | |
| )) | |
| )} | |
| </List> | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/pages/feed/UserSearchResult.tsx around lines 22 to 34, the type prop
passed to UserProfileItem is hardcoded as "followerlist", limiting component
reusability. Modify UserSearchResult to accept a type prop and pass it down to
UserProfileItem instead of the hardcoded string. Update the component's props
definition to include type and use that prop when rendering UserProfileItem.
…to feature/feed
#️⃣연관된 이슈
#23 [UI] 피드페이지 - 사용자 검색
📝작업 내용
💬리뷰 요구사항
해당 화면은 호준님이 이전에 구현하신 BookSearch 페이지를 공통컴포넌트화 하는건 무리라고 판단해서 필요한 컴포넌트와 로직만 가져와 작성해서 큰 어려움은 없었습니다. 이전에 조건부 처리를 잘 해놓으셔서 크게 문제사항 없이 구현했습니다.
추가로, 기존에 제가 구현한 UserProfileItem.tsx 의 경우에 pathname을 가져와 컴포넌트의 형태를 다르게 구현했었는데 props로 type을 넘겨서 컴포넌트를 원하는 형태로 재사용하게끔 수정했습니다. 필요할 경우 참고하시면 되겠습니다~
Summary by CodeRabbit
신규 기능
버그 수정
UI/스타일
기타