script 태그의 성능 최적화: async와 defer 속성 완벽 가이드

2025. 5. 28. 10:08·Frontend Development
반응형

이미지 출처 - https://medium.com/@saravanaeswari22/javascript-async-defer-attributes-a30b7eb8e993

"웹사이트의 성능은 사용자 경험의 핵심 요소입니다. 단 1초의 로딩 지연도 이탈률을 크게 높일 수 있습니다. script 태그의 실행 방식을 최적화하는 것만으로도 체감 속도를 크게 향상시킬 수 있습니다."

웹 개발에서 자바스크립트 파일을 불러오는 방식은 페이지 로딩 성능에 큰 영향을 미칩니다. 특히 <script> 태그의 async와 defer 속성은 브라우저가 자바스크립트를 어떻게 다운로드하고 실행할지를 결정하는 중요한 요소입니다. 이번 글에서는 이 두 속성의 차이점과 각각의 사용 시나리오를 상세히 알아보겠습니다.

목차

  • 기본 script 태그의 동작 방식
  • async와 defer의 등장 배경
  • async 속성 상세 분석
  • defer 속성 상세 분석
  • async vs defer 비교
  • 실제 사용 시나리오
  • 브라우저 호환성
  • 성능 최적화 팁
  • 결론

기본 script 태그의 동작 방식

먼저 아무 속성 없는 일반 <script> 태그가 어떻게 동작하는지 이해해 봅시다.

<script src="app.js"></script>

브라우저는 HTML을 파싱하다가 위와 같은 스크립트 태그를 만나면 다음과 같은 과정을 거칩니다:

  1. HTML 파싱을 일시 중지합니다.
  2. 자바스크립트 파일을 다운로드합니다.
  3. 다운로드가 완료되면 즉시 스크립트를 실행합니다.
  4. 스크립트 실행이 완료된 후 HTML 파싱을 계속합니다.

이는 마치 고속도로에서 갑자기 공사 구간을 만나 차량이 멈추는 것과 같습니다. HTML 파싱(차량 이동)이 중단되고, 사용자는 콘텐츠를 보기 위해 더 오래 기다려야 합니다.


async와 defer의 등장 배경

웹 페이지가 점점 복잡해지고 자바스크립트 파일의 크기와 수가 증가함에 따라, 기본 스크립트 로딩 방식은 성능 문제를 일으키기 시작했습니다. 이러한 문제를 해결하기 위해 HTML5에서 async와 defer 속성이 도입되었습니다.

이 두 속성은 브라우저가 HTML 파싱과 자바스크립트 로딩을 병렬로 처리할 수 있게 하여, 전체 페이지 로드 시간을 단축시키는 것을 목표로 합니다. 그러나 두 속성은 스크립트의 실행 시점에서 중요한 차이를 보입니다.


async 속성 상세 분석

async 속성은 스크립트 로딩과 실행을 비동기적으로 처리합니다.

<script async src="analytics.js"></script>

동작 방식

  1. 비동기 다운로드: 브라우저는 HTML 파싱을 계속하면서 병렬로 스크립트를 다운로드합니다.
  2. 즉시 실행: 다운로드가 완료되면 HTML 파싱을 중단하고 즉시 스크립트를 실행합니다.
  3. 파싱 재개: 스크립트 실행이 완료된 후 HTML 파싱을 재개합니다.

주요 특징

  • 실행 순서가 보장되지 않음: 여러 async 스크립트는 다운로드가 완료되는 순서대로 실행됩니다. 즉, HTML에 작성된 순서와 실제 실행 순서가 다를 수 있습니다.
  • DOM 접근 시 주의 필요: 스크립트가 실행될 때 HTML이 완전히 파싱되지 않았을 수 있으므로, 아직 생성되지 않은 DOM 요소에 접근하면 오류가 발생할 수 있습니다.
  • 파싱 차단 가능성: 다운로드는 비동기적이지만, 실행 시에는 여전히 HTML 파싱을 차단합니다.

비유로 이해하기

async는 마치 식당에서 메인 요리(HTML 파싱)를 준비하는 동안 음료(스크립트)를 주문한 것과 같습니다. 음료가 준비되면 메인 요리 준비를 잠시 중단하고 음료를 테이블에 가져다 놓은 후, 다시 메인 요리 준비를 계속합니다.


defer 속성 상세 분석

defer 속성 역시 스크립트를 비동기적으로 다운로드하지만, 실행 시점이 다릅니다.

<script defer src="app.js"></script>

동작 방식

  1. 비동기 다운로드: HTML 파싱과 병렬로 스크립트를 다운로드합니다.
  2. 지연 실행: HTML 파싱이 완료된 후, DOMContentLoaded 이벤트 발생 직전에 스크립트를 실행합니다.
  3. 순서 보장: 여러 defer 스크립트는 HTML에 작성된 순서대로 실행됩니다.

주요 특징

  • 실행 순서 보장: 스크립트들은 HTML에 정의된 순서대로 실행됩니다.
  • DOM 완전 접근 가능: HTML 파싱이 완료된 후 실행되므로, 모든 DOM 요소에 안전하게 접근할 수 있습니다.
  • 파싱 차단 없음: HTML 파싱이 완료된 후 실행되므로, 파싱을 중단하지 않습니다.

비유로 이해하기

defer는 식당에서 메인 요리(HTML 파싱)를 모두 준비한 후에, 주문해 둔 디저트(스크립트)를 순서대로 제공하는 것과 같습니다. 손님은 메인 요리를 끝내고 디저트를 즐길 수 있으며, 디저트는 주문한 순서대로 나옵니다.


async vs defer 비교

두 속성의 차이점을 시각적으로 비교해보겠습니다:

특성 일반 script async defer
다운로드 방식 HTML 파싱 차단 비동기(병렬) 비동기(병렬)
실행 시점 다운로드 후 즉시 다운로드 후 즉시 HTML 파싱 완료 후
HTML 파싱 차단 다운로드 및 실행 시 실행 시에만 차단하지 않음
실행 순서 문서 순서대로 다운로드 완료 순서대로 문서 순서대로
DOM 접근 실행 시점까지 생성된 요소만 실행 시점까지 생성된 요소만 모든 요소 접근 가능

아래 다이어그램은 세 가지 방식의 로드 타임라인을 시각화한 것입니다:

일반 script:
파싱 HTML -----(스크립트 다운로드 및 실행)-----> 파싱 HTML 계속 ---> 완료

async:
파싱 HTML -------|
                 |---> (스크립트 실행) ---> 파싱 HTML 계속 ---> 완료
(스크립트 다운로드) -|

defer:
파싱 HTML ---------------------------|---> (스크립트 실행) ---> 완료
(스크립트 다운로드) ----------------|

실제 사용 시나리오

async가 적합한 경우

  1. 독립적인 스크립트: 페이지 렌더링이나 다른 스크립트에 의존하지 않는 독립적인 기능을 수행하는 스크립트

    • 예: 애널리틱스 도구(Google Analytics), 광고 스크립트
    <script async src="https://www.google-analytics.com/analytics.js"></script>
  2. 빠른 로드가 중요한 경우: 가능한 빨리 실행되어야 하는 스크립트

    • 예: A/B 테스트 도구, 중요하지 않은 위젯

defer가 적합한 경우

  1. DOM 조작이 필요한 스크립트: 문서의 요소를 조작하는 스크립트

    • 예: 메인 애플리케이션 코드, jQuery 플러그인
    <script defer src="app.js"></script>
    <script defer src="ui-components.js"></script>
  2. 실행 순서가 중요한 경우: 다른 스크립트에 의존하는 스크립트

    • 예: 라이브러리 로드 후 실행되어야 하는 초기화 코드
    <script defer src="jquery.js"></script>
    <script defer src="jquery-plugin.js"></script>
    <script defer src="app-init.js"></script>

실제 프로젝트 예시

<!DOCTYPE html>
<html>
<head>
  <!-- 핵심 기능에 필요한 스크립트는 defer 사용 -->
  <script defer src="vendor.js"></script>
  <script defer src="main.js"></script>

  <!-- 독립적인 분석 도구는 async 사용 -->
  <script async src="analytics.js"></script>
  <script async src="pixel-tracker.js"></script>
</head>
<body>
  <!-- 페이지 콘텐츠 -->
</body>
</html>

브라우저 호환성

async와 defer 속성은 대부분의 최신 브라우저에서 지원됩니다:

  • async: IE 10+, 크롬, 파이어폭스, 사파리, 엣지 등 대부분의 최신 브라우저에서 지원
  • defer: IE 10+ (제한적 지원), 다른 최신 브라우저에서 완전 지원

Internet Explorer 9 이하를 지원해야 하는 경우, 대체 방법을 고려하는 것이 좋습니다:

<!-- 대체 방법: 문서 끝에 스크립트 배치 -->
<body>
  <!-- 페이지 콘텐츠 -->

  <!-- 문서 끝에 스크립트 배치 -->
  <script src="app.js"></script>
</body>

성능 최적화 팁

1. 적절한 위치에 스크립트 배치하기

<head>
  <!-- 중요하고 빨리 로드되어야 하는 작은 스크립트 -->
  <script src="critical.js"></script>

  <!-- 페이지 렌더링에 필요한 스크립트는 defer 사용 -->
  <script defer src="main.js"></script>

  <!-- 독립적인 기능은 async 사용 -->
  <script async src="widget.js"></script>
</head>

2. 모듈 스크립트 활용하기

ES 모듈은 기본적으로 defer처럼 동작합니다:

<script type="module" src="app.mjs"></script>

3. 스크립트 프리로드 고려하기

중요한 스크립트는 프리로드하여 더 빨리 다운로드되도록 할 수 있습니다:

<link rel="preload" href="critical.js" as="script">
<script defer src="critical.js"></script>

4. 인라인 스크립트의 최적화

작은 인라인 스크립트에도 async와 defer 개념을 적용할 수 있습니다:

<!-- 일반적인 인라인 스크립트 -->
<script>
  // 이 코드는 즉시 실행되고 HTML 파싱을 차단함
  document.querySelector('h1').style.color = 'red';
</script>

<!-- DOM 로드 후 실행되도록 개선 -->
<script>
  document.addEventListener('DOMContentLoaded', function() {
    document.querySelector('h1').style.color = 'red';
  });
</script>

결론

웹 성능 최적화에서 자바스크립트 로딩 전략은 매우 중요한 요소입니다. async와 defer 속성은 각각 다른 상황에 적합한 특성을 가지고 있으며, 이를 적절히 활용하면 사용자 경험을 크게 개선할 수 있습니다.

간단히 요약하자면:

  • async: 독립적인 스크립트, 빠른 실행이 중요한 경우에 적합
  • defer: DOM 조작이 필요하거나 실행 순서가 중요한 스크립트에 적합

실제 프로젝트에서는 두 속성을 혼합하여 사용하는 것이 일반적입니다. 핵심 애플리케이션 로직은 defer로, 독립적인 서드파티 스크립트는 async로 로드하는 방식이 권장됩니다.

자바스크립트 로딩 최적화는 단순한 기술적 개선을 넘어, 사용자 경험의 질을 높이는 중요한 요소입니다. 로딩 시간이 1초만 줄어도 이탈률을 크게 낮추고 전환율을 높일 수 있다는 연구 결과가 있습니다. 작은 속성 추가만으로도 큰 변화를 만들 수 있으니, 여러분의 프로젝트에 오늘부터 적용해 보세요.


참고 자료

  • MDN Web Docs - Script 태그
  • HTML Living Standard - Script 요소
  • Google Web Fundamentals - JavaScript 로딩 최적화
  • 웹 성능 최적화를 위한 스크립트 로딩 전략
  • Steve Souders - 고성능 웹사이트 구축
반응형
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

Netflix의 아키텍처와 API 성능 최적화 전략  (0) 2025.05.28
React의 동시성 모드(Concurrent Mode): 사용자 경험을 혁신하는 비밀 무기  (0) 2025.05.28
React 성능 최적화: 메모이제이션 기법으로 불필요한 리렌더링 방지하기  (4) 2025.05.28
CORS 완벽 가이드: 웹 개발자가 반드시 알아야 할 교차 출처 리소스 공유  (0) 2025.05.28
React의 Error Boundary: 안정적인 프론트엔드 애플리케이션을 위한 필수 요소  (2) 2025.05.28
'Frontend Development' 카테고리의 다른 글
  • Netflix의 아키텍처와 API 성능 최적화 전략
  • React의 동시성 모드(Concurrent Mode): 사용자 경험을 혁신하는 비밀 무기
  • React 성능 최적화: 메모이제이션 기법으로 불필요한 리렌더링 방지하기
  • CORS 완벽 가이드: 웹 개발자가 반드시 알아야 할 교차 출처 리소스 공유
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (100) N
      • Frontend Development (39) N
      • Backend Development (21) N
      • Algorithm (33) N
        • 백준 (11) N
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    frontend development
    tailwindcss
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
script 태그의 성능 최적화: async와 defer 속성 완벽 가이드
상단으로

티스토리툴바