[refactor] 검색창 입력시 리렌더링되는 문제를 해결한다#714
Conversation
- React Context에서 Zustand로 마이그레이션 - 검색창 입력 시 헤더와 카테고리 리스트 리렌더링 방지 - 개별 상태 구독을 통한 컴포넌트 최적화
- MainPage: keyword, isSearching만 구독하도록 변경 - SearchBox: useSearchInput hook 사용으로 검색 입력 관련 상태만 구독 - CategoryButtonList: getState() 직접 사용으로 상태 구독 제거 - useHeaderService: getState() 직접 사용으로 상태 구독 제거 - 검색창 입력 시 Header, CategoryButtonList 리렌더링 방지
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
상태관리 마이그레이션: Context → Zustandfrontend/src/context/SearchContext.tsx |
SearchContext 모듈 전체 제거(Provider, useSearch 훅 삭제). |
Zustand 스토어 추가frontend/src/store/useSearchStore.ts |
zustand 기반 검색 스토어 추가: 상태(keyword, inputValue, isSearching), 액션(setters, resetSearch), 셀렉터 훅(useSearchKeyword, useSearchIsSearching, useSearchInput) 정의. |
컴포넌트: 스토어 적용frontend/src/pages/MainPage/MainPage.tsx, frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx, frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx, frontend/src/services/header/useHeaderService.ts |
Context 훅 사용 제거 후 스토어 훅/액션으로 대체. 카테고리 클릭/홈 이동 시 resetSearch 사용. MainPage는 셀렉터 훅으로 keyword/isSearching 구독. SearchBox는 useSearchInput 사용. |
루트/스토리: Provider 제거frontend/src/App.tsx, frontend/src/components/common/Header/Header.stories.tsx |
SearchProvider 및 관련 데코레이터 제거. 트리에서 QueryClientProvider → CategoryProvider → BrowserRouter 구성 유지. |
의존성frontend/package.json |
zustand@^5.0.8 추가. JSON 수정(콤마 정리). |
Sequence Diagram(s)
sequenceDiagram
actor User
participant Header
participant SearchBox
participant MainPage
participant Router as BrowserRouter
participant Store as useSearchStore (Zustand)
rect rgba(220,235,255,0.4)
note over SearchBox,Store: 검색 실행 흐름 (변경: Context → Store)
User->>SearchBox: 키워드 입력/검색 제출
SearchBox->>Store: setInputValue / setKeyword / setIsSearching(true)
SearchBox->>Router: navigate(/)
Router-->>MainPage: 라우트 렌더
MainPage->>Store: useSearchKeyword / useSearchIsSearching (select)
MainPage-->>User: 검색 결과 렌더
end
rect rgba(225,255,225,0.4)
note over Header,Store: 홈 이동 시 초기화
User->>Header: 로고 클릭
Header->>Router: navigate(/)
Header->>Store: resetSearch()
end
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 헤더 메모이제이션 MOA-208 | ❓ | 메모이제이션 적용 코드가 확인되지 않습니다. |
| 검색창 메모이제이션 MOA-208 | ❓ | 메모이제이션 적용 코드가 확인되지 않습니다. |
| 리렌더링 성능 측정 MOA-208 | ❌ | 측정 도구/지표/로그가 추가되지 않았습니다. |
Assessment against linked issues: Out-of-scope changes
(해당 없음)
Possibly related issues
- [feature] MOA-208 메인페이지 검색창 입력시 리렌더링되는 문제를 해결한다 #712: SearchContext를 Zustand로 대체하여 메인/검색 박스의 불필요한 리렌더를 줄이는 변경과 직접적으로 연관됩니다.
Possibly related PRs
- [feature] 지원자를 검색할 수 있다 #650: SearchField/검색 컴포넌트를 도입하며 SearchContext 의존을 추가했던 변경과 같은 부분을 이번 PR이 Zustand로 치환.
- [feature] 검색 시 전체동아리 기준으로 검색된다 #616: SearchContext에 isSearching을 확장했던 변경과 동일 책임 영역으로, 이번 PR이 해당 상태를 스토어로 이전.
- [feature] Common 컴포넌트 스토리 파일 적용 #225: Header 스토리에서 SearchProvider 데코레이터를 추가했던 변경과 반대로, 이번 PR이 이를 제거.
Suggested reviewers
- lepitaaar
- oesnuj
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#712-optimize-rerender-main-page-MOA-208
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignoreor@coderabbit ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
frontend/src/context/useSearchStore.ts (1)
56-63: 불필요한 다중 구독을 줄이기 위한 미세 개선현재 4번(useSearchStore) 호출로 각각 구독합니다. setter는 안정 참조이므로 문제는 크지 않지만, 하나의 셀렉터로 묶으면 구독 수를 줄일 수 있습니다.
선호안(버전 호환되는 얕은 비교 유틸 사용 시):
-export const useSearchInput = () => { - const inputValue = useSearchStore((state) => state.inputValue); - const setInputValue = useSearchStore((state) => state.setInputValue); - const setKeyword = useSearchStore((state) => state.setKeyword); - const setIsSearching = useSearchStore((state) => state.setIsSearching); - - return { inputValue, setInputValue, setKeyword, setIsSearching }; -}; +// shallow 비교 유틸이 프로젝트에 이미 있다면 적용 고려 +export const useSearchInput = () => + useSearchStore((s) => ({ + inputValue: s.inputValue, + setInputValue: s.setInputValue, + setKeyword: s.setKeyword, + setIsSearching: s.setIsSearching, + }));frontend/src/components/common/Header/Header.stories.tsx (1)
15-15: Storybook 데코레이터 단순화 LGTMSearchContext 의존 제거와 일관됩니다. 필요 시 MemoryRouter로도 대체 가능하지만 현재 구성으로 충분합니다.
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (1)
40-47: 검색 상태 리셋을 한 번에 처리하고 순서를 조정해 불필요한 중간 fetch를 방지하세요현재는 setKeyword → setInputValue → setIsSearching(false) → setSelectedCategory 순으로 호출됩니다. MainPage의
searchCategory = isSearching ? 'all' : selectedCategory로직상 isSearching을 먼저 false로 바꾸면 직전 selectedCategory로 한 번, 이후 새로운 selectedCategory로 또 한 번 요청이 발생할 수 있습니다. 또한 store를 3회 갱신해 구독자에 불필요한 notify가 누적될 수 있습니다.
- 권장: (1) 먼저
setSelectedCategory를 호출해서 선택 카테고리를 확정하고, (2)useSearchStore.setState로 검색 상태를 한 번에 리셋하세요.적용 diff:
- const { setKeyword, setInputValue, setIsSearching } = - useSearchStore.getState(); - setKeyword(''); - setInputValue(''); - setIsSearching(false); - - setSelectedCategory(category.id); + // 1) 카테고리를 먼저 변경하여 중간 불필요한 fetch를 방지 + setSelectedCategory(category.id); + // 2) 검색 상태 리셋을 단일 setState로 묶어 구독자 알림/리렌더를 최소화 + useSearchStore.setState({ keyword: '', inputValue: '', isSearching: false });추가로, 재사용을 위해 store에 액션을 두는 것도 좋습니다:
// frontend/src/context/useSearchStore.ts // store 생성부에 추가 resetSearch: () => set({ keyword: '', inputValue: '', isSearching: false });그 후 이 파일에서는:
useSearchStore.getState().resetSearch();처럼 호출하면 됩니다.
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (1)
8-9: 키워드/검색중 상태를 단일 업데이트로 묶어 리렌더/재요청 최소화
setKeyword(inputValue)와setIsSearching(true)가 분리 호출됩니다. 두 값은 논리적으로 한 동작(검색 실행)에 속하므로 하나의 setState로 묶는 편이 좋습니다. 또한setSelectedCategory('all')를 먼저 호출하면 검색 파라미터가 안정화된 뒤 단일 요청으로 이어질 가능성이 큽니다.옵션 A: setState 직접 사용
- setKeyword(inputValue); - setSelectedCategory('all'); - setIsSearching(true); + setSelectedCategory('all'); + useSearchStore.setState({ keyword: inputValue, isSearching: true });추가 import:
- import { useSearchInput } from '@/context/useSearchStore'; + import { useSearchInput, useSearchStore } from '@/context/useSearchStore';옵션 B: store에 액션 추가 (권장)
// useSearchStore.ts startSearch: (keyword: string) => set({ keyword, isSearching: true });이 파일:
setSelectedCategory('all'); useSearchInput().startSearch(inputValue);두 옵션 모두 렌더/요청 횟수를 줄이는 데 도움이 됩니다. 네트워크 탭에서 검색 실행 시 요청이 1회로 유지되는지 확인 부탁드립니다.
Also applies to: 24-27
frontend/src/pages/MainPage/MainPage.tsx (1)
27-32: 'all'/'OPEN'과 같은 매직 문자열을 상수로 통일여러 파일에서 동일 리터럴을 반복하면 오타·불일치가 생기기 쉽습니다. 예:
ALL_CATEGORY = 'all',RECRUITMENT_OPEN = 'OPEN'. 공용 constants로 분리해 재사용하면 의미가 명확해지고 변경에도 강해집니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (9)
frontend/package.json(1 hunks)frontend/src/App.tsx(1 hunks)frontend/src/components/common/Header/Header.stories.tsx(1 hunks)frontend/src/context/SearchContext.tsx(0 hunks)frontend/src/context/useSearchStore.ts(1 hunks)frontend/src/pages/MainPage/MainPage.tsx(2 hunks)frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx(2 hunks)frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx(1 hunks)frontend/src/services/header/useHeaderService.ts(2 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/context/SearchContext.tsx
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/components/common/Header/Header.stories.tsxfrontend/src/App.tsxfrontend/src/services/header/useHeaderService.tsfrontend/src/pages/MainPage/components/SearchBox/SearchBox.tsxfrontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsxfrontend/src/context/useSearchStore.tsfrontend/src/pages/MainPage/MainPage.tsx
frontend/**/*.tsx
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Separate significantly different conditional UI/logic into distinct components.
Colocate simple, localized logic or use inline definitions to reduce context switching.
Choose field-level or form-level cohesion based on form requirements.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/components/common/Header/Header.stories.tsxfrontend/src/App.tsxfrontend/src/pages/MainPage/components/SearchBox/SearchBox.tsxfrontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsxfrontend/src/pages/MainPage/MainPage.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-19T05:09:10.702Z
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.702Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Applied to files:
frontend/src/pages/MainPage/MainPage.tsx
🧬 Code graph analysis (5)
frontend/src/App.tsx (2)
frontend/src/context/CategoryContext.tsx (1)
CategoryProvider(20-52)frontend/src/context/AdminClubContext.tsx (1)
AdminClubProvider(15-28)
frontend/src/services/header/useHeaderService.ts (1)
frontend/src/context/useSearchStore.ts (1)
useSearchStore(13-22)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (1)
frontend/src/context/useSearchStore.ts (1)
useSearchInput(56-63)
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (1)
frontend/src/context/useSearchStore.ts (1)
useSearchStore(13-22)
frontend/src/pages/MainPage/MainPage.tsx (1)
frontend/src/context/useSearchStore.ts (2)
useSearchKeyword(44-48)useSearchIsSearching(50-54)
🔇 Additional comments (5)
frontend/package.json (1)
41-43: Zustand 의존성 추가가 올바르게 반영되었습니다 — 락파일 재생성 후 CI 캐시/빌드만 확인하세요.frontend/src/App.tsx (1)
28-94: SearchProvider 제거 및 라우팅 트리 정리 LGTMZustand 전환에 맞춘 Provider 제거가 적절하며, Category/Admin 컨텍스트 경계도 명확합니다.
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx (1)
10-10: LGTM — Zustand로 전환하며 불필요한 전역 상태 구독 제거Context 구독 없이 store 접근으로 해당 컴포넌트의 리렌더를 막는 방향이 맞습니다.
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (1)
1-1: LGTM — useSearchInput 훅으로 교체 적절입력값만 구독하도록 분리되어 리렌더 범위를 줄이는 방향이 맞습니다.
frontend/src/pages/MainPage/MainPage.tsx (1)
2-5: LGTM — selector 단위 훅 분리로 구독 범위 축소
useSearchKeyword/useSearchIsSearching로 필요한 조각만 구독하는 구조가 본 PR의 목적(리렌더 최소화)에 부합합니다.
- keyword, inputValue, isSearching을 한 번에 초기화하는 resetSearch 함수 구현 - 카테고리 변경, 홈 이동 등에서 검색 상태 리셋 시 편의성 향상
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (6)
frontend/src/store/useSearchStore.ts (4)
12-24: 초기값 중복 제거 및 리셋 안정성: INITIAL_STATE 도입 제안초기값이 여러 군데에 중복 정의돼 있어(초기화와 resetSearch) 변경 시 누락 위험이 있습니다. 상수로 묶어 단일 소스 유지가 안전합니다.
interface SearchStore { keyword: string; setKeyword: (keyword: string) => void; inputValue: string; setInputValue: (value: string) => void; isSearching: boolean; setIsSearching: (isSearching: boolean) => void; resetSearch: () => void; } +// 초기 상태를 단일 소스로 관리 +const INITIAL_STATE: Pick<SearchStore, 'keyword' | 'inputValue' | 'isSearching'> = { + keyword: '', + inputValue: '', + isSearching: false, +}; + export const useSearchStore = create<SearchStore>()( subscribeWithSelector((set) => ({ - keyword: '', + ...INITIAL_STATE, - setKeyword: (keyword) => set({ keyword }), - inputValue: '', + setKeyword: (keyword) => set({ keyword }), setInputValue: (value) => set({ inputValue: value }), - isSearching: false, - setIsSearching: (isSearching) => set({ isSearching }), - resetSearch: () => set({ keyword: '', inputValue: '', isSearching: false }), + setIsSearching: (isSearching) => set({ isSearching }), + resetSearch: () => set(INITIAL_STATE), })), );
38-45: SearchBox 편의성 향상: resetSearch도 함께 노출SearchBox에서 입력 초기화 시 store 외부 접근(getState) 없이 처리할 수 있게 resetSearch를 함께 노출하면 사용성이 좋아집니다. 함수 레퍼런스는 안정적이라 리렌더 영향도 없습니다.
export const useSearchInput = () => { const inputValue = useSearchStore((state) => state.inputValue); const setInputValue = useSearchStore((state) => state.setInputValue); const setKeyword = useSearchStore((state) => state.setKeyword); const setIsSearching = useSearchStore((state) => state.setIsSearching); + const resetSearch = useSearchStore((state) => state.resetSearch); - return { inputValue, setInputValue, setKeyword, setIsSearching }; + return { inputValue, setInputValue, setKeyword, setIsSearching, resetSearch }; };
32-36: 네이밍 미세 개선 제안useSearchIsSearching 보다는 useSearchStatus 같은 이름이 읽기 자연스럽습니다. 강제사항은 아니니 참고만 부탁드립니다.
1-3: devtools 미들웨어 추가 및 subscribeWithSelector 사용 여부 검토
useSearchStore생성부에 devtools 미들웨어를 래핑하여 개발 환경에서만 활성화하세요.-import { subscribeWithSelector } from 'zustand/middleware'; +import { subscribeWithSelector, devtools } from 'zustand/middleware'; export const useSearchStore = create<SearchStore>()( - subscribeWithSelector((set) => ({ + subscribeWithSelector( + devtools((set) => ({ ...INITIAL_STATE, setKeyword: (keyword) => set({ keyword }), setInputValue: (value) => set({ inputValue: value }), setIsSearching: (isSearching) => set({ isSearching }), resetSearch: () => set(INITIAL_STATE), - })), + }), { name: 'search-store', enabled: process.env.NODE_ENV !== 'production' }) + ), );
- 프로젝트 전반에
subscribeWithSelector호출은 해당 파일 외에 존재하지 않으므로, selector 기반 구독이 실제로 필요 없다면 미들웨어 자체를 제거하는 것도 고려하세요.frontend/src/pages/MainPage/MainPage.tsx (2)
60-70: 중첩 삼항식은 IIFE/if-else로 풀어 가독성 향상가이드라인(중첩/복잡한 삼항 지양)에 따라 조건 분기를 풀면 읽기 쉬워집니다.
- {isLoading ? ( - <Spinner /> - ) : isEmpty ? ( - <Styled.EmptyResult> - 앗, 조건에 맞는 동아리가 없어요. - <br /> - 다른 키워드나 조건으로 다시 시도해보세요! - </Styled.EmptyResult> - ) : ( - <Styled.CardList>{clubList}</Styled.CardList> - )} + {(() => { + if (isLoading) return <Spinner />; + if (isEmpty) { + return ( + <Styled.EmptyResult> + 앗, 조건에 맞는 동아리가 없어요. + <br /> + 다른 키워드나 조건으로 다시 시도해보세요! + </Styled.EmptyResult> + ); + } + return <Styled.CardList>{clubList}</Styled.CardList>; + })()}
38-41: useMemo 의존성 정리(불필요 의존 제거)clubList는 clubs만으로 결정됩니다. hasData를 의존성에서 제거해 불필요 재계산을 줄일 수 있습니다.
- const clubList = useMemo(() => { - if (!hasData) return null; - return clubs.map((club: Club) => <ClubCard key={club.id} club={club} />); - }, [clubs, hasData]); + const clubList = useMemo(() => { + if (!clubs?.length) return null; + return clubs.map((club: Club) => <ClubCard key={club.id} club={club} />); + }, [clubs]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
frontend/src/pages/MainPage/MainPage.tsx(2 hunks)frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx(2 hunks)frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx(1 hunks)frontend/src/services/header/useHeaderService.ts(2 hunks)frontend/src/store/useSearchStore.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx
- frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx
- frontend/src/services/header/useHeaderService.ts
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/store/useSearchStore.tsfrontend/src/pages/MainPage/MainPage.tsx
frontend/**/*.tsx
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Separate significantly different conditional UI/logic into distinct components.
Colocate simple, localized logic or use inline definitions to reduce context switching.
Choose field-level or form-level cohesion based on form requirements.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/MainPage/MainPage.tsx
🧬 Code graph analysis (1)
frontend/src/pages/MainPage/MainPage.tsx (1)
frontend/src/store/useSearchStore.ts (2)
useSearchKeyword(26-30)useSearchIsSearching(32-36)
🔇 Additional comments (4)
frontend/src/store/useSearchStore.ts (1)
14-24: Zustand로의 전환 및 슬라이스 선택자 사용, 방향성 좋습니다검색 관련 리렌더링을 슬라이스 단위로 한정하는 접근이 명확하고, 메인 페이지의 불필요 리렌더링을 줄이는 데 효과적입니다.
frontend/src/pages/MainPage/MainPage.tsx (3)
24-26: 스토어 슬라이스 선택 사용으로 리렌더링 축소, 방향성 적절keyword와 isSearching만 구독해 메인 페이지 리렌더 범위를 최소화했습니다. 의도와 구현이 일치합니다.
28-29: 검색 중 카테고리 무시 정책 확인 필요isSearching일 때 searchCategory를 'all'로 강제하는 정책이 의도(검색 시 카테고리 필터 무시)와 일치하는지 PO/기획 확인 부탁드립니다. 기존 사용자 기대(카테고리+키워드 AND 검색)와 다를 수 있습니다.
30-35: 입력 타이핑 시 네트워크 트래픽 증가 여부 확인useGetCardList가 keyword 기준으로만 요청 키를 구성한다면(의도대로 제출 시점에만 setKeyword), 타이핑 중에는 재요청이 발생하지 않습니다. SearchBox에서 setKeyword 호출이 제출 시점에만 일어나는지 한 번만 점검 부탁드립니다.
lepitaaar
left a comment
There was a problem hiding this comment.
zustand를 이용한 전역상태관리 좋습니다. zustand 사용 이유도 상세히 작성해주셔서 좋네요!!
조금씩 context에서 zustand로 리팩토링 해보죠
frontend/src/pages/MainPage/components/CategoryButtonList/CategoryButtonList.tsx
Show resolved
Hide resolved
oesnuj
left a comment
There was a problem hiding this comment.
zustand 도입 좋습니다
이유와 함께 노션에 정리 해주신 부분이 좋았습니다 ㅎㅎ
좋은 영향 받아서 저도 애니메이션 관련 학습 정리하고 있습니다~~
#️⃣연관된 이슈
📝작업 내용
문제상황
2025-09-01.02.53.27.mov
메인페이지 검색어를 입력할 시 해당 페이지의 모든 컴포넌트가 리렌더링되는 문제가 있었습니다.
Context api의 고질적인 리렌더링 문제 때문에 발생하였습니다. 참고자료
구독중인 context 상태가 바뀔 때마다, 해당 context를 구독 중인 하위 컴포넌트들이 리렌더링 된다는 것이었어요.
해결방법
fe자료실-zustand사용이유
적용 이후
2025-09-01.02.52.20.mov
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit