Files
2nd/10_Wiki/Topics/Domain_Programming/Frontend/반응형 윈도우 리사이즈(Resize) 이벤트 처리.md
T
Antigravity Agent c24165b8bc refactor(topics): 멀티 에이전트용 지식 재편 — _Common(공통 기본기) + Domain_* 구조
에이전트 8종(대화형/프로그래머 C·S/디자이너/설계자/기획자/QA/PD/PM)에게
[공통 기본 능력 + 롤별 Specialty] 2층으로 지식을 주입하기 위한 재분류.
문서 내용·포맷은 무수정, 폴더 이동만 (6,372개 문서 수 보존 확인).

- Topic_Programming → Domain_Programming (내부 구조 보존)
- Topic_Graphic → Domain_Design
- Topic_Business → Domain_Product
- Topic_General → Domain_General
- _Common 신설: Math(구 Topic_Math_Specialty), Reasoning(구 General/From_Thinking & Reasoning),
  Reasoning_Creativity(구 General/From_창의성), Communication(Poetic_Blog_Writing + From_writing)
- 타 도메인의 From_* 폴더는 유지 (출처 표기일 뿐, 이미 도메인에 맞게 분류된 문서)
- 빈 폴더 정리 (memory/procedures)
- 에이전트→폴더 매핑은 workspace의 .astra/agent-knowledge-map.json (9개 에이전트)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 11:05:56 +09:00

6.8 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-반응형-윈도우-리사이즈-resize-이벤트-처리 반응형 윈도우 리사이즈(Resize) 이벤트 처리 10_Wiki/Topics verified self
Resize Event
ResizeObserver
window resize
윈도우 리사이즈
none A 0.9 applied
frontend
browser-events
performance
react
2026-05-10 pending
language framework
typescript react

반응형 윈도우 리사이즈(Resize) 이벤트 처리

매 한 줄

"매 resize 의 fire 매 cheap 매 listener 매 expensive — debounce 또는 ResizeObserver". window resize 매 60fps+ 의 fire — 매 naive listener 매 layout thrash. 매 modern (2026) 의 ResizeObserver 의 element-level + native browser-throttled API.

매 핵심

매 두 종류 의 API

  • window.addEventListener('resize', ...) — viewport-level, fires 매 every pixel change.
  • ResizeObserver — element-level, browser-throttled, observes contentBox/borderBox.

매 throttle vs debounce

  • Debounce — fire only N ms 후 last event (e.g., resize stops).
  • Throttle — fire at most every N ms (continuous fire).
  • Resize: 보통 debounce (final size 만 needed) 또는 throttle (live preview).

매 응용

  1. Responsive breakpoint — JS-driven layout.
  2. Canvas/Chart resize — re-draw on size change.
  3. Virtualized list — recalculate row count.
  4. Modal positioning — re-center.

💻 패턴

1. ResizeObserver (modern, preferred)

const ro = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { width, height } = entry.contentRect;
    console.log('resized:', width, height);
  }
});

const el = document.querySelector('.box')!;
ro.observe(el);
// cleanup
// ro.unobserve(el); ro.disconnect();

2. React hook: useResizeObserver

import { useEffect, useState, useRef } from 'react';

export function useResizeObserver<T extends HTMLElement>() {
  const ref = useRef<T>(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(([entry]) => {
      const { width, height } = entry.contentRect;
      setSize({ width, height });
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);

  return [ref, size] as const;
}

// usage
function Card() {
  const [ref, { width }] = useResizeObserver<HTMLDivElement>();
  return <div ref={ref}>{width < 400 ? <Compact /> : <Full />}</div>;
}

3. Window resize with debounce

function debounce<T extends (...args: any[]) => any>(fn: T, ms = 200): T {
  let timer: ReturnType<typeof setTimeout> | null = null;
  return ((...args: Parameters<T>) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => fn(...args), ms);
  }) as T;
}

const onResize = debounce(() => {
  console.log('done resizing:', window.innerWidth);
}, 200);

window.addEventListener('resize', onResize);

4. React useWindowSize hook

import { useEffect, useState } from 'react';

export function useWindowSize() {
  const [size, setSize] = useState({
    width: typeof window === 'undefined' ? 0 : window.innerWidth,
    height: typeof window === 'undefined' ? 0 : window.innerHeight,
  });

  useEffect(() => {
    let raf = 0;
    const onResize = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        setSize({ width: window.innerWidth, height: window.innerHeight });
      });
    };
    window.addEventListener('resize', onResize, { passive: true });
    return () => {
      window.removeEventListener('resize', onResize);
      cancelAnimationFrame(raf);
    };
  }, []);

  return size;
}

5. Throttle with rAF

function rafThrottle<T extends (...args: any[]) => any>(fn: T): T {
  let raf = 0;
  let lastArgs: Parameters<T>;
  return ((...args: Parameters<T>) => {
    lastArgs = args;
    if (raf) return;
    raf = requestAnimationFrame(() => {
      fn(...lastArgs);
      raf = 0;
    });
  }) as T;
}

window.addEventListener('resize', rafThrottle(() => {
  // runs at most once per frame
  redrawCanvas();
}));

6. matchMedia (breakpoint detection)

const mql = window.matchMedia('(min-width: 768px)');

function onChange(e: MediaQueryListEvent) {
  console.log('is desktop:', e.matches);
}
mql.addEventListener('change', onChange);
console.log('initial desktop:', mql.matches);

7. Canvas resize (HiDPI handling)

function fitCanvasToDPR(canvas: HTMLCanvasElement) {
  const dpr = window.devicePixelRatio || 1;
  const rect = canvas.getBoundingClientRect();
  canvas.width = rect.width * dpr;
  canvas.height = rect.height * dpr;
  const ctx = canvas.getContext('2d')!;
  ctx.scale(dpr, dpr);
  return ctx;
}

const ro = new ResizeObserver(([entry]) => {
  const canvas = entry.target as HTMLCanvasElement;
  fitCanvasToDPR(canvas);
  draw(canvas.getContext('2d')!);
});
ro.observe(canvas);

매 결정 기준

상황 Approach
Element-level size watch ResizeObserver (browser-throttled, no manual debounce).
Window-level (viewport) window.addEventListener('resize') + debounce.
Smooth visual feedback during drag requestAnimationFrame throttle.
Breakpoint change only matchMedia (fires on threshold cross only).
Canvas / chart redraw ResizeObserver + DPR scaling.

기본값: ResizeObserver 의 element-level. matchMedia 의 breakpoint. window resize 의 only viewport-wide concerns + debounce 200ms.

🔗 Graph

🤖 LLM 활용

언제: dynamic layout, canvas/chart, virtualized lists, modal repositioning, JS-driven breakpoint. 언제 X: pure CSS responsive 매 sufficient — JS resize handler 매 unneeded.

안티패턴

  • No debounce/throttle on window.resize: 60+ fires/sec 매 layout thrash.
  • offsetWidth in resize handler without rAF: forced sync layout.
  • Forgetting cleanup: memory leak (especially in React without removeEventListener).
  • Polling window.innerWidth: use event-driven approach.
  • Heavy work synchronously: canvas full redraw 매 each event — defer/batch.

🧪 검증 / 중복

  • Verified (MDN ResizeObserver 2026, web.dev resize patterns, React hooks docs).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — full content with ResizeObserver/debounce/rAF/matchMedia patterns