Skip to content

[♻️ Refactor/#59] Portal Root 공통 관리 구조 적용#60

Merged
Lseojeong merged 2 commits into
devfrom
refactor/#59
Jul 1, 2026
Merged

[♻️ Refactor/#59] Portal Root 공통 관리 구조 적용#60
Lseojeong merged 2 commits into
devfrom
refactor/#59

Conversation

@Lseojeong

@Lseojeong Lseojeong commented Jul 1, 2026

Copy link
Copy Markdown
Member

#️⃣연관된 이슈

체크 사항

  • UI 동작 및 레이아웃 확인
  • 불필요한 console 제거
  • 기능 정상 동작
  • 팀 컨벤션에 맞게 구현했는지
  • 관련 문서 또는 주석을 갱신했는지

📝작업 내용

Portal Root 공통 관리 구조 적용

  • layout.tsx에 앱 전역 Portal Root를 추가했습니다.
  • Portal Root id를 src/shared/constants/portal.ts에서 상수로 관리하도록 분리했습니다.
  • src/shared/ui/portal에 공통 Portal 컴포넌트를 추가해 createPortal 호출과 기본 container 선택 로직을 중앙화했습니다.

Portal 기반 컴포넌트 리팩터링

  • Surface.Portal이 직접 createPortal을 호출하지 않고 공통 Portal을 사용하도록 수정했습니다.
  • Tooltip.Content가 공통 Portal을 사용하도록 수정했습니다.
  • Modal, Tooltip 등 Portal 기반 UI가 동일한 Portal Root를 기본 렌더링 대상으로 사용하도록 정리했습니다.

스크린샷 (선택)

  • 구조 리팩터링 중심 작업으로 생략

추가한 라이브러리 (선택)

  • 없음

💬리뷰 요구사항(선택)

Summary by CodeRabbit

  • 새 기능

    • 앱 전역에서 사용할 공통 포털 렌더링이 추가되었습니다.
    • 툴팁과 표면 관련 UI가 이제 일관된 전역 포털 영역을 사용합니다.
  • 개선 사항

    • 앱 레이아웃에 포털 전용 렌더링 영역이 포함되어, 포털 기반 UI가 더 안정적으로 동작합니다.
    • 포털 대상이 없는 환경에서도 안전하게 처리되도록 기본 동작이 정리되었습니다.

@Lseojeong Lseojeong self-assigned this Jul 1, 2026
@Lseojeong Lseojeong added the ✨Feature 새로운 기능 구현 label Jul 1, 2026
@Lseojeong Lseojeong linked an issue Jul 1, 2026 that may be closed by this pull request
5 tasks
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rewrite Ready Ready Preview, Comment Jul 1, 2026 2:54pm

@Lseojeong Lseojeong added ♻️ Refactor 코드 리팩토링 and removed ✨Feature 새로운 기능 구현 labels Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🧪 테스트 결과

항목 결과
✅ Jest 테스트 success

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🚦 CI 검증 결과

항목 결과
🧠 TypeScript 타입 체크 success
🧹 ESLint 검사 success
🎨 Prettier 검사 success
🏗️ Build 검증 success

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Portal Root를 layout.tsx로 중앙화하여 PORTAL_ROOT_ID div를 추가하고, 공통 Portal 컴포넌트/타입을 새로 구현했습니다. SurfacePortalTooltipContent는 자체 createPortal 로직 대신 공통 Portal 컴포넌트를 사용하도록 리팩터링되었으며, 관련 타입과 문서 주석이 업데이트되었습니다.

Changes

Portal Root 중앙화

Layer / File(s) Summary
Portal Root 상수 및 Layout 마운트
src/shared/constants/portal.ts, src/app/layout.tsx
PORTAL_ROOT_ID 상수('portal-root')를 정의하고, RootLayout<body>에 해당 id를 가진 <div>{children} 뒤에 추가로 렌더링함.
공통 Portal 컴포넌트 및 타입 구현
src/shared/ui/portal/Portal.tsx, Portal.types.ts, index.ts
PortalContainer, PortalProps 타입을 정의하고, PORTAL_ROOT_ID 요소를 우선 사용하고 없으면 document.body로 fallback하며 서버 환경에서는 null을 반환하는 Portal 컴포넌트를 구현, createPortal로 렌더링함.
Surface 포털 위임
src/shared/ui/surface/Surface.types.ts, SurfacePortal.tsx
SurfacePortalProps.container 타입을 PortalContainer | null로 변경하고, SurfacePortal이 자체 document.body 계산 대신 공통 Portal 컴포넌트에 렌더링을 위임하도록 수정함.
Tooltip 포털 위임
src/shared/ui/tooltip/Tooltip.tsx, Tooltip.types.ts, TooltipContent.tsx
TooltipContentProps.container 타입을 PortalContainer | null로 변경하고, TooltipContent가 자체 portalContainer 계산 대신 공통 Portal 컴포넌트를 사용하도록 수정하며 JSDoc을 "앱 전역 Portal Root" 기준으로 갱신함.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RootLayout
  participant SurfacePortal
  participant TooltipContent
  participant Portal
  participant DOM

  RootLayout->>DOM: PORTAL_ROOT_ID div 렌더링
  SurfacePortal->>Portal: container prop 전달
  TooltipContent->>Portal: container prop 전달
  Portal->>DOM: PORTAL_ROOT_ID 요소 조회 (container 없을 시)
  DOM-->>Portal: 요소 존재 여부 반환
  Portal->>DOM: 없으면 document.body로 fallback
  Portal->>DOM: createPortal(children, container)
Loading

Possibly related PRs

  • Rewrite-Team/Rewrite-FE#50: SurfacePortal/Surface.types.ts의 포털 컨테이너 로직이 이 PR에서 도입된 공통 Portal 컴포넌트 및 PORTAL_ROOT_ID와 직접 맞물림.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 Portal Root를 공통 구조로 옮기는 리팩터링이라는 핵심 변경을 간결하게 잘 설명합니다.
Linked Issues check ✅ Passed layout.tsx 추가, 공통 Portal 컴포넌트 도입, 중복 portal 로직 제거, Surface와 Tooltip의 공통 Portal 사용이 이슈 요구와 부합합니다.
Out of Scope Changes check ✅ Passed 변경 내용은 모두 Portal Root 공통화와 관련된 파일들로 한정되어 있어 뚜렷한 범위 이탈은 보이지 않습니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/#59

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

@coderabbitai coderabbitai Bot left a comment

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 comments (2)
src/shared/ui/surface/Surface.types.ts (1)

57-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

container 주석에 기본 동작(Portal Root) 설명 추가 권장.

Tooltip.types.tscontainer 주석은 "생략하면 앱 전역 Portal Root를 사용합니다. null을 전달하면 portal 렌더링을 하지 않습니다."로 갱신되었지만, 이 파일의 container 주석은 여전히 "portal을 렌더링할 대상 요소입니다."로만 남아 있습니다. SurfacePortal.tsx의 함수 JSDoc은 이미 기본값 동작을 설명하고 있어, 타입 파일만 문서가 뒤처진 상태입니다. 일관성을 위해 갱신을 권장합니다.

📝 제안 diff
 interface SurfacePortalProps {
   children: ReactNode;
-  /** portal을 렌더링할 대상 요소입니다. */
+  /**
+   * portal을 렌더링할 대상 요소입니다.
+   *
+   * 생략하면 앱 전역 Portal Root를 사용하고, `null`이면 렌더링하지 않습니다.
+   */
   container?: PortalContainer | null;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/ui/surface/Surface.types.ts` around lines 57 - 61, The
SurfacePortalProps container doc is outdated and should match the actual default
behavior documented in SurfacePortal.tsx and Tooltip.types.ts. Update the
container comment in SurfacePortalProps to explain that omitting it uses the
app-wide Portal Root, and passing null disables portal rendering. Keep the
change limited to the SurfacePortalProps type documentation so the API docs stay
consistent.
src/shared/ui/portal/Portal.tsx (1)

9-15: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

컨테이너 조회를 메모이즈하는 것을 고려해 보세요.

containerundefined인 경우 매 렌더마다 getDefaultPortalContainer()document.getElementById를 다시 호출합니다. Tooltip처럼 위치 갱신 등으로 자주 리렌더링되는 컴포넌트가 이 Portal을 사용하는 경로(TooltipContent.tsx)에서는 불필요한 DOM 조회가 반복될 수 있습니다. container prop 값이 렌더 간 안정적으로 유지되는 경우가 많으므로 useMemo로 캐싱하면 이 다운스트림 리렌더링 비용을 줄일 수 있습니다.

♻️ 제안 diff
+import { useMemo } from 'react';
+
 export function Portal({ children, container }: PortalProps) {
-  const portalContainer = container === undefined ? getDefaultPortalContainer() : container;
+  const portalContainer = useMemo(
+    () => (container === undefined ? getDefaultPortalContainer() : container),
+    [container]
+  );
 
   if (!portalContainer) {
     return null;
   }
 
   return createPortal(children, portalContainer);
 }

Also applies to: 30-38

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/shared/ui/portal/Portal.tsx` around lines 9 - 15, `Portal`에서 `container`가
없을 때마다 `getDefaultPortalContainer()`가 `document.getElementById`를 다시 호출하고 있으니, 이
조회 결과를 메모이즈하도록 수정하세요. `Portal` 컴포넌트와 `getDefaultPortalContainer` 주변에서
`container` prop이 안정적일 때는 `useMemo`(또는 동등한 캐싱)로 기본 포털 컨테이너를 한 번만 계산하게 바꾸고,
`TooltipContent.tsx`처럼 자주 리렌더링되는 경로에서 불필요한 DOM 조회가 반복되지 않도록 하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/shared/ui/portal/Portal.tsx`:
- Around line 9-15: `Portal`에서 `container`가 없을 때마다
`getDefaultPortalContainer()`가 `document.getElementById`를 다시 호출하고 있으니, 이 조회 결과를
메모이즈하도록 수정하세요. `Portal` 컴포넌트와 `getDefaultPortalContainer` 주변에서 `container` prop이
안정적일 때는 `useMemo`(또는 동등한 캐싱)로 기본 포털 컨테이너를 한 번만 계산하게 바꾸고, `TooltipContent.tsx`처럼
자주 리렌더링되는 경로에서 불필요한 DOM 조회가 반복되지 않도록 하세요.

In `@src/shared/ui/surface/Surface.types.ts`:
- Around line 57-61: The SurfacePortalProps container doc is outdated and should
match the actual default behavior documented in SurfacePortal.tsx and
Tooltip.types.ts. Update the container comment in SurfacePortalProps to explain
that omitting it uses the app-wide Portal Root, and passing null disables portal
rendering. Keep the change limited to the SurfacePortalProps type documentation
so the API docs stay consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 43b437f0-7b06-4898-a49e-7d1376157322

📥 Commits

Reviewing files that changed from the base of the PR and between 6efa3b9 and 00f2303.

📒 Files selected for processing (10)
  • src/app/layout.tsx
  • src/shared/constants/portal.ts
  • src/shared/ui/portal/Portal.tsx
  • src/shared/ui/portal/Portal.types.ts
  • src/shared/ui/portal/index.ts
  • src/shared/ui/surface/Surface.types.ts
  • src/shared/ui/surface/SurfacePortal.tsx
  • src/shared/ui/tooltip/Tooltip.tsx
  • src/shared/ui/tooltip/Tooltip.types.ts
  • src/shared/ui/tooltip/TooltipContent.tsx

@Lseojeong Lseojeong merged commit 0ad8f3e into dev Jul 1, 2026
4 checks passed
@Lseojeong Lseojeong deleted the refactor/#59 branch July 1, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

♻️ Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

# [♻️ Refactor] Portal Root를 Layout으로 이동

1 participant