Skip to content

feat: 프롬프트 정렬 기능 구현#50

Merged
ccconac merged 4 commits intodevfrom
feat/49-prompt-sort
Feb 8, 2026
Merged

feat: 프롬프트 정렬 기능 구현#50
ccconac merged 4 commits intodevfrom
feat/49-prompt-sort

Conversation

@ccconac
Copy link
Copy Markdown
Member

@ccconac ccconac commented Feb 6, 2026

✅ 체크리스트

  • Select 컴포넌트 radix-ui 사용해 리팩토링
  • 프롬프트 정렬 기능 구현 (latest/likes/views)

📝 작업 상세 내용

1. Select 컴포넌트 radix-ui 사용해 리팩토링

Select 컴포넌트의 수월한 커스텀 및 편리하게 확장 기능을 사용하기 위해 radix-ui를 활용해 리팩토링했습니다. MVP 완성을 위해 빠르게 개발한 터라 추후 추가적인 리팩토링이 진행될 수 있습니다.

2. 프롬프트 정렬 기능 구현

프롬프트 정렬 기능 구현이 완료되었습니다. 기본은 최신순이며, 좋아요/조회수로 정렬 기준 변경이 가능합니다.

sorting-prompt

✅ 셀프 체크리스트

  • 브랜치 확인하기
  • 불필요한 코드가 들어가지 않았는지 재확인하기
  • issue 닫기
  • reiewers, assignees, Lables 등록 확인하기

이슈 번호: #49

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 정렬 옵션 확대: 최신순, 좋아요순, 조회순으로 확장
    • URL 기반 페이지네이션 및 정렬 상태 관리 추가
    • 개선된 선택 필드 컴포넌트 UI 재설계
  • 개선 사항

    • 입력 필드 스타일 및 외관 개선
    • 폼 입력 제어 기능 강화
    • 레이아웃 반응형 너비 최적화

@ccconac ccconac self-assigned this Feb 6, 2026
@ccconac ccconac added ♻️ REFACTOR 리팩토링 (성능 개선 등) ✨ FEAT 기능 개발 labels Feb 6, 2026
@ccconac ccconac linked an issue Feb 6, 2026 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

이 PR은 프롬프트 정렬 기능을 개선하고, Radix UI Select 기반의 새로운 선택 필드를 도입하며, URL 기반의 상태 관리를 추가합니다. 또한 API 타입을 업데이트하고 여러 컴포넌트의 스타일과 레이아웃을 조정합니다.

Changes

코호트 / 파일(들) 요약
프롬프트 API 타입
src/apis/prompts/prompts.types.ts
SortType 새 타입 도입 ('latest' | 'likes' | 'views')으로 GetPromptListParams.sort 업데이트
입력 필드 컴포넌트 재구성
src/components/Input/Input.tsx, Input.types.ts, Input.styles.ts
Radix UI Select 통합, forwardRef 제거, ref를 props로 전달 변경, CommonStyle 업데이트로 새로운 컨테이너 스타일 적용
UI 컴포넌트 업데이트
src/components/Card/Card.tsx, src/components/Icon/iconMap.ts, src/components/Layout/Layout.tsx
카드 호버 효과 변경, check 및 chevronDown 아이콘 추가, 반응형 너비 제약 조건 추가
설정 상수
src/config/constants.ts
세 개의 정렬 옵션(latest, likes, views)을 포함하는 SORT_OPTIONS 상수 추가
페이지 컴포넌트 업데이트
src/pages/PromptList/PromptListPage.tsx, src/pages/CreatePrompt/_components/CreatePromptForm.tsx
URL 기반 정렬 및 페이지 상태 관리 추가, SelectField 정렬 UI 구현, CreatePromptForm에서 Controller 통합으로 선택 필드 제어

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • feat: Input 컴포넌트 구현 #33: Input 컴포넌트 및 관련 타입/스타일(Input.tsx, Input.styles.ts, Input.types.ts) 수정 — 이전 PR에서 기본 Input 구현을 추가했고, 이 PR에서는 Radix Select 통합과 타입 변경으로 재구성합니다.
  • feat: 프롬프트 목록 페이지 추가 기능 구현 #42: src/apis/prompts/prompts.types.ts의 GetPromptListParams.sort 수정 — 이전 PR에서 'latest' | 'oldest'로 확장했고, 이 PR에서는 SortType = 'latest' | 'likes' | 'views'로 변경합니다.
  • feat: 프롬프트 반응 기능 연결 #46: src/pages/PromptList/PromptListPage.tsx 수정으로 사용자 좋아요 ID와 프롬프트 병합 — mergedPrompts에서 isLiked 추가와 관련됩니다.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '프롬프트 정렬 기능 구현'은 변경사항의 핵심인 프롬프트 정렬 기능 구현을 명확하게 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/49-prompt-sort

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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: 8

🤖 Fix all issues with AI agents
In `@src/components/Card/Card.tsx`:
- Line 85: The hover translate utility in the Card component's className moves
the card down; change the utility token from hover:translate-y-0.5 to
hover:-translate-y-0.5 inside the className string in
src/components/Card/Card.tsx (the Card component) so the card lifts upward on
hover and matches the hover:shadow behavior.

In `@src/components/Input/Input.tsx`:
- Around line 74-76: InputField is receiving a flex-based CommonStyle (from
Input.styles.ts) which contains "flex items-center justify-between
min-w-[140px]" and that breaks input layout and removed the original w-full;
update InputField to avoid applying those flex-specific utilities to the raw
<input>: either remove/replace CommonStyle usage in the InputField component so
it uses input-appropriate classes (e.g., restore w-full and remove
flex/items-center/justify-between/min-w) or split CommonStyle into separate
container vs input styles and apply only the input style to InputField; check
consumer pages such as CreatePromptForm.tsx after the change to ensure layout is
restored.
- Around line 22-26: The check icon wrapped by the span with className "absolute
right-3" (SelectPrimitive.ItemIndicator) can overlap long
SelectPrimitive.ItemText; update the item container (the element that renders
SelectPrimitive.ItemText / the item row component) to add right padding (e.g.,
pr-8) so text has room before the absolute-positioned indicator, and ensure the
container is position: relative so the indicator stays anchored to the item.
- Around line 54-58: SelectPrimitive.Content is using position="popper", which
doesn't auto-match trigger width; update the Content styling to use the Radix
CSS variable so the dropdown matches the trigger width (replace the current
fixed/min-w utility with min-width: var(--radix-select-trigger-width) or
equivalent inline style/class) when rendering SelectPrimitive.Content with
position="popper" so the dropdown aligns with the trigger.

In `@src/components/Layout/Layout.tsx`:
- Around line 16-20: The conditional class string passed to cn in the Layout
component leaves the main-page class "md:w-4xl" active on non-main pages because
tailwind-merge does not remove it; update the non-main-page class list (the
conditional in className in Layout.tsx where cn(...) is called) to explicitly
override the md breakpoint (e.g., add a desired "md:w-5xl" or other md:w-* value
alongside "lg:w-7xl") so the md size changes on non-main pages; modify the
conditional branch that builds the non-main-page classes used with cn to include
the explicit md:w-* override.

In `@src/config/constants.ts`:
- Around line 24-37: SORT_OPTIONS 배열의 라벨 불일치 문제: '최신순'과 '좋아요 순'에는 '순' 접미사가 있는 반면
'조회수'만 예외이므로 사용자 표시 텍스트를 일관되게 맞춥니다; src 상수의 SORT_OPTIONS 항목 중 label 값을 찾아 '조회수'를
'조회수 순' 또는 '조회순'(팀 스타일에 맞춰 선택)으로 변경하고 다른 라벨들의 공백/접미사 스타일과 일치하도록 수정하세요.

In `@src/pages/PromptList/PromptListPage.tsx`:
- Around line 82-87: The Select is being rendered as a controlled component by
passing value={sortOrder}, so remove the redundant defaultValue prop from
Input.SelectField to avoid warnings; update the component instance
(Input.SelectField) to only use value={sortOrder}, options={SORT_OPTIONS}, and
onValueChange={handleSortChange}, and ensure the initial sortOrder state (where
it's initialized) is set to SORT_OPTIONS[0].value if you need a default.
- Around line 19-20: The code currently casts searchParams.get('sort') to
SortType without runtime validation; change the logic that sets sortOrder to
validate the raw value against the allowed keys in SORT_OPTIONS and fall back to
'latest' if invalid. Specifically, replace the direct cast of sortOrder (where
searchParams.get('sort') is used) with a small check like: read rawSort =
searchParams.get('sort'), if rawSort is a key/value present in SORT_OPTIONS then
set sortOrder = rawSort as SortType else sortOrder = 'latest'; keep the page
extraction unchanged. This ensures SortType, searchParams.get('sort'),
SORT_OPTIONS, and sortOrder are used to gate allowed values before calling the
API.
🧹 Nitpick comments (7)
src/components/Input/Input.styles.ts (1)

1-2: CommonStyle이 Select 전용으로 변경되어 InputField/TextField에 부적절한 스타일이 적용됨

CommonStyleflex items-center justify-between min-w-[140px]가 포함되어 있는데, 이 스타일은 SelectField 트리거에는 적합하지만:

  • <textarea>: items-center로 인해 여러 줄 텍스트가 수직 중앙 정렬되어 어색할 수 있습니다.
  • <input>/<textarea>: justify-betweenmin-w-[140px]은 일반 입력 필드에 의미 없거나 제약적입니다.
  • w-full이 제거되어 기존 InputField/TextField가 전체 너비를 차지하지 않게 됩니다.

SelectField 전용 스타일과 공통 스타일(border, rounded, transition 등)을 분리하는 것을 권장합니다.

src/components/Input/Input.types.ts (2)

4-10: React 19에서 ref 명시적 추가는 중복됨

React 19에서는 ComponentPropsWithRef에 이미 ref가 포함되어 있으므로, & { ref?: React.Ref<...> } 교차 타입은 불필요합니다. 기능적으로 문제는 없지만 코드 의도가 불명확해질 수 있습니다.

♻️ 정리 제안
-export type InputFieldProps = ComponentPropsWithRef<'input'> & {
-  ref?: React.Ref<HTMLInputElement>;
-};
+export type InputFieldProps = ComponentPropsWithRef<'input'>;

-export type TextFieldProps = ComponentPropsWithRef<'textarea'> & {
-  ref?: React.Ref<HTMLTextAreaElement>;
-};
+export type TextFieldProps = ComponentPropsWithRef<'textarea'>;

17-22: SelectFieldPropsref 타입이 HTMLButtonElement로 되어 있는데, 실제 연결 대상 확인 필요

refReact.Ref<HTMLButtonElement>로 선언되어 있고, Input.tsx에서 SelectPrimitive.Trigger에 전달됩니다. Radix Select의 Trigger가 <button>을 렌더링하므로 타입은 맞지만, ComponentPropsWithRef<typeof SelectPrimitive.Root>를 확장하면서 Root의 props와 Trigger의 ref가 혼재되어 있어 API가 혼란스러울 수 있습니다.

src/components/Input/Input.tsx (1)

16-20: SelectItem의 className 구성에 불필요한 공백 및 가독성 문제

템플릿 리터럴 내 멀티라인 클래스 문자열에 들여쓰기로 인한 불필요한 공백이 포함됩니다. cn() 유틸리티가 이미 프로젝트에 존재하므로(Layout.tsx에서 import) 이를 활용하면 가독성과 안전한 클래스 병합이 가능합니다.

♻️ cn() 유틸리티 활용 제안
     <SelectPrimitive.Item
       ref={ref}
-      className={`relative flex w-full cursor-default select-none items-center rounded-sm text-sm outline-none px-4 py-2 text-left justify-between transition-colors focus:bg-brand-purple-light focus:text-brand-purple data-[state=checked]:bg-brand-purple-light data-[state=checked]:text-brand-purple data-disabled:pointer-events-none data-disabled:opacity-50
-      ${className ?? ''}
-    `}
+      className={cn(
+        'relative flex w-full cursor-default select-none items-center rounded-sm text-sm outline-none px-4 py-2 text-left justify-between transition-colors focus:bg-brand-purple-light focus:text-brand-purple data-[state=checked]:bg-brand-purple-light data-[state=checked]:text-brand-purple data-disabled:pointer-events-none data-disabled:opacity-50',
+        className
+      )}
       {...props}>
src/pages/CreatePrompt/_components/CreatePromptForm.tsx (2)

139-151: Platform Controller도 category와 동일한 {...field} 스프레드 이슈가 있습니다.

위 카테고리 Controller에 대한 코멘트와 동일하게, 필요한 props만 명시적으로 전달하는 것을 권장합니다.

♻️ 동일한 패턴 적용 제안
          render={({ field }) => (
            <Input.SelectField
-              {...field}
+              value={field.value}
              placeholder="플랫폼을 선택하세요"
              options={safePlatformOptions}
              onValueChange={field.onChange}
            />
          )}

124-136: SelectField에 {...field} 스프레드 시 불필요한 props를 명시적으로 처리하는 것이 권장됩니다.

SelectField 컴포넌트는 받은 props를 그대로 SelectPrimitive.Root에 전달합니다. {...field}를 스프레드하면 onChange, onBlur, name 등 Radix Select가 인식하지 못하는 props도 함께 전달됩니다. Radix UI는 일반적으로 미인식 props를 무시하지만, 필요한 props만 명시적으로 전달하는 것이 더 명확하고 유지보수하기 좋습니다.

♻️ 명시적 props 전달 방식 제안
          render={({ field }) => (
            <Input.SelectField
-              {...field}
+              value={field.value}
              placeholder="카테고리를 선택하세요"
              options={safeCategoryOptions}
              onValueChange={field.onChange}
            />
          )}
src/pages/PromptList/PromptListPage.tsx (1)

43-57: setSearchParams가 기존 파라미터를 모두 교체합니다.

현재는 sortpage만 사용하므로 문제가 없지만, 향후 검색어나 필터 등 추가 파라미터가 생길 경우 기존 값이 유실됩니다. 확장성을 고려하면 기존 파라미터를 유지하는 패턴이 더 안전합니다.

♻️ 기존 파라미터를 유지하는 방식 제안
  const handleSortChange = (newSort: string) => {
-    setSearchParams({
-      sort: newSort,
-      page: '1',
+    setSearchParams((prev) => {
+      prev.set('sort', newSort);
+      prev.set('page', '1');
+      return prev;
    });
  };

  const handlePageChange = (newPage: number) => {
-    setSearchParams({
-      sort: sortOrder,
-      page: String(newPage),
+    setSearchParams((prev) => {
+      prev.set('page', String(newPage));
+      return prev;
    });

    window.scrollTo(0, 0);
  };

<article
key={id}
className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:-translate-y-0.5 hover:border-brand-purple-border"
className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:translate-y-0.5 hover:border-brand-purple-border"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

호버 시 카드가 아래로 이동하는 것은 의도된 동작인지 확인 필요

hover:translate-y-0.5는 호버 시 카드를 아래로 이동시킵니다. 일반적으로 카드 호버 효과는 hover:-translate-y-0.5로 카드를 위로 들어올려 부유(lift) 효과를 줍니다. 현재 호버 시 그림자가 커지면서(hover:shadow-[...]) 카드가 아래로 이동하는 것은 시각적으로 모순됩니다. 기존의 -translate-y-0.5에서 - 부호가 실수로 제거된 것으로 보입니다.

🐛 수정 제안
-      className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:translate-y-0.5 hover:border-brand-purple-border"
+      className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:-translate-y-0.5 hover:border-brand-purple-border"
📝 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
className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:translate-y-0.5 hover:border-brand-purple-border"
className="flex flex-col h-58 gap-3 px-5 pt-5 pb-4 group bg-white rounded-2xl border border-gray-200 overflow-hidden cursor-pointer transition-all shadow-[0_1px_3px_0_rgba(0,0,0,0.04)] hover:shadow-[0_8px_24px_-4px_rgba(109,91,208,0.12),0_4px_8px_-2px_rgba(109,91,208,0.08)] hover:-translate-y-0.5 hover:border-brand-purple-border"
🤖 Prompt for AI Agents
In `@src/components/Card/Card.tsx` at line 85, The hover translate utility in the
Card component's className moves the card down; change the utility token from
hover:translate-y-0.5 to hover:-translate-y-0.5 inside the className string in
src/components/Card/Card.tsx (the Card component) so the card lifts upward on
hover and matches the hover:shadow behavior.

Comment on lines +22 to +26
<span className="absolute right-3">
<SelectPrimitive.ItemIndicator>
<Icon name="check" size="sm" />
</SelectPrimitive.ItemIndicator>
</span>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

체크 아이콘 absolute right-3 위치가 ItemText와 겹칠 수 있음

SelectPrimitive.ItemText의 텍스트가 길 경우, absolute right-3으로 배치된 체크 아이콘과 겹칠 수 있습니다. 아이템에 pr-8 등의 오른쪽 패딩을 추가하여 텍스트와 아이콘 간 충돌을 방지해 주세요.

♻️ 패딩 추가 제안
-      className={`relative flex w-full cursor-default select-none items-center rounded-sm text-sm outline-none px-4 py-2 text-left justify-between transition-colors ...`}
+      className={`relative flex w-full cursor-default select-none items-center rounded-sm text-sm outline-none pl-4 pr-8 py-2 text-left justify-between transition-colors ...`}
🤖 Prompt for AI Agents
In `@src/components/Input/Input.tsx` around lines 22 - 26, The check icon wrapped
by the span with className "absolute right-3" (SelectPrimitive.ItemIndicator)
can overlap long SelectPrimitive.ItemText; update the item container (the
element that renders SelectPrimitive.ItemText / the item row component) to add
right padding (e.g., pr-8) so text has room before the absolute-positioned
indicator, and ensure the container is position: relative so the indicator stays
anchored to the item.

Comment on lines +54 to +58
<SelectPrimitive.Portal>
<SelectPrimitive.Content
className="relative overflow-hidden text-gray-900 left-0 bg-white border border-gray-200 rounded-lg shadow-lg z-50 min-w-50 py-1 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
position="popper"
sideOffset={4}>
Copy link
Copy Markdown

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:

#!/bin/bash
# Check for tailwindcss-animate in package.json and dependencies
if [ -f "package.json" ]; then
  echo "=== Checking package.json for tailwindcss-animate ==="
  rg "tailwindcss-animate" package.json
  echo ""
  echo "=== Checking package.json for any animate-related packages ==="
  rg "animate" package.json
fi

Repository: promlog/promlog-client

Length of output: 183


🏁 Script executed:

#!/bin/bash
# Check for Tailwind config file
echo "=== Looking for Tailwind config files ==="
fd -e "config.js" -e "config.ts" -e "config.mjs" | grep -i tail

echo ""
echo "=== Listing tailwind-related files ==="
fd -e "tailwind" -e "tw-"

Repository: promlog/promlog-client

Length of output: 147


🏁 Script executed:

#!/bin/bash
# Check the actual Input component to see context
echo "=== Examining Input.tsx file ==="
if [ -f "src/components/Input/Input.tsx" ]; then
  cat -n src/components/Input/Input.tsx | head -80
fi

Repository: promlog/promlog-client

Length of output: 3982


🏁 Script executed:

#!/bin/bash
# Search more broadly for Tailwind configuration
echo "=== Checking for any config files (broader search) ==="
fd "config" | head -20

echo ""
echo "=== Checking package.json location and full content ==="
cat package.json | head -50

Repository: promlog/promlog-client

Length of output: 1921


🏁 Script executed:

#!/bin/bash
# Look for CSS files and PostCSS config
echo "=== Looking for CSS and config files ==="
fd -e "css" -e "postcss.config" -e "tailwind.config" -e "vite.config" -e "tsconfig" | head -30

Repository: promlog/promlog-client

Length of output: 121


🏁 Script executed:

#!/bin/bash
# Check if there's any Tailwind class definitions in CSS files
echo "=== Searching for animation-related CSS definitions ==="
rg "animate-in|animate-out|fade-in|zoom-in" --type css -A 2

Repository: promlog/promlog-client

Length of output: 121


🏁 Script executed:

#!/bin/bash
echo "=== Checking global.css content ==="
cat -n src/global.css

Repository: promlog/promlog-client

Length of output: 4156


🏁 Script executed:

#!/bin/bash
echo "=== Checking vite.config.ts ==="
cat -n vite.config.ts

Repository: promlog/promlog-client

Length of output: 1734


🏁 Script executed:

#!/bin/bash
echo "=== Checking radix-ui version ==="
rg "radix-ui" package.json -A 1

Repository: promlog/promlog-client

Length of output: 149


🏁 Script executed:

#!/bin/bash
echo "=== Searching for any width-related CSS or configurations for SelectField ==="
rg "radix-select-trigger-width|select.*width|content.*width" -A 2

Repository: promlog/promlog-client

Length of output: 287


position="popper" 사용 시 Content 너비가 트리거와 일치하지 않을 수 있음

position="popper" 모드에서는 드롭다운 Content의 너비가 트리거 너비와 자동으로 일치하지 않습니다. CSS 변수 --radix-select-trigger-width를 활용하여 Content 너비를 트리거에 맞출 수 있습니다:

min-width: var(--radix-select-trigger-width)
🤖 Prompt for AI Agents
In `@src/components/Input/Input.tsx` around lines 54 - 58, SelectPrimitive.Content
is using position="popper", which doesn't auto-match trigger width; update the
Content styling to use the Radix CSS variable so the dropdown matches the
trigger width (replace the current fixed/min-w utility with min-width:
var(--radix-select-trigger-width) or equivalent inline style/class) when
rendering SelectPrimitive.Content with position="popper" so the dropdown aligns
with the trigger.

Comment on lines +74 to +76
const InputField = ({ className, ref, ...restProps }: InputFieldProps) => {
return <input ref={ref} className={`py-3 ${CommonStyle} ${className ?? ''}`} {...restProps} />;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

InputField에 flex 기반 CommonStyle이 적용되어 입력 필드 레이아웃이 깨질 수 있음

Input.styles.ts 리뷰에서 언급한 것과 동일한 이슈입니다. CommonStyleflex items-center justify-between min-w-[140px]<input> 요소에 적합하지 않으며, 기존의 w-full이 제거되어 입력 필드가 전체 너비를 차지하지 않습니다. 기존 InputField를 사용하는 페이지(예: CreatePromptForm.tsx)에서 레이아웃이 깨지는지 확인이 필요합니다.

🤖 Prompt for AI Agents
In `@src/components/Input/Input.tsx` around lines 74 - 76, InputField is receiving
a flex-based CommonStyle (from Input.styles.ts) which contains "flex
items-center justify-between min-w-[140px]" and that breaks input layout and
removed the original w-full; update InputField to avoid applying those
flex-specific utilities to the raw <input>: either remove/replace CommonStyle
usage in the InputField component so it uses input-appropriate classes (e.g.,
restore w-full and remove flex/items-center/justify-between/min-w) or split
CommonStyle into separate container vs input styles and apply only the input
style to InputField; check consumer pages such as CreatePromptForm.tsx after the
change to ensure layout is restored.

Comment on lines 16 to 20
className={cn(
'max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8',
'max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8 lg:w-6xl md:w-4xl sm:w-2xl',
!isMainPage &&
'lg:w-7xl min-h-[calc(100vh-4rem)] bg-white/95 backdrop-blur-sm border-x border-gray-200 shadow-[0_1px_3px_0_rgba(0,0,0,0.05)] flex justify-center sm:w-4xl'
)}>
Copy link
Copy Markdown

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:

# Find and read the Button.styles file to understand cn utility
find src -name "*Button*" -type f | head -20

Repository: promlog/promlog-client

Length of output: 328


🏁 Script executed:

# Also check for the cn utility definition
rg "export.*cn\s*=" src/ -A 3

Repository: promlog/promlog-client

Length of output: 48


🏁 Script executed:

# Read the Layout.tsx file to verify the code
cat -n src/components/Layout/Layout.tsx | head -50

Repository: promlog/promlog-client

Length of output: 1123


🏁 Script executed:

# Read Button.styles.ts to see the cn implementation
cat -n src/components/Button/Button.styles.ts

Repository: promlog/promlog-client

Length of output: 1209


비 메인 페이지에서 md:w-4xl이 명시적으로 오버라이드되지 않음

cn 함수가 tailwind-merge를 사용하므로, 메인 페이지 클래스(Line 17)의 md:w-4xl이 비 메인 페이지 조건(Line 19)에서도 그대로 적용됩니다. 현재 상태에서 비 메인 페이지의 반응형 너비는 sm:w-4xl → md:w-4xl (변화 없음) → lg:w-7xl이 되어, md 브레이크포인트에서 스케일링이 제대로 되지 않습니다. 의도에 맞게 비 메인 페이지의 md 브레이크포인트를 명시적으로 오버라이드하세요.

예: Line 19에 md:w-5xl (또는 다른 값) 추가

🤖 Prompt for AI Agents
In `@src/components/Layout/Layout.tsx` around lines 16 - 20, The conditional class
string passed to cn in the Layout component leaves the main-page class
"md:w-4xl" active on non-main pages because tailwind-merge does not remove it;
update the non-main-page class list (the conditional in className in Layout.tsx
where cn(...) is called) to explicitly override the md breakpoint (e.g., add a
desired "md:w-5xl" or other md:w-* value alongside "lg:w-7xl") so the md size
changes on non-main pages; modify the conditional branch that builds the
non-main-page classes used with cn to include the explicit md:w-* override.

Comment thread src/config/constants.ts
Comment on lines +24 to +37
export const SORT_OPTIONS = [
{
label: '최신순',
value: 'latest',
},
{
label: '좋아요 순',
value: 'likes',
},
{
label: '조회수',
value: 'views',
},
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

정렬 옵션 라벨의 일관성 부족

최신순, 좋아요 순에는 '순' 접미사가 있지만, 조회수에는 없습니다. 사용자에게 표시되는 텍스트이므로 조회수 순 또는 조회순으로 통일하는 것이 좋습니다.

✏️ 수정 제안
   {
-    label: '조회수',
+    label: '조회수 순',
     value: 'views',
   },
📝 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
export const SORT_OPTIONS = [
{
label: '최신순',
value: 'latest',
},
{
label: '좋아요 순',
value: 'likes',
},
{
label: '조회수',
value: 'views',
},
];
export const SORT_OPTIONS = [
{
label: '최신순',
value: 'latest',
},
{
label: '좋아요 순',
value: 'likes',
},
{
label: '조회수',
value: 'views',
},
];
🤖 Prompt for AI Agents
In `@src/config/constants.ts` around lines 24 - 37, SORT_OPTIONS 배열의 라벨 불일치 문제:
'최신순'과 '좋아요 순'에는 '순' 접미사가 있는 반면 '조회수'만 예외이므로 사용자 표시 텍스트를 일관되게 맞춥니다; src 상수의
SORT_OPTIONS 항목 중 label 값을 찾아 '조회수'를 '조회수 순' 또는 '조회순'(팀 스타일에 맞춰 선택)으로 변경하고 다른
라벨들의 공백/접미사 스타일과 일치하도록 수정하세요.

Comment on lines +19 to +20
const page = Number(searchParams.get('page')) || 1;
const sortOrder = (searchParams.get('sort') as SortType) || 'latest';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

URL에서 가져온 sort 값에 대한 타입 검증이 없습니다.

as SortType은 런타임 검증 없이 타입만 단언합니다. 사용자가 URL에 ?sort=invalid를 직접 입력하면 검증되지 않은 값이 API에 그대로 전달됩니다.

🛡️ SortType 유효성 검증 추가 제안
+import { SORT_OPTIONS } from '../../config/constants';
+
+const isValidSortType = (value: string | null): value is SortType =>
+  SORT_OPTIONS.some((opt) => opt.value === value);
+
  const page = Number(searchParams.get('page')) || 1;
-  const sortOrder = (searchParams.get('sort') as SortType) || 'latest';
+  const rawSort = searchParams.get('sort');
+  const sortOrder: SortType = isValidSortType(rawSort) ? rawSort : 'latest';

참고: SORT_OPTIONS는 이미 Line 10에서 import되어 있으므로 별도 import는 불필요합니다.

📝 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
const page = Number(searchParams.get('page')) || 1;
const sortOrder = (searchParams.get('sort') as SortType) || 'latest';
const isValidSortType = (value: string | null): value is SortType =>
SORT_OPTIONS.some((opt) => opt.value === value);
const page = Number(searchParams.get('page')) || 1;
const rawSort = searchParams.get('sort');
const sortOrder: SortType = isValidSortType(rawSort) ? rawSort : 'latest';
🤖 Prompt for AI Agents
In `@src/pages/PromptList/PromptListPage.tsx` around lines 19 - 20, The code
currently casts searchParams.get('sort') to SortType without runtime validation;
change the logic that sets sortOrder to validate the raw value against the
allowed keys in SORT_OPTIONS and fall back to 'latest' if invalid. Specifically,
replace the direct cast of sortOrder (where searchParams.get('sort') is used)
with a small check like: read rawSort = searchParams.get('sort'), if rawSort is
a key/value present in SORT_OPTIONS then set sortOrder = rawSort as SortType
else sortOrder = 'latest'; keep the page extraction unchanged. This ensures
SortType, searchParams.get('sort'), SORT_OPTIONS, and sortOrder are used to gate
allowed values before calling the API.

Comment on lines +82 to +87
<Input.SelectField
defaultValue={SORT_OPTIONS[0].value}
value={sortOrder}
options={SORT_OPTIONS}
onValueChange={handleSortChange}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

defaultValuevalue를 동시에 사용하고 있습니다.

Radix UI Select에서 value를 전달하면 제어 컴포넌트(controlled component)로 동작합니다. 이 경우 defaultValue는 무시되거나 경고를 발생시킬 수 있습니다. value={sortOrder}가 이미 있으므로 defaultValue를 제거하세요.

🔧 defaultValue 제거 제안
        <Input.SelectField
-          defaultValue={SORT_OPTIONS[0].value}
          value={sortOrder}
          options={SORT_OPTIONS}
          onValueChange={handleSortChange}
        />
📝 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
<Input.SelectField
defaultValue={SORT_OPTIONS[0].value}
value={sortOrder}
options={SORT_OPTIONS}
onValueChange={handleSortChange}
/>
<Input.SelectField
value={sortOrder}
options={SORT_OPTIONS}
onValueChange={handleSortChange}
/>
🤖 Prompt for AI Agents
In `@src/pages/PromptList/PromptListPage.tsx` around lines 82 - 87, The Select is
being rendered as a controlled component by passing value={sortOrder}, so remove
the redundant defaultValue prop from Input.SelectField to avoid warnings; update
the component instance (Input.SelectField) to only use value={sortOrder},
options={SORT_OPTIONS}, and onValueChange={handleSortChange}, and ensure the
initial sortOrder state (where it's initialized) is set to SORT_OPTIONS[0].value
if you need a default.

@ccconac ccconac merged commit 23c4a11 into dev Feb 8, 2026
1 check passed
@ccconac ccconac deleted the feat/49-prompt-sort branch February 12, 2026 17:51
@coderabbitai coderabbitai Bot mentioned this pull request Feb 26, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ FEAT 기능 개발 ♻️ REFACTOR 리팩토링 (성능 개선 등)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 정렬 기능 추가

1 participant