고급 컴포넌트 패턴 (Portals & forwardRef)
UI의 제약을 넘어서는 고급 렌더링 패턴
부모 컴포넌트의 DOM 계층 구조를 벗어나 렌더링하는 createPortal을 활용한 모달(Modal) 구현법과, 부모 컴포넌트가 자식 커스텀 컴포넌트 내부의 DOM 요소에 직접 접근할 수 있게 해주는 forwardRef의 활용법을 다룹니다.
🚪 createPortal: z-index 지옥 탈출
모달이나 툴팁을 렌더링할 때, 부모 컨테이너의
overflow: hidden이나 z-index에 막혀 화면에 제대로 표시되지 않는 경우가 많습니다. createPortal을 사용하면 논리적인 React 트리 구조는 유지하면서, 실제 DOM은 최상단 <body> 직속으로 빼내어 렌더링하므로 이러한 CSS 충돌을 완벽히 해결할 수 있습니다.🎯 forwardRef: 컴포넌트를 관통하는 참조
부모 컴포넌트에서 커스텀 입력창 컴포넌트에
ref를 전달하고 싶을 때, 기본적으로 커스텀 컴포넌트는 ref를 받지 못합니다. forwardRef로 자식 컴포넌트를 감싸주면 부모가 넘긴 ref를 내부의 실제 <input> 등의 DOM에 바로 꽂아넣어 포커스 제어 등 직접적인 조작이 가능해집니다.import React, { useRef } from 'react';
import CustomInput from './CustomInput';
import Modal from './Modal';
export default function App() {
const inputRef = useRef(null);
const focusInput = () => {
// 부모 컴포넌트에서 자식 컴포넌트 내부의 DOM 함수(focus)를 직접 호출
inputRef.current?.focus();
};
return (
<div style={{ padding: '20px', background: '#f8fafc', borderRadius: '8px' }}>
<CustomInput ref={inputRef} label="사용자 이름" />
<button
onClick={focusInput}
style={{ marginTop: '16px', background: '#3b82f6', color: 'white', padding: '8px 16px', border: 'none', borderRadius: '4px', cursor: 'pointer' }}>
포커스 이동하기
</button>
<Modal isOpen={true}>
<h2 style={{marginTop: 0}}>이것은 포탈로 열린 모달입니다!</h2>
</Modal>
</div>
);
}