--- id: wiki-2026-0508-escape-hatch-탈출구 title: Escape Hatch (탈출구) category: 10_Wiki/Topics status: verified canonical_id: self aliases: [React escape hatch, useRef escape hatch, useEffect escape hatch] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [react, hooks, useref, useeffect, framework-design] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: React --- # Escape Hatch (탈출구) ## 매 한 줄 > **"매 framework 가 model 외의 reality 의 reach 매 escape hatch"**. React 의 escape hatch — useRef, useEffect, flushSync, ref callback 의 declarative model 외의 imperative DOM/non-React system 의 bridge. 매 2026 React 19 (Activity API, useOptimistic) 의 도입 매 escape hatch 의 사용 빈도 의 감소 but 매 여전히 essential. ## 매 핵심 ### 매 왜 필요 - React model: declarative — UI = f(state). - 매 reality 매 imperative — DOM focus, video play, third-party (D3, Chart.js, Three.js), browser API (IntersectionObserver). - 매 escape hatch 의 controlled bridge 의 제공. ### 매 React 의 4 hatch - **useRef**: 매 mutable container 매 re-render trigger 없음. - **useEffect**: 매 commit 후 의 side-effect — 매 subscription, manual DOM, external system. - **flushSync**: 매 sync update 의 force — 매 layout measurement before paint. - **ref callback**: 매 mount/unmount lifecycle 의 ref level access. ### 매 응용 1. Third-party library integration (D3, Three.js). 2. Imperative DOM (focus, scroll, select). 3. Subscription (WebSocket, EventSource). ## 💻 패턴 ### useRef Mutable Value ```tsx function Stopwatch() { const intervalRef = useRef(null) const [time, setTime] = useState(0) const start = () => { if (intervalRef.current) return intervalRef.current = window.setInterval(() => setTime((t) => t + 1), 1000) } const stop = () => { if (intervalRef.current) clearInterval(intervalRef.current) intervalRef.current = null } return } ``` ### useRef DOM Reference ```tsx function AutoFocus() { const inputRef = useRef(null) useEffect(() => { inputRef.current?.focus() }, []) return } ``` ### useEffect External System ```tsx function ChatRoom({ roomId }: { roomId: string }) { useEffect(() => { const conn = createConnection(roomId) conn.connect() return () => conn.disconnect() // 매 cleanup essential }, [roomId]) return

Welcome to {roomId}

} ``` ### Third-party Integration (Three.js) ```tsx function ThreeScene() { const containerRef = useRef(null) useEffect(() => { const scene = new THREE.Scene() const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000) const renderer = new THREE.WebGLRenderer() renderer.setSize(800, 600) containerRef.current?.appendChild(renderer.domElement) let raf: number const animate = () => { raf = requestAnimationFrame(animate) renderer.render(scene, camera) } animate() return () => { cancelAnimationFrame(raf) renderer.dispose() containerRef.current?.removeChild(renderer.domElement) } }, []) return
} ``` ### flushSync (force sync) ```tsx import { flushSync } from "react-dom" function ScrollToBottom() { const [items, setItems] = useState([]) const listRef = useRef(null) const addItem = (text: string) => { flushSync(() => { setItems((prev) => [...prev, text]) }) // 매 flushSync 후 매 DOM 매 updated 의 guaranteed listRef.current?.scrollTo(0, listRef.current.scrollHeight) } return (/* ... */) } ``` ### Ref Callback (mount/unmount) ```tsx function ObservedItem({ onVisible }: { onVisible: () => void }) { const refCallback = useCallback((node: HTMLDivElement | null) => { if (!node) return const obs = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) onVisible() }) obs.observe(node) return () => obs.disconnect() // React 19+ cleanup }, [onVisible]) return
Observed
} ``` ### useImperativeHandle (parent 의 controlled API) ```tsx type ModalHandle = { open: () => void; close: () => void } const Modal = forwardRef((_, ref) => { const [open, setOpen] = useState(false) useImperativeHandle(ref, () => ({ open: () => setOpen(true), close: () => setOpen(false), }), []) return open ?
Modal
: null }) function App() { const modalRef = useRef(null) return } ``` ## 매 결정 기준 | 상황 | Hatch | |---|---| | Mutable value 매 no re-render | useRef | | DOM focus/scroll | useRef + useEffect | | External lib (D3/Three.js) | useEffect | | Sync DOM measurement | flushSync | | Mount/unmount lifecycle | ref callback | | Parent imperative API | useImperativeHandle | **기본값**: 매 declarative 매 first — 매 hatch 매 last resort. ## 🔗 Graph - 부모: [[React]] · [[React Hooks]] - 변형: [[Vue Composition API]] · [[Solid Signals]] - 응용: [[Three.js Integration]] · [[D3 Integration]] · [[IntersectionObserver]] - Adjacent: [[useEffect]] · [[useRef]] · [[useImperativeHandle]] ## 🤖 LLM 활용 **언제**: 매 third-party imperative library 의 React 의 integration, DOM 의 manual control. **언제 X**: 매 pure UI state — 매 useState/useReducer 의 사용. ## ❌ 안티패턴 - **useRef 매 derived state**: 매 useState 또는 useMemo 매 사용. - **useEffect 매 data fetch (cascading)**: 매 React Query / RSC 매 사용. - **Stale ref read in render**: 매 ref.current 의 render 의 read 매 X. - **Cleanup 의 omit**: 매 memory leak — 매 return 매 always. ## 🧪 검증 / 중복 - Verified (React 공식 docs "Escape Hatches" chapter, React 19 — 2026). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — React 19 escape hatch 4종 + 패턴 작성 |