August 18, 2025

πŸ•³οΈ `querySelector` 체이닝 μ§€μ˜₯: κ³Όλ„ν•œ DOM 탐색과 μ„±λŠ₯ μ €ν•˜, μœ μ§€λ³΄μˆ˜μ„± μ•…ν™”

JavaScript
React
μ„±λŠ₯
μ•„ν‚€ν…μ²˜
μ»΄ν¬λ„ŒνŠΈ
μ›Ήν‘œμ€€
λ Œλ”λ§μ „λž΅
UX

Summary

querySelector 체이닝은 DOM 탐색 μ„±λŠ₯ μ €ν•˜, μœ μ§€λ³΄μˆ˜μ„± μ•…ν™”, κΉ¨μ§€κΈ° μ‰¬μš΄ μ½”λ“œ, 가독성 μ €ν•˜λ₯Ό μ•ΌκΈ°ν•©λ‹ˆλ‹€. CSS μ„ νƒμž μ΅œμ ν™”, querySelectorAll, closest(), React Refs, 이벀트 μœ„μž„, μƒνƒœ 관리 라이브러리 ν™œμš© λ“±μ˜ λ°©λ²•μœΌλ‘œ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Why Wrong?

querySelector 체이닝은 νŠΉμ • μš”μ†Œλ₯Ό μ°ΎκΈ° μœ„ν•΄ DOM 트리λ₯Ό 깊이 νƒμƒ‰ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€. ν•„μš” μ΄μƒμœΌλ‘œ κΉŠκ±°λ‚˜ κΈ΄ 체이닝은 λ‹€μŒκ³Ό 같은 λ¬Έμ œμ μ„ μ•ΌκΈ°ν•©λ‹ˆλ‹€.

  • μ„±λŠ₯ μ €ν•˜: DOM 탐색은 λΉ„μš©μ΄ 많이 λ“œλŠ” μž‘μ—…μž…λ‹ˆλ‹€. 특히 λ³΅μž‘ν•œ νŽ˜μ΄μ§€μ—μ„œ querySelector 체이닝을 κ³Όλ„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ λΈŒλΌμš°μ €κ°€ μš”μ†Œλ₯Ό μ°ΎκΈ° μœ„ν•΄ λΆˆν•„μš”ν•œ 연산을 μˆ˜ν–‰ν•΄μ•Ό ν•˜λ―€λ‘œ μ„±λŠ₯이 μ €ν•˜λ  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μœ μ§€λ³΄μˆ˜μ„± μ•…ν™”: 체이닝이 κΈΈμ–΄μ§ˆμˆ˜λ‘ μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κ³  μˆ˜μ •ν•˜κΈ° μ–΄λ €μ›Œμ§‘λ‹ˆλ‹€. DOM ꡬ쑰가 λ³€κ²½λ˜λ©΄ μ²΄μ΄λ‹λœ μ½”λ“œ 전체λ₯Ό μˆ˜μ •ν•΄μ•Ό ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.
  • κΉ¨μ§€κΈ° μ‰¬μš΄ μ½”λ“œ: DOM ꡬ쑰에 κ°•ν•˜κ²Œ κ²°ν•©λ˜μ–΄, μ•½κ°„μ˜ 변경에도 μ½”λ“œκ°€ μ‰½κ²Œ λ§κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 가독성 μ €ν•˜: μ½”λ“œκ°€ κΈΈκ³  λ³΅μž‘ν•΄μ Έμ„œ λ‹€λ₯Έ κ°œλ°œμžκ°€ μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κΈ° μ–΄λ €μ›Œμ§‘λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 이유둜 querySelector 체이닝은 μ΅œμ†Œν™”ν•˜κ³ , 더 효율적인 DOM 탐색 λ°©λ²•μ΄λ‚˜ μ ‘κ·Ό 방식을 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

How to Fix?

λ‹€μŒκ³Ό 같은 λ°©λ²•μœΌλ‘œ querySelector 체이닝 문제λ₯Ό ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • CSS μ„ νƒμž μ΅œμ ν™”: querySelector에 μ „λ‹¬ν•˜λŠ” CSS μ„ νƒμžλ₯Ό μ΅œλŒ€ν•œ ꡬ체적으둜 μž‘μ„±ν•˜μ—¬ 탐색 λ²”μœ„λ₯Ό μ€„μž…λ‹ˆλ‹€. λΆˆν•„μš”ν•œ λ³΅μž‘ν•œ μ„ νƒμžλ₯Ό ν”Όν•˜κ³ , idλ‚˜ classλ₯Ό ν™œμš©ν•˜μ—¬ μ§μ ‘μ μœΌλ‘œ μš”μ†Œλ₯Ό μ„ νƒν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.
  • querySelectorAll ν™œμš©: μ—¬λŸ¬ μš”μ†Œλ₯Ό ν•œ λ²ˆμ— 선택해야 ν•˜λŠ” 경우, querySelectorAll을 μ‚¬μš©ν•˜μ—¬ ν•„μš”ν•œ μš”μ†Œλ§Œ μ„ νƒν•˜κ³  λ°˜λ³΅λ¬Έμ„ 톡해 μ²˜λ¦¬ν•©λ‹ˆλ‹€.
  • closest() λ©”μ„œλ“œ ν™œμš©: νŠΉμ • μš”μ†Œμ—μ„œ κ°€μž₯ κ°€κΉŒμš΄ μƒμœ„ μš”μ†Œλ₯Ό 찾을 λ•Œ closest() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 λΆˆν•„μš”ν•œ DOM 탐색을 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.
  • React Refs μ‚¬μš©: Reactμ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈ λ‚΄μ—μ„œ DOM λ…Έλ“œμ— 직접 μ ‘κ·Όν•΄μ•Ό ν•˜λŠ” 경우, useRef 훅을 μ‚¬μš©ν•˜μ—¬ Refsλ₯Ό μƒμ„±ν•˜κ³  ν•΄λ‹Ή DOM λ…Έλ“œλ₯Ό μ°Έμ‘°ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 querySelectorλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ DOM λ…Έλ“œμ— 직접 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 이벀트 μœ„μž„: 이벀트 μœ„μž„μ„ 톡해 μƒμœ„ μš”μ†Œμ— 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό λ“±λ‘ν•˜κ³ , μ΄λ²€νŠΈκ°€ λ°œμƒν•œ μš”μ†Œμ— 따라 λ‹€λ₯Έ λ™μž‘μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 κ°œλ³„ μš”μ†Œμ— 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό λ“±λ‘ν•˜λŠ” λŒ€μ‹ , μƒμœ„ μš”μ†Œμ— ν•˜λ‚˜μ˜ 이벀트 λ¦¬μŠ€λ„ˆλ§Œ λ“±λ‘ν•˜μ—¬ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μƒνƒœ 관리 라이브러리 ν™œμš©: λ³΅μž‘ν•œ μƒνƒœ 관리가 ν•„μš”ν•œ 경우, Redux, Zustand, Recoilκ³Ό 같은 μƒνƒœ 관리 라이브러리λ₯Ό ν™œμš©ν•˜μ—¬ μƒνƒœλ₯Ό 쀑앙 μ§‘μ€‘μ‹μœΌλ‘œ κ΄€λ¦¬ν•˜κ³  μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ 데이터 흐름을 효율적으둜 μ œμ–΄ν•©λ‹ˆλ‹€.

Before Code (Bad)

function handleClick() {
  const element = document.querySelector('.container .item:nth-child(2) .content .title');
  if (element) {
    element.textContent = 'Updated Title';
  }
}

After Code (Good)

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

function MyComponent() {
  const titleRef = useRef(null);

  useEffect(() => {
    if (titleRef.current) {
      titleRef.current.textContent = 'Updated Title';
    }
  }, []);

  return (
    <div className="container">
      <div className="item">
        <div className="content">
          <h2 className="title" ref={titleRef}>Original Title</h2>
        </div>
      </div>
    </div>
  );
}

export default MyComponent;