Skip to content

refactor(ui): SearchField 공통 스타일 분리 및 종속성 제거#644

Closed
seongwon030 wants to merge 1 commit intodevelop-fefrom
refactor/#643-common-searchField-MOA-152
Closed

refactor(ui): SearchField 공통 스타일 분리 및 종속성 제거#644
seongwon030 wants to merge 1 commit intodevelop-fefrom
refactor/#643-common-searchField-MOA-152

Conversation

@seongwon030
Copy link
Member

@seongwon030 seongwon030 commented Aug 12, 2025

  • SearchField.styles.ts 도입으로 페이지 전용 스타일 의존 제거
  • SearchField가 공통 스타일을 직접 참조하도록 변경
  • 검색 입력 UI의 재사용성/일관성 향상

#️⃣연관된 이슈

ex) #이슈번호, #이슈번호

📝작업 내용

이번 PR에서 작업한 내용을 간략히 설명해주세요(이미지/동영상 첨부 가능)

중점적으로 리뷰받고 싶은 부분(선택)

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • 신기능
    • 재사용 가능한 검색 입력 필드 도입으로 접근성(ARIA 라벨)과 포커스/자동 블러 동작 개선.
  • 리팩터
    • 검색 상자를 메인 페이지 컴포넌트로 이동하여 구조 정리.
    • 헤더에서 검색 상자 경로를 신규 위치로 업데이트.
    • 기존 공용 검색 상자를 제거하고 신규 구성요소 기반으로 대체.
  • 기타
    • 사용자 관점의 검색 동작과 화면 표시에는 변화 없음.

- SearchField.styles.ts 도입으로 페이지 전용 스타일 의존 제거
- SearchField가 공통 스타일을 직접 참조하도록 변경
- 검색 입력 UI의 재사용성/일관성 향상
@vercel
Copy link

vercel bot commented Aug 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Project Deployment Preview Comments Updated (UTC)
moadong Ready Preview Comment Aug 12, 2025 1:43pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 12, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

공통 SearchBox를 제거하고, 메인 페이지 전용 SearchBox로 이동·구현했으며, 재사용 가능한 공통 SearchField 컴포넌트를 신규 추가했다. Header의 SearchBox import 경로를 메인 페이지 컴포넌트로 변경했다. 로직은 기존 컨텍스트 기반 검색 흐름을 유지한다.

Changes

Cohort / File(s) Change Summary
Header import 업데이트
frontend/src/components/common/Header/Header.tsx
SearchBox import 경로를 @/pages/MainPage/components/SearchBox/SearchBox로 변경. 사용 방식 동일.
구(舊) 공통 SearchBox 제거
frontend/src/components/common/SearchBox/SearchBox.tsx
기존 공통 SearchBox 컴포넌트 파일 삭제(기능: 컨텍스트 기반 검색, 라우팅, 믹스패널 트래킹 포함).
신규 공통 SearchField 추가
frontend/src/components/common/SearchField/SearchField.tsx
재사용 가능한 검색 입력/버튼 컴포넌트 추가. 제어형 입력, onSubmit, autoBlur, 접근성 ARIA 라벨 지원. 기본 props 제공.
메인 페이지 SearchBox 신규 구현
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx
SearchField를 사용해 검색 흐름 구성. 키워드 설정, 카테고리 초기화, 검색 상태 플래그, 홈 경로로의 네비게이션, 믹스패널 이벤트 트래킹 수행.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant SearchField as UI: SearchField
  participant SearchBox as MainPage SearchBox
  participant Router as React Router
  participant SearchCtx as SearchContext
  participant CategoryCtx as CategoryContext
  participant Mixpanel as Mixpanel

  User->>SearchField: 입력 및 제출
  SearchField->>SearchBox: onSubmit()
  SearchBox->>Router: 필요 시 "/"로 이동
  SearchBox->>SearchCtx: setKeyword(inputValue), setIsSearching(true)
  SearchBox->>CategoryCtx: setSelectedCategory('all')
  SearchBox->>Mixpanel: track("Search Executed", { value, path })
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Assessment against linked issues

Objective Addressed Explanation
공통 검색 필드 컴포넌트 도입 (MOA-152)
기존 공통 SearchBox 대체/이관 (MOA-152)
메인 페이지 검색 흐름 유지 및 연동 (MOA-152)
헤더에서 신규 구조 반영 (MOA-152)

Possibly related PRs

Suggested labels

🔨 Refactor, 💻 FE

Suggested reviewers

  • Zepelown
  • oesnuj
  • lepitaaar
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#643-common-searchField-MOA-152

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.

❤️ Share
🪧 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 @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in 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 ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
frontend/src/components/common/Header/Header.tsx (1)

59-76: 모바일 헤더에서 Admin 경로에서도 SearchBox가 항상 렌더링 — Provider 미존재 시 런타임 크래시 가능

useSearch/useCategory는 Provider 외부 사용 시 예외를 던집니다(참고: frontend/src/context/SearchContext.tsx, CategoryContext.tsx). DesktopHeader는 isAdminPage에서 SearchBox를 숨기지만, MobileHeader는 조건 없이 SearchBox를 렌더링합니다. Admin 경로(예: /admin)에서 모바일 화면일 때 Provider로 래핑되어 있지 않으면 크래시가 발생할 수 있습니다.

아래와 같이 MobileHeader에도 isAdminPage를 전달하고 조건부 렌더링으로 정렬해 주세요.

다음 diff를 적용하세요:

 interface MobileHeaderProps {
+  isAdminPage: boolean;
   handleHomeClick: (device: 'mobile' | 'desktop') => void;
   handleMenuClick: () => void;
 }

-const MobileHeader = ({
-  handleHomeClick,
-  handleMenuClick,
-}: MobileHeaderProps) => (
+const MobileHeader = ({
+  isAdminPage,
+  handleHomeClick,
+  handleMenuClick,
+}: MobileHeaderProps) => (
   <Styled.MobileHeaderContainer>
     <Styled.MobileHeaderWrapper>
       <Styled.MobileMainIcon>
         <img
           src={MobileMainIcon}
           alt='홈 버튼'
           onClick={() => handleHomeClick('mobile')}
         />
       </Styled.MobileMainIcon>
-      <SearchBox />
+      {!isAdminPage && <SearchBox />}
       <Styled.MobileMenu aria-label='메뉴 버튼'>
         <img src={MenuBar} alt='메뉴 버튼' onClick={handleMenuClick} />
       </Styled.MobileMenu>
     </Styled.MobileHeaderWrapper>
   </Styled.MobileHeaderContainer>
 );

그리고 Header에서 prop을 전달합니다:

   return isMobile ? (
     <>
       <MobileHeader
+        isAdminPage={isAdminPage}
         handleHomeClick={handleHomeClick}
         handleMenuClick={openMenu}
       />
♻️ Duplicate comments (1)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (1)

8-13: Provider 의존성 런타임 예외 가능성 재확인

useSearch/useCategory는 Provider 외부 사용 시 예외를 던집니다. Header.tsx에서 모바일 admin 경로 케이스를 방지하도록 수정 제안 드렸습니다(중복 코멘트).

아래 스크립트로 Provider/Header 배치를 대략적으로 확인할 수 있습니다. 결과에서 App/레이아웃 루트에 SearchProvider/CategoryProvider가 존재하고 Header가 그 하위에 있는지 확인해 주세요.

#!/bin/bash
# Search Provider 선언 및 사용 위치 확인
rg -n "SearchProvider|CategoryProvider" -A 3 -B 3

# Header 사용 위치 살펴보기
rg -n "<Header\b" -A 5 -B 5

# SearchBox가 Admin 라우트에서 렌더링될 수 있는지 탐색(모바일 레이아웃 등)
rg -n "path=['\"]?/admin" -A 10 -B 10
🧹 Nitpick comments (4)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (2)

20-30: 네비게이션 직후 window.location.pathname 사용은 타이밍 이슈 가능 — 이벤트 필드 명확화(from/to) 및 상수화 권장

navigate('/') 호출 직후 window.location.pathname을 읽으면 비동기 타이밍에 따라 이전 경로가 기록될 수 있습니다. 검색 발생 위치와 이동 목적지를 명확히 기록하도록 이벤트 속성을 from/to로 구분하고, 매직 스트링들을 상수로 추출하세요.

아래 diff를 참고하세요:

+// Constants
+const DEFAULT_CATEGORY = 'all';
+const SEARCH_EXECUTED_EVENT = 'Search Executed';
+
 const SearchBox = () => {
   const { setKeyword, inputValue, setInputValue, setIsSearching } = useSearch();
   const { setSelectedCategory } = useCategory();
   const trackEvent = useMixpanelTrack();
   const navigate = useNavigate();
   const location = useLocation();

   const redirectToHome = () => {
     if (location.pathname !== '/') {
       navigate('/');
     }
   };

-  const handleSearch = () => {
+  const handleSearch = () => {
+    const trimmed = inputValue.trim();
+    if (!trimmed) {
+      // 비어있는 검색어는 무시하거나 별도 이벤트로 로깅하는 정책을 적용할 수 있습니다.
+      return;
+    }
     redirectToHome();
-    setKeyword(inputValue);
-    setSelectedCategory('all');
+    setKeyword(trimmed);
+    setSelectedCategory(DEFAULT_CATEGORY);
     setIsSearching(true);

-    trackEvent('Search Executed', {
-      inputValue: inputValue,
-      page: window.location.pathname,
-    });
+    trackEvent(SEARCH_EXECUTED_EVENT, {
+      inputValue: trimmed,
+      from: location.pathname,
+      to: '/',
+    });
   };

정책적으로 빈 검색어 허용이 필요하다면 trimmed 체크 부분은 제거하셔도 됩니다.


23-24: 카테고리 기본값 'all' 매직 스트링 사용 — 도메인 상수로 추출하세요

'ALL'/'전체'와 같은 도메인 상수는 전역적으로 일관되어야 합니다. CategoryContext에서 사용하는 상수(예: DEFAULT_CATEGORY 또는 Category.All)가 있다면 그것을 사용하고, 없다면 본 파일 상단에 상수로 선언하세요. 상기 diff에 DEFAULT_CATEGORY 예시 포함했습니다.

CategoryContext에서 허용하는 값이 정확히 'all'인지 확인해 주세요. 필요 시 enum 또는 유니언 타입으로 한정하는 것을 권장합니다.

frontend/src/components/common/SearchField/SearchField.tsx (2)

35-41: 검색 입력 필드의 시맨틱과 모바일 UX 개선

type을 'search'로 변경하고 enterKeyHint/autoComplete를 추가하면 모바일 키보드와 브라우저 기본 동작이 더 적합해집니다.

-        type='text'
+        type='search'
+        enterKeyHint='search'
+        autoComplete='on'

필요 시 name='q' 등 검색 파라미터 명도 지정하면 폼 통합 시 재사용성이 좋아집니다.


44-49: 아이콘의 스크린리더 중복 낭독 방지

버튼 자체에 aria-label='검색'이 있으므로, 내부 아이콘 이미지는 장식용으로 처리하세요. alt를 빈 문자열로 하고 aria-hidden을 지정하면 접근성이 향상됩니다.

-      <Styled.SearchButton
+      <Styled.SearchButton
         type='submit'
         $isFocused={isFocused}
         aria-label='검색'
       >
-        <img src={searchButtonIcon} alt='Search Button' />
+        <img src={searchButtonIcon} alt='' aria-hidden='true' />
       </Styled.SearchButton>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25fbe15 and 5f4ab80.

📒 Files selected for processing (4)
  • frontend/src/components/common/Header/Header.tsx (1 hunks)
  • frontend/src/components/common/SearchBox/SearchBox.tsx (0 hunks)
  • frontend/src/components/common/SearchField/SearchField.tsx (1 hunks)
  • frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/components/common/SearchBox/SearchBox.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/pages/MainPage/components/SearchBox/SearchBox.tsx
  • frontend/src/components/common/Header/Header.tsx
  • frontend/src/components/common/SearchField/SearchField.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/components/SearchBox/SearchBox.tsx
  • frontend/src/components/common/Header/Header.tsx
  • frontend/src/components/common/SearchField/SearchField.tsx
🧬 Code Graph Analysis (1)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (2)
frontend/src/context/SearchContext.tsx (1)
  • useSearch (39-45)
frontend/src/context/CategoryContext.tsx (1)
  • useCategory (11-18)
🔇 Additional comments (2)
frontend/src/components/common/Header/Header.tsx (2)

3-3: 경로 변경 적합 — 페이지 전용 SearchBox로의 의존 전환 LGTM

Header가 공통 SearchBox에서 MainPage 전용 SearchBox로 경로를 전환했습니다. PR 목표(공통 스타일 분리 및 페이지 전용 의존 제거)와 부합합니다. 런타임 영향 없음.


106-113: isAdminPage 계산 로직과 MobileHeader/ DesktopHeader 간 표시 기준 일관성 확인 요청

현재 isAdminPage는 location.pathname.startsWith('/admin')으로 판단합니다. 경로가 '/admin' 외 하위 경로(예: '/admin/users')까지 정확히 커버하는 점은 좋습니다. 위 코멘트대로 MobileHeader에도 동일 조건을 적용하여 UX 및 오류 방지 일관성을 유지하세요.

원하시면 Provider 트리(App 루트 등)에서 Header가 항상 SearchProvider/CategoryProvider 하위에 있는지 추가로 검증하는 스크립트를 제공할 수 있습니다.

Also applies to: 118-137

Comment on lines +31 to +33
return (
<Styled.SearchBoxContainer $isFocused={isFocused} onSubmit={handleSubmit}>
<Styled.SearchInputStyles
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

컨테이너 요소가 form인지 확인 필요 — onSubmit 동작/엔터 제출 보장 위해 as='form'과 role='search' 지정 권장

Styled.SearchBoxContainer가 styled.form이 아니라면 onSubmit이 동작하지 않습니다. 안전하게 as='form'과 role='search'를 명시하세요.

-  return (
-    <Styled.SearchBoxContainer $isFocused={isFocused} onSubmit={handleSubmit}>
+  return (
+    <Styled.SearchBoxContainer
+      as='form'
+      role='search'
+      $isFocused={isFocused}
+      onSubmit={handleSubmit}
+    >

또는 SearchField.styles.ts에서 SearchBoxContainer를 styled.form으로 선언되어 있는지 확인해 주세요.


🏁 Script executed:

#!/bin/bash
# Find the style file and inspect the definition of SearchBoxContainer
STYLE_FILE=$(fd SearchField.styles.ts)
echo "Found style file: $STYLE_FILE"
rg -n "SearchBoxContainer" "$STYLE_FILE"

Length of output: 446


Search 역할 명시를 위해 role="search" 추가 필요

현재 SearchBoxContainerstyled.form으로 정의되어 있어 onSubmit은 정상적으로 동작합니다. 그러나 접근성(ARIA) 향상을 위해 role="search" 속성을 추가하세요.

대상 파일: frontend/src/components/common/SearchField/SearchField.tsx
라인: 31–33

-  return (
-    <Styled.SearchBoxContainer $isFocused={isFocused} onSubmit={handleSubmit}>
+  return (
+    <Styled.SearchBoxContainer
+      role="search"
+      $isFocused={isFocused}
+      onSubmit={handleSubmit}
+    >
📝 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.

Suggested change
return (
<Styled.SearchBoxContainer $isFocused={isFocused} onSubmit={handleSubmit}>
<Styled.SearchInputStyles
return (
<Styled.SearchBoxContainer
role="search"
$isFocused={isFocused}
onSubmit={handleSubmit}
>
<Styled.SearchInputStyles
/* ...rest of props... */
🤖 Prompt for AI Agents
In frontend/src/components/common/SearchField/SearchField.tsx around lines 31 to
33, the SearchBoxContainer (a styled.form) needs the ARIA role added; update the
JSX to include role="search" on the Styled.SearchBoxContainer element (ensure
the prop passes through to the underlying form element and adjust any TS props
types if necessary so role is accepted).

@lepitaaar lepitaaar deleted the refactor/#643-common-searchField-MOA-152 branch October 21, 2025 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments