서드파티 스크립트 최적화: 코어 웹 바이탈을 망치는 외부 스크립트 길들이기

94%의 웹사이트가 쓰는 서드파티 스크립트, LCP·INP·CLS를 얼마나 망치고 있을까요? async/defer부터 Partytown, Cloudflare Zaraz, 서버 사이드 태깅까지 실전에서 검증된 7가지 최적화 전략을 코드 예제와 함께 정리했습니다.

웹사이트에 Google Analytics, Facebook Pixel, 채팅 위젯, 광고 스크립트를 하나씩 추가할 때마다 성능이 조금씩 느려지는 걸 느끼셨나요? 솔직히 저도 처음엔 "스크립트 몇 개 더 넣는 게 뭐 대수야"라고 생각했습니다. 그런데 Chrome 팀의 연구 결과를 보면 좀 충격적인데요, 분석 대상 웹사이트의 절반 이상에서 서드파티 스크립트가 메인 스레드를 최대 1.6초까지 차단하고 있었습니다. 94% 이상의 웹사이트가 서드파티 스크립트를 쓰고 있으니까, 사실상 거의 모든 사이트의 문제라고 봐야 합니다.

그리고 이 스크립트들이 "약간 느려지는" 정도가 아닙니다. 코어 웹 바이탈(Core Web Vitals) 세 가지 지표 — LCP, INP, CLS — 전부 다 건드려요. 2026년 현재 Google이 페이지 경험 신호의 가중치를 꽤 높인 상태라서, 콘텐츠 품질이 비슷하다면 코어 웹 바이탈이 좋은 페이지가 검색 순위에서 확실히 유리합니다.

이 글에서는 서드파티 스크립트가 성능을 어떻게 갉아먹는지 하나씩 뜯어보고, 2026년 기준 최신 도구와 기법을 활용한 실전 최적화 전략을 코드 예제와 함께 정리합니다. Chrome DevTools의 "Dim 3rd parties" 기능부터 Partytown, Cloudflare Zaraz, 서버 사이드 태깅까지 — 제가 실무에서 써보면서 효과를 확인한 방법들을 거의 다 담았습니다.

서드파티 스크립트가 코어 웹 바이탈에 미치는 영향

서드파티 스크립트는 여러분이 직접 작성하지 않았지만 사이트에 임베드된 코드입니다. 분석 도구, 광고 네트워크, 소셜 미디어 위젯, 라이브 채팅, A/B 테스팅 도구 같은 것들이죠. 이것들이 코어 웹 바이탈 각 지표에 어떤 영향을 주는지 하나씩 살펴보겠습니다.

LCP(Largest Contentful Paint)에 미치는 영향

서드파티 스크립트는 크게 두 가지 경로로 LCP를 악화시킵니다. 첫째, 네트워크 대역폭과 CPU를 놓고 경쟁합니다. 브라우저가 분석 스크립트 다운로드하고 실행하느라 바쁘면, 정작 사용자에게 보여줘야 할 히어로 이미지나 주요 콘텐츠 로딩이 뒤로 밀리거든요. 둘째, Google Tag Manager(GTM) 하나만으로도 렌더링을 최대 1,730ms — 약 1.7초 — 차단할 수 있습니다. LCP 기준이 2.5초인데 GTM 하나가 그 시간의 70%를 잡아먹는다니, 생각보다 심각하죠.

INP(Interaction to Next Paint)에 미치는 영향

개인적으로 서드파티 스크립트가 가장 치명적인 영향을 미치는 지표가 INP라고 봅니다. JavaScript가 기본적으로 메인 스레드에서 돌아가니까, 메인 스레드가 바쁘면 사용자 클릭이나 키보드 입력 응답이 당연히 늦어집니다.

특히 위험한 타이밍이 있는데, 페이지 로드 직후입니다. 주요 콘텐츠가 화면에 그려져서 사용자는 이미 클릭을 시작하는데, 백그라운드에서 서드파티 스크립트들이 아직도 메인 스레드를 점유하고 있으면 모든 상호작용이 버벅거립니다. Zendesk 같은 채팅 위젯은 말풍선 아이콘 하나 렌더링하려고 500KB 이상의 JavaScript를 로드해요. 아이콘 하나에 500KB라니... (이건 좀 과하다고 봅니다)

CLS(Cumulative Layout Shift)에 미치는 영향

서드파티 스크립트가 동적으로 콘텐츠를 삽입하면 레이아웃 시프트가 발생합니다. 광고 배너가 갑자기 튀어나와서 아래 콘텐츠를 밀어내거나, 쿠키 동의 배너가 자리 잡기도 전에 로드되거나, 채팅 위젯이 느닷없이 페이지 하단에 나타나는 경우가 대표적이죠. 다들 한 번쯤 겪어보셨을 겁니다.

문제 스크립트 식별하기: 감사(Audit) 방법

최적화하려면 당연히 뭐가 문제인지부터 파악해야 합니다. 어떤 서드파티 스크립트가 성능 비용을 가장 많이 발생시키는지 찾아내는 게 첫 단계예요. 다행히 2026년 Chrome DevTools가 이 부분에서 상당히 좋아졌습니다.

Chrome DevTools Performance 패널

Performance 패널의 "Dim 3rd parties" 기능은 서드파티 스크립트 분석할 때 진짜 유용합니다. 이 옵션을 켜면 퍼포먼스 트레이스에서 서드파티 관련 이벤트가 회색으로 처리되고, 퍼스트파티 이벤트만 강조돼요. 그래서 성능 문제가 내 코드 때문인지, 외부 스크립트 때문인지 한눈에 구분됩니다.

Summary 탭에서 1st / 3rd party 테이블을 보면 전송 크기와 메인 스레드 시간이 퍼스트파티/서드파티별로 깔끔하게 분류됩니다. 서드파티 엔티티 위에 마우스를 올리면 트레이스에서 관련 이벤트가 하이라이트되는 것도 꽤 편하고요.

Insights 사이드바의 3rd parties insight도 한번 확인해보세요. 리소스와 CPU 활동을 퍼스트/서드파티별로 나눠서, 어떤 서드파티가 가장 많은 시간과 리소스를 잡아먹는지 바로 알 수 있습니다.

Lighthouse "Reduce the impact of third-party code" 감사

Lighthouse는 서드파티 스크립트의 메인 스레드 차단 시간이 250ms를 초과하면 이 감사 항목을 트리거합니다. 모든 서드파티를 목록으로 쭉 보여주면서, 각 스크립트의 전송 크기랑 메인 스레드 차단 시간을 표시해줘요. 총 JavaScript 실행 시간이 2초 넘으면 경고, 3.5초 넘으면 실패로 뜹니다.

# Lighthouse CLI로 서드파티 스크립트 감사 실행
npx lighthouse https://example.com \
  --only-audits=third-party-summary,bootup-time,mainthread-work-breakdown \
  --output=json \
  --output-path=./audit-results.json

Chrome DevTools Coverage 탭

Coverage 탭(Ctrl+Shift+P → "Show Coverage")은 각 스크립트에서 실제로 실행된 코드와 안 된 코드의 비율을 보여줍니다. 서드파티 스크립트의 미사용 바이트 비율이 높다면, 그 스크립트가 쓸데없이 많은 코드를 끌고 들어온다는 뜻입니다. 이 정보를 가지고 지연 로딩 적용할지, 아예 빼버릴지 판단하면 됩니다.

WebPageTest의 서드파티 분석

WebPageTest에서 테스트 돌린 다음, "Third Parties" 탭을 보면 각 서드파티의 요청 수, 바이트 수, CPU 시간을 확인할 수 있습니다. 여기서 진짜 유용한 건 "Block" 기능인데요, 특정 서드파티 도메인을 차단한 상태에서 테스트를 돌릴 수 있어서, 해당 스크립트를 뺐을 때 성능이 얼마나 좋아지는지 미리 예측할 수 있습니다. 저는 최적화 제안할 때 이 비교 데이터를 항상 뽑아서 같이 보여주는 편이에요.

최적화 전략 1: async와 defer로 로딩 제어하기

가장 기본적이면서도 바로 효과를 볼 수 있는 전략입니다. 기본적으로 <script> 태그는 동기적으로 로드돼서 HTML 파싱을 차단하는데, asyncdefer 속성을 쓰면 이 차단을 막을 수 있습니다.

<!-- 동기 로딩 (나쁨): HTML 파싱을 완전히 차단 -->
<script src="https://example.com/analytics.js"></script>

<!-- async: 다운로드는 비동기, 다운로드 완료 즉시 실행 (파싱 중단) -->
<script async src="https://example.com/analytics.js"></script>

<!-- defer: 다운로드는 비동기, DOM 파싱 완료 후 실행 -->
<script defer src="https://example.com/analytics.js"></script>

그래서 뭘 써야 하냐면 — 대부분의 서드파티 스크립트에는 defer가 더 안전합니다. DOM 파싱 완료 후에 실행되니까 실행 순서가 보장되고, DOM 요소에 접근하는 스크립트도 문제없이 돌아가거든요. async는 실행 순서가 보장 안 되므로, 다른 스크립트와 의존성이 없는 독립적인 스크립트(분석, 광고 픽셀 같은 것들)에 쓰는 게 맞습니다.

최적화 전략 2: 지연 로딩으로 초기 부하 줄이기

async/defer를 적용해도 스크립트는 어차피 페이지 로드 중에 다운로드됩니다. 당장 필요 없는 스크립트라면? 아예 나중에 로드하는 게 훨씬 효과적이에요.

사용자 상호작용 기반 로딩

채팅 위젯이나 댓글 시스템처럼 사용자가 실제로 필요로 할 때만 쓰는 스크립트는, 상호작용을 감지한 뒤에 로드하면 됩니다. 제가 실무에서 이 패턴 하나만 적용해도 LCP가 눈에 띄게 개선되는 경우를 여러 번 봤습니다.

// 사용자가 처음 스크롤, 클릭, 터치할 때 비필수 스크립트 로드
function loadDeferredScripts() {
  // Google Analytics
  const gaScript = document.createElement('script');
  gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXX';
  gaScript.async = true;
  document.head.appendChild(gaScript);

  // 채팅 위젯
  const chatScript = document.createElement('script');
  chatScript.src = 'https://widget.example.com/chat.js';
  chatScript.defer = true;
  document.head.appendChild(chatScript);
}

// 첫 번째 상호작용에서만 실행
const events = ['scroll', 'click', 'touchstart', 'keydown'];
function onFirstInteraction() {
  loadDeferredScripts();
  events.forEach(e =>
    document.removeEventListener(e, onFirstInteraction)
  );
}
events.forEach(e =>
  document.addEventListener(e, onFirstInteraction, { once: true })
);

Intersection Observer 기반 로딩

페이지 하단에 있는 소셜 공유 버튼이나 댓글 위젯은 사용자가 거기까지 스크롤했을 때 로드하면 충분합니다.

// 특정 요소가 뷰포트에 보일 때 스크립트 로드
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const script = document.createElement('script');
      script.src = entry.target.dataset.scriptSrc;
      document.head.appendChild(script);
      observer.unobserve(entry.target);
    }
  });
}, { rootMargin: '200px' }); // 200px 전에 미리 로드 시작

document.querySelectorAll('[data-lazy-script]').forEach(el => {
  observer.observe(el);
});

requestIdleCallback 활용

브라우저가 놀고 있을 때(유휴 상태일 때) 스크립트를 로드하면 중요한 렌더링 작업을 방해하지 않습니다. 간단하지만 효과가 꽤 좋은 방법이에요.

// 브라우저 유휴 시간에 비필수 스크립트 로드
if ('requestIdleCallback' in window) {
  requestIdleCallback(() => {
    loadNonCriticalScripts();
  }, { timeout: 5000 }); // 최대 5초 후 강제 실행
} else {
  // fallback: 3초 후 로드
  setTimeout(loadNonCriticalScripts, 3000);
}

최적화 전략 3: Facade 패턴으로 초기 비용 제거하기

Facade(퍼사드) 패턴은 제가 특히 좋아하는 기법입니다. 무거운 서드파티 위젯 대신 가벼운 정적 대체물을 먼저 보여주고, 사용자가 실제로 클릭하거나 상호작용할 때만 진짜 위젯을 로드하는 방식이에요. YouTube 임베드, 라이브 채팅, 소셜 공유 버튼에 특히 잘 먹힙니다.

YouTube 임베드 퍼사드 예시

YouTube <iframe> 하나가 약 600KB 이상의 리소스를 로드한다는 거 아시나요? 블로그 글에 영상 3개만 넣어도 거의 2MB입니다. 퍼사드를 쓰면 사용자가 재생 버튼을 누르기 전까지 초기 비용이 거의 0에 가까워집니다.

<!-- Facade: 클릭 전에는 썸네일만 표시 -->
<div class="youtube-facade"
     data-video-id="dQw4w9WgXcQ"
     style="position:relative; cursor:pointer; max-width:640px; aspect-ratio:16/9;">
  <img loading="lazy"
       src="https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"
       alt="동영상 썸네일"
       style="width:100%; height:100%; object-fit:cover;">
  <!-- 재생 버튼 오버레이 -->
  <div style="position:absolute; inset:0; display:flex; align-items:center;
              justify-content:center;">
    <svg width="68" height="48" viewBox="0 0 68 48">
      <path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/>
      <path d="M 45,24 27,14 27,34" fill="white"/>
    </svg>
  </div>
</div>

<script>
document.querySelectorAll('.youtube-facade').forEach(facade => {
  facade.addEventListener('click', function() {
    const videoId = this.dataset.videoId;
    const iframe = document.createElement('iframe');
    iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
    iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture';
    iframe.allowFullscreen = true;
    iframe.style.cssText = 'width:100%; height:100%; position:absolute; inset:0; border:0;';
    this.innerHTML = '';
    this.style.position = 'relative';
    this.appendChild(iframe);
  }, { once: true });
});
</script>

같은 원리로 라이브 채팅 위젯(Intercom, Zendesk, Drift 등)도 가벼운 채팅 아이콘 버튼만 먼저 띄워놓고, 클릭했을 때만 실제 위젯 스크립트를 로드할 수 있습니다. 이거 적용하면 체감 속도가 확 달라져요.

최적화 전략 4: Partytown으로 Web Worker에서 실행하기

Partytown은 서드파티 스크립트를 메인 스레드가 아닌 Web Worker에서 실행하는 오픈소스 라이브러리입니다. Builder.io 팀이 만들었고, 핵심 아이디어는 단순한데 — 메인 스레드를 퍼스트파티 코드 전용으로 확보하자는 거죠.

동작 원리

Partytown은 type="text/partytown" 속성이 달린 스크립트의 메인 스레드 실행을 비활성화하고, 해당 스크립트를 Web Worker 내부에서 Blob을 통해 실행합니다. Worker 안에서 DOM이나 전역 객체에 접근해야 할 때는 전용 Proxy 객체를 쓰고, 동기 통신이 필요하면 동기 HTTP 요청과 Service Worker를 조합해서 처리합니다. (구현 방식이 꽤 영리한데, 관심 있으면 GitHub 소스를 한번 읽어보시는 것도 추천드립니다)

적용 방법

<!-- 1. Partytown 스니펫을 <head>에 추가 -->
<script src="/~partytown/partytown.js"></script>

<!-- 2. 서드파티 스크립트에 type="text/partytown" 추가 -->
<!-- 기존 (메인 스레드에서 실행) -->
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"></script>

<!-- Partytown 적용 (Web Worker에서 실행) -->
<script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"></script>

Next.js에서 Partytown 적용

// next.config.js에 설정은 불필요 — Next.js 내장 지원
// pages/_app.js 또는 app/layout.js
import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <Script
          src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"
          strategy="worker" // Partytown을 통해 Web Worker에서 실행
        />
        <Script id="gtag-init" strategy="worker">
          {`
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', 'G-XXXXX');
          `}
        </Script>
      </head>
      <body>{children}</body>
    </html>
  );
}

실제 성능 개선 사례

Partytown 적용 전후 벤치마크에서 Lighthouse Performance 점수가 약 70에서 99로 뛴 사례가 있습니다. "Minimize main-thread work"랑 "Reduce the impact of third-party code" 권장사항이 둘 다 사라졌다고 해요. Web Worker가 서드파티 작업을 별도 스레드에서 처리하니까, 메인 스레드는 렌더링과 사용자 상호작용에만 집중할 수 있는 거죠. 숫자만 보면 꿈같은 결과인데, 실제로 적용해보면 체감이 됩니다.

주의사항

다만 Partytown은 아직 베타 상태입니다. 모든 서드파티 스크립트와 호환되지는 않고요, 특히 GTM을 통해 다른 서드파티를 체이닝하는 경우에 꽤 복잡해질 수 있습니다. 적용 전에 반드시 충분히 테스트하세요. DOM 접근이 잦은 스크립트는 오히려 성능이 나빠질 수도 있으니까 맹목적으로 전부 넘기면 안 됩니다.

최적화 전략 5: Cloudflare Zaraz로 엣지에서 실행하기

Partytown이 클라이언트 쪽에서 문제를 풀려는 거라면, Cloudflare Zaraz는 아예 서버(엣지) 쪽에서 풀어버리는 접근입니다. Zaraz는 서드파티 스크립트를 브라우저에서 돌리지 않고, Cloudflare 엣지 네트워크(Workers)에서 실행합니다.

Zaraz의 장점

  • 코드 변경 불필요: Cloudflare 대시보드에서 설정만 몇 번 만지면 됩니다. 개발자 리소스가 부족할 때 특히 좋아요.
  • 극적인 성능 개선: Instacart 사례를 보면, Total Blocking Time이 500ms에서 0ms로, Time to Interactive가 63% 개선, JavaScript 크기가 63% 줄었습니다.
  • 개인정보 보호: Managed Components와 DLP(Data Loss Prevention)로 서드파티에 전송되는 데이터를 통제할 수 있습니다.
  • 무료 티어 제공: 월 100만 이벤트까지 무료입니다. 월 20~30만 페이지뷰 규모 사이트는 돈 한 푼 안 내고 쓸 수 있어요.

Partytown vs Zaraz 선택 기준

이미 Cloudflare를 쓰고 있고 코드 안 건드리고 빠르게 적용하고 싶다면 Zaraz가 답입니다. Cloudflare를 안 쓰거나, 프레임워크 레벨에서 세밀하게 컨트롤하고 싶다면 Partytown이 맞고요. 둘 다 장단점이 뚜렷하니까 상황에 맞게 고르시면 됩니다.

최적화 전략 6: 셀프 호스팅과 리소스 힌트

셀프 호스팅

서드파티 스크립트를 직접 자기 서버나 CDN에서 호스팅하면 의외로 이점이 많습니다. DNS 조회 시간이 없어지고, 캐시 헤더를 마음대로 설정할 수 있고, 파일 버전 관리도 가능해지죠. Google Analytics의 gtag.js처럼 자주 안 바뀌는 스크립트에 특히 효과적입니다.

# 서드파티 스크립트를 주기적으로 다운로드하는 크론 스크립트
#!/bin/bash
# cron: 0 */6 * * * (6시간마다 실행)
curl -s https://www.googletagmanager.com/gtag/js?id=G-XXXXX \
  -o /var/www/static/js/gtag.js

# 버전 해시 생성 (캐시 버스팅용)
HASH=$(md5sum /var/www/static/js/gtag.js | cut -d' ' -f1 | head -c 8)
cp /var/www/static/js/gtag.js "/var/www/static/js/gtag.${HASH}.js"

dns-prefetch와 preconnect

셀프 호스팅이 여건상 어렵다면, 최소한 리소스 힌트로 서드파티 도메인 연결을 미리 설정해두세요. 이것만으로도 체감 차이가 납니다.

<!-- DNS 조회 미리 수행 -->
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
<link rel="dns-prefetch" href="https://connect.facebook.net">

<!-- DNS + TCP + TLS 핸드셰이크 미리 수행 (더 적극적) -->
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
<link rel="preconnect" href="https://www.google-analytics.com" crossorigin>

preconnectdns-prefetch보다 더 많은 작업(TCP + TLS 핸드셰이크)을 미리 해두는 거라, 반드시 사용할 도메인에만 걸어야 합니다. 이것저것 다 걸어놓으면 초기 로드에 오히려 부담이 되니까 주의하세요.

최적화 전략 7: 서버 사이드 태깅

서버 사이드 태깅은 서드파티 데이터 수집을 브라우저가 아닌 서버에서 처리하는 방식입니다. Google Tag Manager Server-Side Container가 대표적이고요.

동작 방식

기존에는 브라우저가 각 서드파티 서버에 직접 데이터를 쏴야 했습니다. 서버 사이드 태깅에서는 브라우저가 여러분의 서버(퍼스트파티 엔드포인트) 하나에만 데이터를 보내고, 서버가 알아서 각 서드파티에 전달하는 구조예요. 브라우저 입장에서는 JavaScript 실행량과 네트워크 요청이 확 줄어듭니다.

Google Tag Gateway 활용

Google Tag Gateway를 쓰면 gtm.js 같은 Google 스크립트를 Google 서버가 아니라 자체 퍼스트파티 인프라에서 직접 서빙할 수 있습니다. Cloudflare랑 연동하면 퍼스트파티 컨텍스트에서 Google 태그를 제공할 수 있어서, 광고 차단기에 걸리는 문제도 상당 부분 해결됩니다. (마케팅팀이 좋아합니다)

실전 최적화 워크플로

여기까지 전략들을 쭉 살펴봤는데, 실제로 어떤 순서로 적용해야 하는지 정리해보겠습니다.

1단계: 감사(Audit)

  1. Chrome DevTools Performance 패널에서 "Dim 3rd parties" 켜고 트레이스를 녹화합니다.
  2. Lighthouse 돌려서 "Reduce the impact of third-party code" 감사 결과를 확인합니다.
  3. Coverage 탭에서 각 스크립트의 미사용 바이트 비율을 봅니다.
  4. 모든 서드파티 스크립트를 목록으로 뽑고, 비즈니스 가치 대비 성능 비용을 따져봅니다.

2단계: 분류(Categorize)

각 스크립트를 아래 네 가지 카테고리로 나눕니다. 이 분류 작업이 생각보다 중요한데, 여기서 판단을 잘못하면 나중에 마케팅팀이랑 싸울 수 있습니다.

  • 필수(Critical): 결제 시스템, 핵심 분석 등 비즈니스에 직접 필요한 스크립트
  • 중요(Important): A/B 테스팅, 마케팅 태그 등 가치는 있지만 지연 로딩 가능한 스크립트
  • 선택(Optional): 소셜 위젯, 부가 분석 등 제거하거나 퍼사드로 대체 가능한 스크립트
  • 불필요(Unnecessary): 더 이상 안 쓰거나 누가 왜 넣었는지 아무도 모르는 스크립트 → 바로 제거

3단계: 적용(Optimize)

  • 불필요한 스크립트부터 제거합니다. 이게 가장 효과 좋습니다.
  • 필수 스크립트에 async 또는 defer를 적용합니다.
  • 중요 스크립트에 지연 로딩(사용자 상호작용 또는 requestIdleCallback)을 적용합니다.
  • 선택 스크립트에 퍼사드 패턴을 적용합니다.
  • 여건이 된다면 Partytown이나 Zaraz를 도입합니다.
  • 서드파티 도메인에 dns-prefetchpreconnect를 설정합니다.

4단계: 모니터링(Monitor)

최적화 한 번 하고 끝이 아닙니다. web-vitals 라이브러리로 실제 사용자 데이터(RUM)를 계속 수집하고, CI/CD 파이프라인에 Lighthouse CI를 넣어서 누군가 서드파티 스크립트를 슬쩍 추가했을 때 바로 잡아낼 수 있어야 합니다.

// web-vitals로 서드파티 영향 모니터링
import { onINP, onLCP, onCLS } from 'web-vitals/attribution';

function reportMetric(metric) {
  const payload = {
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
    url: window.location.href,
    timestamp: Date.now()
  };

  // INP의 경우 어떤 요소/이벤트가 문제인지 추적
  if (metric.name === 'INP' && metric.attribution) {
    payload.debug = {
      element: metric.attribution.interactionTarget,
      type: metric.attribution.interactionType,
      loadState: metric.attribution.loadState
    };
  }

  navigator.sendBeacon('/api/web-vitals', JSON.stringify(payload));
}

onINP(reportMetric);
onLCP(reportMetric);
onCLS(reportMetric);

자주 묻는 질문 (FAQ)

서드파티 스크립트를 완전히 제거하면 안 되나요?

마음은 그렇지만 현실적으로 어렵습니다. 분석, 결제, 마케팅 등 비즈니스 운영에 필요한 스크립트를 다 빼버릴 수는 없으니까요. 핵심은 제거가 아니라 최적화입니다. 각 스크립트의 비즈니스 가치를 냉정하게 평가해서, 가치가 애매한 건 과감하게 빼고, 꼭 필요한 건 이 글의 전략들(지연 로딩, 퍼사드, Partytown/Zaraz 등)로 성능 영향을 최소화하세요.

Google Tag Manager(GTM)는 async로 로드해도 느린데, 어떻게 해야 하나요?

이건 정말 많이 받는 질문인데, GTM 자체보다 GTM을 통해 로드되는 태그들이 진짜 범인인 경우가 훨씬 많습니다. GTM 컨테이너 안에 태그가 20개, 30개씩 들어있는 사이트를 종종 보는데요, 우선 안에 들어있는 태그를 감사해서 안 쓰는 거 정리하세요. 그다음 트리거 조건을 세분화해서 모든 페이지에서 전부 발동하지 않게 하고, 장기적으로는 서버 사이드 태깅이나 Zaraz 전환을 검토해보시길 권합니다.

Partytown과 Cloudflare Zaraz 중 어떤 것을 선택해야 하나요?

Cloudflare를 이미 쓰고 있다면 Zaraz부터 시도해보세요. 설정이 간편하고 서버 측에서 돌아가니까 효과도 확실합니다. Cloudflare를 안 쓰거나 Next.js, Astro 같은 프레임워크에서 세밀하게 제어해야 하면 Partytown이 맞고요. 다만 Partytown은 베타라는 점, 프로덕션에 바로 올리기 전에 꼭 테스트해야 한다는 점은 기억해두세요.

서드파티 스크립트 최적화 후 효과를 어떻게 측정하나요?

세 가지를 같이 보는 게 좋습니다. 첫째, WebPageTest에서 서드파티 도메인 차단 전후 비교 테스트. 둘째, Lighthouse의 "Reduce the impact of third-party code" 점수 비교. 셋째, CrUX 데이터나 자체 RUM으로 실제 사용자의 코어 웹 바이탈 변화 추적. 랩 데이터만 보면 실제 환경이랑 다를 수 있어서, 꼭 필드 데이터까지 확인해야 합니다.

서드파티 스크립트를 지연 로딩하면 분석 데이터가 누락되지 않나요?

솔직하게 말하면, 네. 어느 정도 손실은 생깁니다. GTM을 사용자 상호작용 후에 로드하면 그 전까지의 행동은 당연히 못 잡아요. 이걸 최소화하려면 서버 사이드 태깅을 도입하거나, 핵심 분석만 defer로 빨리 로드하고 부가적인 마케팅 태그만 지연 로딩하는 식으로 나누는 게 현실적인 타협안입니다. 성능과 데이터 수집 사이에서 균형을 잡아야 하는 부분이라, 정답은 없고 각 사이트 상황에 맞게 판단하셔야 합니다.

저자 소개 Editorial Team

Our team of expert writers and editors.