IT의 IT 블로그

번들러 동작 원리 이해하기 본문

Frontend Basic (기초 이론)

번들러 동작 원리 이해하기

IT의 IT 블로그 2026. 3. 15. 23:57

안녕하세요.
이번 글에서는 번들러가 내부에서 어떻게 동작하는지를 구조적으로 정리해보려고 합니다.

프론트엔드 프로젝트를 진행하다 보면 Webpack, Vite, Rollup 같은 번들러를 자연스럽게 사용하게 됩니다. 하지만 대부분은 “빌드 도구” 정도로만 이해하고 실제로 번들러가 내부에서 어떤 과정을 거치는지는 깊이 생각해보지 않는 경우가 많습니다.

번들러는 단순히 여러 파일을 하나로 합치는 도구가 아닙니다.
실제로는 다음과 같은 작업을 수행합니다.

  • 모듈 간 의존성을 분석해 의존성 그래프(Dependency Graph) 를 생성하고
  • 사용하지 않는 코드를 제거하는 트리쉐이킹(Tree Shaking) 을 수행하며
  • 필요한 코드만 로딩할 수 있도록 코드 스플리팅(Code Splitting) 을 적용합니다.

이번 글에서는 번들러가 이 과정을 어떤 순서로 처리하는지를 중심으로 살펴보겠습니다.


1. 번들러는 무엇을 하는 도구일까

먼저 번들러의 역할을 간단히 정리해보겠습니다.

번들러는 여러 개의 모듈 파일을 분석하여 하나 또는 여러 개의 최적화된 결과물로 만들어주는 도구입니다.

예를 들어 프로젝트 구조가 다음과 같다고 가정해보겠습니다.

src
├─ main.js
├─ utils.js
├─ api.js
└─ components
└─ Button.js
 

각 파일은 서로 import로 연결되어 있습니다.

브라우저는 기본적으로 파일 단위로 네트워크 요청을 보내기 때문에, 모듈이 많아질수록 요청 횟수가 증가하게 됩니다.

이 문제를 해결하기 위해 번들러는

  1. 모든 모듈을 분석하고
  2. 하나의 구조로 묶고
  3. 최적화된 파일을 생성합니다.

이 과정의 핵심이 바로 의존성 그래프입니다.


2. 의존성 그래프 생성 과정

2.1 번들링은 엔트리 파일에서 시작된다

번들링은 항상 엔트리 파일(entry file) 에서 시작합니다.

예를 들어 다음과 같은 코드가 있다고 가정해보겠습니다.

 
import { add } from "./utils.js";
import Button from "./components/Button.js";

console.log(add(2, 3));
 

이 파일이 바로 번들링의 시작점이 됩니다.

번들러는 이 파일을 읽은 뒤 내부에서 다음 작업을 수행합니다.

  • import 문을 분석
  • 어떤 파일을 참조하는지 확인
  • 해당 파일을 다시 분석

2.2 import를 따라가며 그래프를 만든다

예를 들어 utils.js가 다음과 같다고 가정해보겠습니다.

 
export function add(a, b) {
return a + b;
}
 

그리고 Button.js가 다음과 같다고 해봅시다.

 
import { fetchData } from "../api.js";
 

이렇게 되면 번들러는 다음과 같은 구조를 파악합니다.

main.js
├─ utils.js
└─ Button.js
└─ api.js
 

이 구조가 바로 의존성 그래프(Dependency Graph) 입니다.

번들러는 이 그래프를 기반으로 전체 프로젝트 구조를 이해하게 됩니다.


3. 코드 분석 단계 (AST 분석)

번들러는 코드를 단순 문자열로 읽지 않습니다.

코드를 AST(Abstract Syntax Tree) 로 변환해 분석합니다.

예를 들어 다음 코드가 있다고 가정해보겠습니다.

 
import { add } from "./utils.js";
 

번들러는 이를 다음과 같은 구조로 파악합니다.

ImportDeclaration
├─ specifier: add
└─ source: "./utils.js"

이렇게 하면 다음과 같은 정보들을 정확하게 알 수 있습니다.

  • 어떤 모듈을 import 하는지
  • 어떤 export가 사용되는지
  • 사용되지 않는 코드가 무엇인지

이 분석이 바로 트리쉐이킹의 기반이 됩니다.


4. 트리쉐이킹(Tree Shaking)

4.1 트리쉐이킹이란 무엇인가

트리쉐이킹은 사용하지 않는 코드를 제거하는 최적화 기법입니다.

예를 들어 다음 코드가 있다고 가정해보겠습니다.

 
export function add() {}
export function subtract() {}
export function multiply() {}
 

그리고 다른 파일에서 다음처럼 사용합니다.

 
import { add } from "./math.js";
 

이 경우 실제로 필요한 코드는 add 함수뿐입니다.

번들러는 이를 분석하여 다음처럼 최적화합니다.

math.js
└─ add()
 

subtract와 multiply는 번들에 포함되지 않습니다.


4.2 트리쉐이킹이 가능한 이유

트리쉐이킹은 ES Module 구조 덕분에 가능합니다.

 
import { add } from "./math.js";
 

이 문법은 정적으로 분석할 수 있기 때문에

  • 어떤 함수가 사용되는지
  • 어떤 export가 필요한지

빌드 시점에 정확하게 파악할 수 있습니다.

반면 CommonJS의 경우

 
const math = require("./math");
 

이 구조는 런타임에 결정되기 때문에
번들러가 어떤 코드가 필요한지 정확히 판단하기 어렵습니다.


5. 코드 스플리팅(Code Splitting)

5.1 왜 코드 스플리팅이 필요한가

모든 코드를 하나의 파일로 묶는 것이 항상 좋은 것은 아닙니다.

예를 들어 다음 기능이 있다고 가정해보겠습니다.

  • 메인 페이지
  • 관리자 페이지
  • 통계 페이지

사용자는 대부분 메인 페이지만 먼저 접근합니다.

그렇다면 관리자 코드까지 함께 로딩할 필요는 없습니다.


5.2 동적 import

이 문제를 해결하는 방법이 동적 import입니다.

import("./admin.js").then(module => {
module.initAdmin();
});

 

이 코드는 admin.js를 필요한 순간에만 로딩합니다.

번들러는 이를 분석해 별도의 파일로 분리합니다.

예를 들어 결과물은 다음과 같이 생성될 수 있습니다.

dist
├─ main.js
├─ vendor.js
└─ admin.chunk.js

이렇게 하면 초기 로딩 속도가 크게 개선됩니다.


6. 최종 번들 생성

모든 분석이 끝나면 번들러는 최종 결과물을 생성합니다.

보통 다음과 같은 최적화가 적용됩니다.

  • 코드 압축 (minify)
  • 변수 이름 축소
  • 불필요한 코드 제거
  • 해시 기반 파일명 생성

예를 들어 결과물은 다음처럼 생성될 수 있습니다.

dist
├─ index.html
├─ app.34ab29.js
├─ vendor.82cd11.js
└─ style.91ac20.css

 

 

해시 파일명은 브라우저 캐시 관리를 위해 사용됩니다.


7. 정리

지금까지 번들러가 내부에서 어떤 과정을 거치는지 살펴보았습니다.

번들러는 단순히 여러 파일을 하나로 합치는 도구가 아니라,

  1. 엔트리 파일을 기준으로 모듈을 분석하고
  2. import 관계를 따라 의존성 그래프(Dependency Graph) 를 생성하며
  3. AST 분석을 통해 코드 구조를 이해하고
  4. 사용되지 않는 코드를 제거하는 트리쉐이킹(Tree Shaking) 을 수행하고
  5. 필요에 따라 코드 스플리팅(Code Splitting) 을 적용해
  6. 최종적으로 최적화된 번들을 만들어냅니다.

즉 번들러는 모듈을 분석하고 최적화하는 빌드 시스템이라고 볼 수 있습니다.

 

지금까지 번들러가 내부에서 어떻게 동작하는지 정리해봤습니다.

그렇다면 실제로 많이 사용되는 번들러들은
이 과정을 어떤 방식으로 처리하고 있을까요?

 

다음 글에서는 대표적인 번들러인 Webpack과 Vite의 구조적 차이와 설계철학을 비교해보겠습니다.

읽어주셔서 감사합니다.