← 목록으로

아직도 직접 코딩하고 있다면 주목! 브라우저가 이미 다 해결한 9가지 오버엔지니어링 사례

2026. 4. 5.

아직도 직접 코딩하고 있다면 주목! 브라우저가 이미 다 해결한 9가지 오버엔지니어링 사례

코드와 일, 그리고 개발 철학에 대한 생각들을 정리하는 걸 즐겨 하는 10년 차 IT 실무자이자 테크 블로거입니다. 물론 깊이 있는 기술 분석도 좋아하죠. 하지만 제가 올리는, 아직 많은 분이 잘 모르는 '꿀 기능' 리스트를 여러분이 가장 좋아한다는 사실도 잘 알고 있습니다. 😉

요즘 저는 컨퍼런스 준비와 끈질긴 성능 문제와의 싸움, 그리고 다가오는 명절 휴가를 위한 작은 몸부림으로 정신없는 나날을 보내고 있습니다. 그러면서도 늘 "어떻게 하면 좀 더 효율적으로, 간결하게 코드를 짤 수 있을까?" 하는 고민을 멈추지 않죠. 이런저런 문제 해결을 위해 브라우저의 내부 동작을 파고들다 보면, 가끔 "와, 이걸 브라우저가 이미 해주고 있었네?" 하고 무릎을 탁 치게 되는 순간들이 있습니다.

오늘 준비한 내용은 바로 그런 순간들의 모음입니다. 얼마 전까지만 해도 제 머릿속에는 없었던, 하지만 이미 대부분의 모던 브라우저에서 잘 지원하는 놀라운 기능들이죠. "아, 괜히 직접 구현했었네!"라는 탄식이 절로 나올지도 모릅니다. 재미있게 봐주세요!


1. "이 코드는 나중에 실행해야지" → requestIdleCallback

처음 이 API를 접했을 땐 "굳이 왜?"라는 생각이 들었습니다. 쉽게 말해 브라우저가 한가할 때 특정 코드를 실행하게 해주는 기능인데, 이게 왜 필요할까 싶었죠.

하지만 실무에서 다양한 상황을 겪으면서 수많은 활용 사례를 발견했습니다. 예를 들어, 사용자의 페이지 행동 데이터를 수집하는 작업은 200개가 넘는 컴포넌트가 렌더링될 때 끼어들면 성능에 악영향을 줄 수 있습니다. 이런 비핵심적인 작업은 requestIdleCallback으로 미루는 게 현명하죠. 중요도가 낮은 데이터 로딩, 백그라운드 이미지 생성, 데이터 전처리 같은 작업에 안성맞춤입니다.

개발자의 수만큼이나 다양한 활용 사례가 존재한다고 봐도 무방합니다.

function trackUserScrolling() {
  console.log("User scrolled. This changes everything.");
}

if ("requestIdleCallback" in window) {
  requestIdleCallback(trackUserScrolling);
} else {
  setTimeout(trackUserScrolling, 0); // 폴백 처리
}

지원: 모던 브라우저 (역사적으로 Safari에는 없었으므로, 폴백 처리는 여전히 좋은 습관입니다.)


2. "왜 내 입력창은 하이라이트가 안 되지???" → :focus-within

특정 요소에 포커스가 갔을 때 스타일을 적용하는 건 쉽습니다. 하지만 그 요소의 부모 div를 스타일링하고 싶다면 어떨까요? 예를 들어, 입력창이 활성화되면 부모 div 테두리를 핑크색으로 바꾸고 싶을 때 말이죠. 예전에는 수십 줄의 JavaScript를 짜거나 복잡한 CSS 핵을 사용하기도 했습니다. 하지만 이제는 :focus-within 하나로 간단히 해결됩니다.

별도의 이벤트 리스너도 필요 없고, 버그 걱정도 덜 수 있습니다. 깔끔하죠.

.form-field {
  border: 1px solid #ccc;
  padding: 12px;
}

.form-field:focus-within {
  border-color: hotpink; /* 자식 요소에 포커스가 가면 부모 테두리가 핫핑크로! */
}
<div class="form-field">
  <input placeholder="의미 있는 내용을 입력하세요..." />
</div>

지원: 사실상 모든 주요 브라우저


3. "오프라인 모드를 보여주자" → navigator.onLine

PWA(Progressive Web App)를 구축해본 경험이 있다면, 사용자가 인터넷 연결을 잃었을 때(예를 들어, 산속에 있거나 엘리베이터에 탔을 때) 어떻게 처리할지가 항상 골칫거리입니다. 복잡한 if 문을 덕지덕지 붙이거나, offlineonline 이벤트를 리스닝하면 됩니다. offline일 때는 IndexedDB에 데이터를 저장하고, online으로 돌아오면 서버로 전송하는 식으로 깔끔하게 처리할 수 있죠.

window.addEventListener("offline", () => {
  alert("네트워크 연결이 끊어졌습니다. 패닉할 시간입니다!");
});

window.addEventListener("online", () => {
  alert("네트워크 연결이 돌아왔습니다. 패닉 취소!");
});

지원: 폭넓게 지원 (하지만 "온라인"이 "백엔드가 잘 동작함"을 의미하진 않습니다 😉)


4. "부드러운 애니메이션, 하지만 저주받은" → requestAnimationFrame

이런 코드는 모두 한 번쯤 봤을 겁니다.

setInterval(() => {
  element.style.left = Math.random() * 100 + "px";
}, 16);

이 코드를 보면 왠지 모르게 불길한 예감이 들지 않나요? 😅 맞습니다, 딱 봐도 랙이 걸릴 것 같은 코드죠. 다행히 브라우저의 리페인트 주기와 동기화되어 실제 움직임을 매우 부드럽게 만들어주는 requestAnimationFrame이 있습니다.

function animate() {
  element.style.transform = `translateX(${Date.now() % 300}px)`;
  requestAnimationFrame(animate); // 다음 프레임에 애니메이션 재개 요청
}

requestAnimationFrame(animate); // 애니메이션 시작

지원: 모든 브라우저


5. "이 카드는 여기에서만 적응해야 해" → Container Queries

이 기능은 제가 경력의 어느 시점에서 CSS를 거의 작성하지 않게 되었을 때 나타나서, 마치 반칙처럼 느껴지기도 합니다. (물론 2026년에 CSS를 배우는 것이 시간 낭비일까요? 같은 글을 쓸 때처럼 가끔씩은 여전히 CSS와 씨름합니다만.)

하지만 제가 한창 CSS를 많이 쓰던 시절이 있었죠. 그때 뷰포트 전체가 아닌 특정 요소에만 미디어 쿼리를 적용할 수 있었다면 얼마나 좋았을까! 정말 간절히 바라던 기능이었습니다. 제가 실무에서 반응형 UI를 만들 때 이걸 얼마나 간절히 바랐는지 모릅니다. 예전에는 뷰포트 기준으로만 잡아야 해서 컴포넌트 내부에서 유연하게 대응하기가 정말 어려웠거든요. 이제는 드디어 가능합니다. 컴포넌트 자체가 '스스로를 인식'하게 되었고, 덕분에 개발자는 커피 한잔의 여유를 가질 수 있게 되었습니다.

.card-wrapper {
  container-type: inline-size; /* 컨테이너 쿼리를 적용할 기준 지정 */
}

.card {
  display: grid;
}

@container (min-width: 400px) { /* 부모 컨테이너 너비가 400px 이상일 때 */
  .card {
    grid-template-columns: 1fr 2fr;
  }
}

지원: 모던 브라우저 (필요하다면 폴백 추가)


6. "랜덤 ID, 뭐가 문제겠어?" → crypto.getRandomValues

const id = Math.random().toString(36).slice(2);

버그는 이렇게 태어나는 겁니다. 얼핏 보면 "이만하면 됐지" 싶은 AliExpress표 암호화처럼 보이고, 동작하는 듯하다가 어느 순간 문제가 터지죠. 우선, 내부적으로 어떤 일이 일어나는지 정확히 알 수 없는 엔진 구현에 의존합니다. 특정 패턴이 나올 가능성도 충분하고, ID를 많이 생성할수록 중복이 발생할 확률은 기하급수적으로 늘어납니다.

다행히 이제는 간단하고 안전한 네이티브 솔루션이 있습니다. 완벽한 은 탄환은 아니지만, crypto.getRandomValues는 훨씬 뛰어난 엔트로피를 제공하며, 이상한 패턴 없이 충돌 가능성을 극적으로 줄여줍니다. 브라우저가 알아서 제대로 처리해주는 거죠.

const bytes = new Uint8Array(8);
crypto.getRandomValues(bytes); // 암호학적으로 안전한 난수 생성

const id = Array.from(bytes)
  .map(b => b.toString(16).padStart(2, "0"))
  .join("");

console.log("Secure-ish ID:", id);

지원: 폭넓게 지원


7. "모달이 필요해요" → <dialog>

솔직히 브라우저가 "알았어, 너희가 그렇게 원하는 모달 여기 있어!"라고 나서준 건 정말 기분 좋은 일입니다. 사용자들이 그렇게 좋아하는 단순한 다이얼로그 하나를 열기 위해 더 이상 12KB짜리 라이브러리를 설치할 필요가 없습니다. 이 <dialog> 요소는 기본적으로 접근성도 좋으니, 그야말로 일석이조죠. 제가 모달 관련 라이브러리의 무거운 번들 사이즈 때문에 씨름했던 지난날을 생각하면, 진작에 나왔어야 할 기능이라고 봅니다.

<dialog id="modal">
  <p>금요일에 배포하는 게 확실합니까?</p>
  <button onclick="modal.close()">취소</button>
  <button onclick="alert('행운을 빕니다 😬')">배포</button>
</dialog>

<button onclick="modal.showModal()">모달 열기</button>

지원: 모던 브라우저


8. "음성 입력이 되면 멋질 텐데…" → Speech API

음성 인식을 위해 벌써부터 transformers.js 같은 라이브러리를 설치하고 계신가요? 잠시 진정하세요. 브라우저도 이 기능을 가지고 있습니다. 음… 적어도 Chromium 기반 브라우저는 말이죠. 😉 따라서 Chrome, Edge 또는 이와 유사한 브라우저 사용을 "권장"할 수 있다면 충분히 활용할 수 있습니다. 개인적으로는 아직 프로덕션 환경에 바로 적용하기엔 조심스럽지만, 데모 용도로는 충분히 매력적입니다.

const SpeechRecognition =
  window.SpeechRecognition || window.webkitSpeechRecognition;

if (SpeechRecognition) {
  const recognition = new SpeechRecognition();

  recognition.onresult = e => {
    console.log("당신이 말했습니다:", e.results[0][0].transcript);
  };

  recognition.start();
}

지원: 주로 Chromium 기반 브라우저


9. "이 CSS가 터질까?" → @supports

여기 "내 컴퓨터에서는 잘 되는데?"라는 고전적인 문제에 대한 현대적인 해결책이 있습니다(적어도 CSS에서는요). 더 이상 특정 CSS 속성이 레이아웃을 망가뜨릴지 추측할 필요가 없습니다. 그냥 @supports 안에 감싸세요. 작은 함정이 있다면, 지원 범위가 매우 넓긴 하지만 문자 그대로 모든 곳에서 지원되는 것은 아니라는 점입니다. 아이러니하게도, @supports 자체를 @supports로 감쌀 수도 있겠죠!

.card {
  background: white;
}

@supports (backdrop-filter: blur(10px)) { /* backdrop-filter 지원 여부 확인 */
  .card {
    backdrop-filter: blur(10px);
    background: rgba(255, 255, 255, 0.6);
  }
}

지원: 매우 우수


⚠️ 오해는 금물입니다

물론 라이브러리는 훌륭합니다. 때로는 라이브러리가 정말로 필요할 때도 분명 있습니다. 하지만 간혹 브라우저가 이미 몇 년 전에 해결해놓은 기능을 위해 불필요한 의존성을 추가하고 있지는 않은지 돌아볼 필요가 있습니다. 무언가를 설치하기 전에 잠시 멈춰 서서 스스로에게 물어보세요 (아니면 구글에 검색해보세요): "혹시 브라우저가 나보다 더 똑똑한 해결책을 가지고 있는 건 아닐까?" 때로는 그 답이 '그렇다'일 때가 있습니다. 그리고 그 사실은… 전혀 문제 될 게 없죠. 😉


원문: https://dev.to/sylwia-lask/9-things-youre-overengineering-the-browser-already-solved-them-o99 수집일: 2026-04-05 05:55:12