Skip to content

[FIX] Drawer 각 자식에게 Key 지정#113

Merged
ujinsim merged 1 commit intomainfrom
feat/#112
Nov 22, 2025
Merged

[FIX] Drawer 각 자식에게 Key 지정#113
ujinsim merged 1 commit intomainfrom
feat/#112

Conversation

@ujinsim
Copy link
Copy Markdown
Collaborator

@ujinsim ujinsim commented Nov 22, 2025

🔥 연관 이슈

🚀 작업 내용

Framer Motion에서 motion 요소가 내부적으로 key로 관리하도록
Drawer의 배경과 패널 각각에 고유 key를 지정했습니다.

🤔 고민했던 내용

💬 리뷰 중점사항

Summary by CodeRabbit

Release Notes

  • Refactor
    • Drawer 컴포넌트의 렌더링 안정성을 개선했습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 22, 2025

Walkthrough

Drawer 컴포넌트의 Framer Motion 요소들에 정적 key 속성을 추가했습니다. backdrop 요소에 key="drawer-backdrop", panel 요소에 key="drawer-panel"을 할당하여 재렌더링 중 요소 식별성을 안정화합니다.

Changes

Cohort / File(s) Change Summary
Drawer 컴포넌트 key 속성 추가
src/shared/ui/Drawer/Drawer.tsx
Framer Motion backdrop 및 panel 요소에 정적 key 속성 추가 (중복키 문제 해결)

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

  • 단일 파일의 최소한의 변경사항
  • 두 개의 key 속성 추가로 구성된 간단한 수정
  • 로직 변경이나 새로운 동작 없음

Suggested reviewers

  • yougyung
  • keemsebin

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경의 주요 내용인 Drawer 컴포넌트의 key 지정을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 리포지토리의 템플릿 구조를 따르고 있으며, 연관 이슈, 작업 내용을 포함하고 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경이 연관 이슈 #112의 목표인 Drawer 중복키 문제 해결을 완료하였습니다.
Out of Scope Changes check ✅ Passed PR의 모든 변경사항(Drawer 컴포넌트의 key 추가)이 연관 이슈 #112의 범위 내에 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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/#112

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.

@github-actions
Copy link
Copy Markdown
Contributor

Update: 2025년 11월 22일 18시 19분 53초
Storybook 미리보기: https://677a9a76c58a295e9421d3ef-lmplyzjjfh.chromatic.com/

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: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aec8673 and 3b61280.

📒 Files selected for processing (1)
  • src/shared/ui/Drawer/Drawer.tsx (2 hunks)

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.

@ujinsim ujinsim merged commit e7decc3 into main Nov 22, 2025
9 checks passed
@ujinsim ujinsim deleted the feat/#112 branch November 22, 2025 09:22
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.

[FIX] Drawer 중복키 문제 해결

1 participant