jQuery / Method / .resize() - 윈도우 크기 변할 때 어떤 작업하기

브라우저 창 크기가 바뀔 때 작업하려면 $(window).on("resize", 처리함수)를 사용합니다. .resize(처리함수) 단축 메서드는 jQuery 3.3부터 사용 중단 권장 상태이므로 새 코드는 .on()으로 작성합니다. 처음 화면에도 같은 처리가 필요하면 크기 변경을 기다리지 말고 갱신 함수를 직접 한 번 호출합니다.

resize 이벤트와 단축 메서드 구분하기

jQuery resize 이벤트는 창 크기 변경에 반응하는 처리기를 window에 등록합니다. .resize() 단축 메서드가 사용 중단 권장이라는 말은 resize 이벤트 자체를 쓰지 말라는 뜻이 아닙니다. 아래 예제에서 확인한 전체 빌드 3.7.1·4.0.0에는 단축 메서드도 존재하지만 .on("resize", …)로 작성하면 이벤트 등록·해제 범위를 명확히 할 수 있습니다.

네이티브 window resize 이벤트는 버블링하지 않으며, 일반 div의 폭을 바꿨다고 그 div에서 자동 발생하는 이벤트가 아닙니다. 요소 자체 크기 변화가 목적이라면 ResizeObserver를 검토합니다. 창을 조절하는 동안 이벤트가 여러 번 발생할 수 있으므로 정확히 몇 번 호출될지에 의존하지 않습니다.

초기 실행·디바운스·정리를 포함한 전체 예제

전체 코드를 UTF-8로 resize-demo.html에 저장하고 브라우저로 엽니다. 콘텐츠 전송망(CDN)의 공식 jQuery 파일 4.0.0을 읽으므로 인터넷 연결이 필요하며 설치는 없습니다. 아래 조작은 jQuery 3.7.1·4.0.0과 Windows의 Edge에서 실행했고, 뷰포트 크기를 바꿔 반응을 확인했습니다.

이 예제의 디바운스(debounce)는 연속 알림 때 이전 타이머를 취소해 마지막 알림 뒤 150ms가 지난 후에 갱신하는 방식입니다. 브라우저의 예약 상황에 따라 실제 실행은 더 늦을 수 있습니다. 600 CSS 픽셀을 기준으로 narrow·wide를 표시하며, 샘플 테두리 모양은 CSS 미디어 쿼리가 담당합니다.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Window resize with debounce and cleanup</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
    button { font: inherit; margin: 4px; max-width: 100%; }
    #sample { padding: 16px; background: #edf9f6; border: 1px solid teal; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
    @media (min-width: 600px) { #sample { border-width: 3px; } }
  </style>
</head>
<body>
  <h1>Observe window resize</h1>
  <button type="button" id="trigger">Trigger resize notification</button>
  <button type="button" id="stop">Stop listener</button>
  <button type="button" id="start">Start listener</button>
  <button type="button" id="reset">Reset counters</button>
  <div id="sample">Resize the browser or responsive preview.</div>
  <pre id="result" aria-live="polite"></pre>
  <script>
    $(function () {
      let timer = null, active = false, events = 0, renders = 0;
      function render() {
        renders++;
        const width = window.innerWidth;
        const height = window.innerHeight;
        $("#result").text(`viewport=${width} x ${height}\nmode=${width < 600 ? "narrow" : "wide"}\nlistener=${active ? "on" : "off"}\nresize events=${events}\nrenders=${renders}`);
      }
      function changed() {
        events++;
        clearTimeout(timer);
        timer = setTimeout(function () { timer = null; render(); }, 150);
      }
      function detach() {
        $(window).off("resize.windowLesson");
        clearTimeout(timer); timer = null; active = false;
      }
      function attach() {
        detach();
        $(window).on("resize.windowLesson", changed);
        active = true;
        render();
      }
      $("#trigger").on("click", function () { $(window).trigger("resize"); });
      $("#stop").on("click", function () { detach(); render(); });
      $("#start").on("click", attach);
      $("#reset").on("click", function () { detach(); events = 0; renders = 0; attach(); });
      attach();
    });
  </script>
</body>
</html>

아래는 뷰포트가 390×844 CSS 픽셀인 초기 실행 결과입니다. 본인의 창 크기에 따라 첫 줄은 다르며, 폭이 600 이상이면 mode는 wide입니다. resize events=0인데 renders=1인 것은 초기 갱신을 직접 실행했기 때문입니다.

viewport=390 x 844
mode=narrow
listener=on
resize events=0
renders=1
  1. 창 크기 또는 개발자 도구의 반응형 미리보기 크기를 바꿉니다. 조절을 멈춘 뒤 출력의 viewport가 최종 크기로 바뀌는지 확인합니다. resize events는 받은 알림 수, renders는 실제 갱신 횟수입니다.
  2. Trigger resize notification은 알림만 발생시킵니다. 창이 그대로여도 이벤트 수와 지연 갱신 횟수는 늘고 크기 값은 같아야 합니다.
  3. Stop listener를 누르면 listener=off가 됩니다. 이후 창 크기를 바꿔도 출력은 그 상태로 남습니다. 예약된 타이머도 취소하므로 뒤늦은 갱신이 나오지 않습니다.
  4. Start listener은 현재 실제 크기를 다시 읽습니다. 기존 처리기를 해제한 뒤 등록하므로 여러 번 눌러도 이 예제의 처리기는 하나입니다.
  5. Reset counters는 현재 창 크기에서 이벤트 수를 0, 갱신 수를 1로 초기화하고 다시 수신합니다. 브라우저 창 자체를 390×844로 되돌리지는 않습니다.

작업 중 멈추는 동작과 크기 자체를 되돌리는 동작을 구분합니다. 같은 크기를 유지한 채 알림 버튼만 눌러 비교하면 이벤트 발생과 실제 창 크기 변화가 별개라는 것을 확인하기 쉽습니다. 연속 알림 세 개를 한 번에 보낸 검증에서는 이벤트 3회에 지연 갱신 1회가 나왔지만, 실제 마우스 조작의 이벤트 수를 이 숫자로 고정하지 않습니다.

등록 해제와 반복 실행에서 중복 막기

코드는 resize.windowLesson이라는 네임스페이스로 자신의 처리기를 구분합니다. .off("resize.windowLesson")는 같은 창의 다른 resize 처리기까지 모두 제거하는 .off("resize")와 범위가 다릅니다. 이 이름은 예제의 고유 이름이며, 여러 컴포넌트가 있으면 서로 겹치지 않는 이름과 수명 관리가 필요합니다.

이벤트 해제만으로 이미 예약한 setTimeout이 취소되지는 않습니다. detach()는 clearTimeout(timer)도 실행합니다. attach()가 detach()를 먼저 호출해 이전 등록과 예약을 정리하고, Reset도 같은 경로를 재사용합니다. 페이지 전환이나 컴포넌트 제거 시에도 등록과 타이머를 함께 정리하는 구조를 사용합니다.

기존 .resize(handler)와 인수 없는 .resize()를 읽어야 할 때는 각각 등록과 알림이라는 차이를 기억합니다. 다음 Console 코드는 독립 객체에서 옛 단축 메서드를 확인하므로 실제 창을 바꾸거나 본문 예제의 카운터에 영향을 주지 않습니다. 새 작성 코드는 앞의 .on()·.trigger() 형태를 사용합니다.

const $target = $({});
let calls = 0;
$target.resize(function () { calls++; });
$target.resize();
console.log(`legacy shorthand calls=${calls}`);
$target.off("resize");
legacy shorthand calls=1

resize로 해결할 작업인지 판단하기

단순한 열 배치·색상·테두리 변경은 CSS 미디어 쿼리로 처리할 수 있습니다. 자바스크립트로 너비를 계속 읽고 스타일을 쓰기 전에 CSS만으로 가능한지 확인합니다. 이 예제도 테두리는 CSS, 알림 수·상태 출력은 자바스크립트로 나눴습니다.

디바운스는 조절 중 실시간으로 매번 갱신하지 않는 대신 마지막 크기를 처리합니다. 조절 중에도 계속 반응해야 하는 작업에 그대로 적용하면 의도적인 지연이 답답하게 보일 수 있으므로 필요한 반응 시점을 먼저 정합니다. 서버 요청 같은 무거운 작업을 모든 이벤트마다 실행하거나, 같은 출력을 반복해 DOM(Document Object Model, 문서 객체 모델)에 계속 추가하지 않습니다.

창이 그대로여도 사이드바·폰트·콘텐츠 때문에 요소 크기만 달라질 수 있습니다. 이 경우 window resize만으로 모든 변경을 잡을 수 없습니다. 요소를 관찰할 때는 ResizeObserver가 알려 주는 상자와 변경할 대상을 구분하고, 콜백에서 관찰 대상의 크기를 계속 바꿔 반복 알림을 만드는 구조를 피합니다. 창의 CSS 픽셀 치수와 물리 디스플레이 해상도도 같은 값으로 취급하지 않습니다.