Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/shared/ui/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function Drawer({ isOpen, onClose, children }: Props) {
return (
<Portal isOpen={isOpen}>
<motion.div
key="drawer-backdrop"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

여러 Drawer 인스턴스가 동시에 렌더링될 경우 중복 키 문제가 발생할 수 있습니다.

정적 키 값("drawer-backdrop", "drawer-panel")은 단일 Drawer 인스턴스에서는 잘 작동하지만, 동일한 페이지에서 여러 Drawer가 동시에 렌더링되면 중복 키 경고가 발생합니다.

코드베이스에서 Drawer의 사용 패턴을 확인하여 여러 인스턴스가 동시에 존재할 가능성이 있는지 검증해주세요.

다음 스크립트를 실행하여 Drawer의 사용 패턴을 확인하세요:

만약 여러 Drawer 인스턴스가 동시에 존재할 수 있다면, 다음과 같이 Props에 id를 추가하여 고유한 키를 생성하는 것을 권장합니다:

 export type Props = {
+  /**
+   * Unique identifier for the drawer instance.
+   */
+  id?: string;
   /**
    * The open state of the drawer.
    */
   isOpen: boolean;
   /**
    * The function to call when the drawer is closed.
    */
   onClose: () => void;
 } & PropsWithChildren;

-export function Drawer({ isOpen, onClose, children }: Props) {
+export function Drawer({ id, isOpen, onClose, children }: Props) {
   return (
     <Portal isOpen={isOpen}>
       <motion.div
-        key="drawer-backdrop"
+        key={id ? `drawer-backdrop-${id}` : "drawer-backdrop"}
         onClick={onClose}
         className="fixed inset-0 bg-black/50"
         initial={FADE_IN_ANIMATION.initial}
         animate={FADE_IN_ANIMATION.animate}
         exit={FADE_IN_ANIMATION.exit}
       />
       <div className="fixed top-0 right-0 z-50 h-full">
         <motion.div
-          key="drawer-panel"
+          key={id ? `drawer-panel-${id}` : "drawer-panel"}
           className="h-full bg-white shadow-lg"
           initial={SLIDE_IN_ANIMATION.initial}
           animate={SLIDE_IN_ANIMATION.animate}
           exit={SLIDE_IN_ANIMATION.exit}
           transition={SLIDE_IN_ANIMATION.transition}
         >
           {children}
         </motion.div>
       </div>
     </Portal>
   );
 }

Also applies to: 43-43


🏁 Script executed:

#!/bin/bash
# Drawer 컴포넌트의 사용 패턴 분석

# Drawer 컴포넌트 import 및 사용 사례 검색
echo "=== Drawer 사용 사례 ==="
rg -n --type tsx --type ts -C 3 '<Drawer'

# 동일 파일 내 여러 Drawer 사용 확인
echo -e "\n=== 동일 파일 내 여러 Drawer 인스턴스 ==="
rg -n --type tsx --type ts -U '(?s)<Drawer.*<Drawer' 

# useState와 함께 사용되는 여러 Drawer 상태 확인
echo -e "\n=== 여러 Drawer 상태 관리 패턴 ==="
rg -n --type tsx --type ts 'is.*Open.*=.*useState.*Drawer|Drawer.*isOpen.*useState' -A 2

Length of output: 164


🏁 Script executed:

#!/bin/bash
# Drawer 컴포넌트의 사용 패턴 분석 (수정된 버전)

# Drawer 컴포넌트 import 및 사용 사례 검색
echo "=== Drawer 사용 사례 ==="
rg -n -g '*.tsx' -g '*.ts' -C 3 '<Drawer'

# Drawer import 찾기
echo -e "\n=== Drawer Import ==="
rg -n -g '*.tsx' -g '*.ts' 'from.*Drawer|import.*Drawer'

# 동일 파일 내 여러 Drawer 사용 확인
echo -e "\n=== 단일 파일 내 여러 Drawer 인스턴스 ==="
rg -n -g '*.tsx' 'Drawer' | awk -F: '{print $1}' | sort | uniq -c | awk '$1 > 1 {print $2}' | while read file; do
  echo "File: $file"
  rg -n -g '*.tsx' '<Drawer' "$file"
done

Length of output: 983


🏁 Script executed:

#!/bin/bash
# Drawer 컴포넌트 구현 확인 및 Portal 사용 패턴 분석

echo "=== Drawer 컴포넌트 구현 ==="
cat git/src/shared/ui/Drawer/Drawer.tsx

echo -e "\n=== Portal 컴포넌트 구현 ==="
find git -name 'Portal*' -type f | head -5

echo -e "\n=== Portal 구현 확인 ==="
cat git/src/shared/ui/Portal/Portal.tsx 2>/dev/null || echo "Portal.tsx not found, searching..."
rg -n -g '*.tsx' 'Portal' git/src/shared/ui/ -l

Length of output: 269


🏁 Script executed:

#!/bin/bash
# Drawer 컴포넌트 및 Portal 구현 확인

echo "=== Drawer 컴포넌트 구현 ==="
cat src/shared/ui/Drawer/Drawer.tsx

echo -e "\n=== Portal 컴포넌트 구현 ==="
cat src/shared/ui/Portal/Portal.tsx 2>/dev/null || echo "Portal.tsx not found"

echo -e "\n=== Portal 디렉토리 확인 ==="
ls -la src/shared/ui/ | grep -i portal

Length of output: 2167


여러 Drawer 인스턴스 동시 렌더링 시 중복 키 위험은 실제 문제입니다.

코드베이스 분석 결과:

  1. 현재 상태: Drawer 컴포넌트는 하나의 인스턴스만 사용 중입니다(Drawer.stories.tsx).

  2. 잠재적 위험: Portal 컴포넌트가 createPortal로 document.body에 콘텐츠를 렌더링하므로, 만약 여러 Drawer가 동시에 열려있다면 동일한 key="drawer-backdrop"key="drawer-panel"이 document.body의 직접 자식으로 여러 개 존재하게 되어 React 경고가 발생합니다.

  3. 현재 코드의 정상성: 단일 Drawer 인스턴스 패턴에서는 정상적으로 작동하며, AnimatePresence 설정도 적절합니다.

권장 사항: 향후 여러 Drawer를 동시에 지원할 가능성이 있다면, 제안된 id 기반 키 생성 방식으로 리팩토링하는 것을 권장합니다. 현재는 필수 사항은 아니지만, 컴포넌트 재사용성을 높이기 위한 좋은 개선입니다.

🤖 Prompt for AI Agents
In src/shared/ui/Drawer/Drawer.tsx around line 34, the component currently uses
a hardcoded key ("drawer-backdrop") which will cause duplicate key warnings if
multiple Drawer instances are rendered into document.body via Portal; change the
keys for backdrop and panel to be unique per-instance by using a stable id
(prefer React.useId() or accept an id prop and fallback to useId()) and
concatenate that id into the keys (e.g. `drawer-backdrop-${id}` and
`drawer-panel-${id}`), ensuring the id is generated once per component instance
so keys remain stable across renders.

onClick={onClose}
className="fixed inset-0 bg-black/50"
initial={FADE_IN_ANIMATION.initial}
Expand All @@ -39,6 +40,7 @@ export function Drawer({ isOpen, onClose, children }: Props) {
/>
<div className="fixed top-0 right-0 z-50 h-full">
<motion.div
key="drawer-panel"
className="h-full bg-white shadow-lg"
initial={SLIDE_IN_ANIMATION.initial}
animate={SLIDE_IN_ANIMATION.animate}
Expand Down