웹 애플리케이션 성능 끌어올리는 ‘번들 크기 최적화’
본문은 요즘IT와 번역가 David가 함께 안토니 맥스(Anthony Max)의 글 <How to Reduce Web Application Bundle Size?>을 번역한 글입니다. 필자는 6년 이상 웹 개발 분야에서 활동해 온 개발자로, 다양한 외주 및 기업 프로젝트에 참여한 경험이 있습니다. 현재 그는 오픈 소스 프로젝트인 HMPL 템플릿 언어를 개발하고 있으며, 가상 DOM 없이도 빠른 성능을 제공하는 Cample 프레임워크를 개발한 경험이 있습니다. 이 글에서는 웹 애플리케이션의 번들 크기를 줄여 성능을 향상시키는 다양한 방법을 소개합니다.
필자에게 허락을 받고 번역했으며, 글에 포함된 각주(*표시)는 ‘번역자주’입니다. 글에 포함된 링크는 원문에 따라 표시했습니다.
프로젝트를 오랫동안 작업하다 보면, 기능을 추가할수록 웹 애플리케이션이 느려지는 것을 종종 발견하게 됩니다. 테이블이나 버튼 또는 다른 무언가를 추가하는 것이 크게 무게를 차지하지 않는 것처럼 보이지만, 결과적으로 초기 로딩 시간이 10~30초 이상 걸리는 등 용납할 수 없는 수준이 될 수 있습니다.
이번 글에서는 이러한 문제를 피하고 사이트의 로딩 속도를 빠르게 하며, 크기를 최대한 줄일 수 있는 몇 가지 방법과 팁을 살펴보고자 합니다.
플랫폼 의존성
웹 애플리케이션의 크기를 줄이고 싶다면, 우선 기본 플랫폼부터 시작해야 합니다. Next.js를 사용한다면 특정 방법들이 있고, 직접 작성한 사이트라면 다른 방법이 있습니다.

무엇보다도 동일한 프레임워크나 라이브러리를 구성하여, 더 나은 결과를 얻을 수 있는 방법을 찾는 것이 중요합니다. 예를 들어, 서버 요청에 대한 응답 캐싱이나 이미지용 애드온 등의 몇 가지 설정만 변경하면 됩니다. 이런 기능들은 때로는 이미 설정 자체에 내장되어 있어 찾기만 하면 됩니다.
CSR에서 SSR(SSG, ISG 등)로 이관
번들 크기를 줄이는 가장 좋은 방법 중 하나는 페이지 일부의 렌더링을 클라이언트에서 서버로 옮기는 것입니다. 이렇게 하면 컴포넌트가 하나씩 차례대로 로드되는 일종의 프레임워크를 얻을 수 있습니다. 따라서 이러한 프로젝트의 HTML 및 JS 소스 파일의 크기는 빈 태그와 준비된 컴포넌트를 넣는 서버 요청으로만 구성됩니다.
이러한 접근 방식의 예는 다음 코드와 같습니다.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= data.title %></title>
</head>
<body>
<h1><%= data.title %></h1>
<p><%= data.content %></p>
server.js
const express = require('express');
const app = express();
const PORT = 3000;
// EJS를 템플릿 엔진으로 설정
app.set('view engine', 'ejs');
// 샘플 데이터
const data = {
title: '서버 사이드 렌더링 예제',
content: '이것은 Node.js와 EJS를 사용한 서버 사이드 렌더링의 예시입니다.'
};
// 루트 정의
app.get('/', (req, res) => {
// EJS를 사용하여 HTML 렌더링
res.render('index', { data });
});
// 서버 시작
app.listen(PORT, () => {
console.log(`서버가 http://localhost:${PORT}에서 실행 중입니다`);
});
여기서 우리는 EJS와 Express 덕분에 모든 것을 서버에서 렌더링할 수 있습니다. 또한 사이트를 Next.js로 재설계할 수 있으며, 그러면 한 페이지뿐만 아니라, 동적 경로를 포함한 다른 페이지에서도 유사한 효과를 얻을 수 있고, 물론 크롤러에 의한 인덱싱도 가능합니다.
하지만 SSR(SSG, ISG 등)을 구현하는 이 방법에는 심각한 단점이 있어 적합하지 않을 수도 있습니다. 예를 들어, 사이트가 이미 클라이언트 측에 초점을 맞춘 프레임워크나 라이브러리 중 하나를 사용하고 있다면, 모든 것을 다시 만드는 데 많은 비용과 시간이 들 수 있습니다.
또한 Next.js를 제외한 이러한 도구를 선택하는 경우, 해당 작업에 적합한 인력 부족을 초래할 수 있습니다. 한 사람이 특정 프레임워크로 일하는 법을 배웠는데, 그 일자리가 인기가 없거나 대체하기 어려운 라이브러리를 사용한다면, 적합한 지원자를 찾는 것이 문제가 될 수 있습니다.
서버 지향 접근 방식을 유지하면서도 위에서 설명한 중요한 문제를 겪지 않으려면, HMPL.js나 유사한 라이브러리를 사용할 수 있습니다.
HMPL.js를 사용한 서버 지향 방식 구현
위에서 설명한 방법과 달리, 이 모듈은 크롤러가 페이지를 인덱싱하도록 허용하지 않지만, WordPress, Vue.js, Tilda, Next.js 등 원하는 어떤 웹 애플리케이션이나 사이트에도 연결할 수 있습니다.
모듈 작업은 다음과 같습니다.
index.html
<main id="app"></main>
<script src="https://unpkg.com/json5/dist/index.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
client.js
const templateFn = hmpl.compile(
`<div>
<button data-action="increment" id="btn">Click!</button>
<div>Clicks: {{ src: "/api/clicks", after: "click:#btn" }}</div>
</div>`
);
const clicker = templateFn(({ request: { event } }) => ({
body: JSON.stringify({ action: event.target.getAttribute("data-action") })
})).response;
document.querySelector("#app").append(clicker);
여기서도 렌더링 된 HTML을 얻지만, 따라야 할 명확한 아키텍처는 없습니다. 어떤 프로젝트에서든 모듈을 비활성화하거나 활성화할 수 있으며 아무런 문제가 없습니다. 또한 사용하기 쉬우며, 작지만 필요한 기능으로 구성되어 있습니다. 예시에서는 ‘after’를 안전하게 제거하고 DOM 렌더링 중에 컴포넌트를 로드할 수 있습니다.
번들 크기를 줄이는 데 도움을 주는 일반적인 방법
서버와의 작업이 아닌, 웹 애플리케이션과의 일반적인 작업을 한다면, 아래에 설명된 방법들도 번들 크기를 줄이는 데 도움이 될 수 있습니다.
1. 불필요한 의존성 제거
웹 애플리케이션을 개발하는 과정에서 특정 기능을 구현하기 위해 다양한 패키지를 다운로드하고, 테스트하고, 가장 적합한 것을 선택하는 경우가 있습니다. 이때 사용하지 않는 패키지를 삭제하는 것을 잊으면 번들 크기는 계속 커지게 됩니다. 또는 일반 자바스크립트로 쉽게 해결할 수 있는 간단한 작업을 위해 거대한 모듈을 연결하고, 그중 단 하나의 함수만 사용하는 것도 무의미합니다.
사용하지 않는 패키지를 파악하기 위해, 다음과 같은 패키지를 사용할 수 있습니다.
npm install depcheck
depcheck /path/to/my/project
또는
npx depcheck
이 모듈은 지원이 중단되었지만, 여전히 종속성을 분석하고 사용되지 않는 것을 식별할 수 있습니다. 그러나 사용되지 않는 것처럼 보이지만, 그것 없이는 일부 모듈이 작동하지 않을 수 있으니 신중하게 사용해야 합니다.
또한 다음 명령을 통해 npm의 내장 기능을 사용할 수 있습니다.
npm prune
이 명령은 “불필요한” 패키지를 제거합니다. 패키지 이름이 제공되면 제공된 이름 중 하나와 일치하는 패키지만 제거됩니다.
2. 작은 크기의 미디어 파일 사용
이것은 아마도 제공할 수 있는 가장 간단하고 명백한 조언 중 하나일 것입니다. 프로젝트에 전체 웹 애플리케이션과 크기가 동일한 비디오 하나가 있다면, ‘git clone’과 같은 작업을 수행할 때 매우 어려움을 겪게 될 것입니다.
이 방법은 이미지에 매우 효과적이며, 손실 없이 이미지당 몇 MB를 절약할 수 있습니다. 요즘은 온라인 압축 플랫폼들이 이를 쉽게 해결해 줍니다.

또한 이미지 해상도를 png, jpg에서 webp로 변경할 수 있습니다. 이 또한 많은 대형 웹 애플리케이션에서 사용되는 좋은 방법입니다.
3. CDN 사용
이는 ‘npm_modules’에서 외부 환경으로 모듈을 불러올 때 사용하는 일반적인 방법 중 하나입니다.
import { chunk } from "lodash";
또는
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
이것은 앞서 설명한 것과 유사하지만 약간 다른 의미를 가집니다.
4. 코드 분할
코드를 분할하는 가장 쉬운 방법 중 하나는 동적으로 모듈을 불러오는 것입니다. Webpack 및 Vite와 같은 현대 번들러에서는 다음과 같이 쉽게 작성할 수 있습니다.
main.js
document.getElementById('loadButton').addEventListener('click', () => {
import('./module.js')
.then(module => {
module.default();
})
.catch(err => {
console.error('모듈 로딩 오류:', err);
});
});
이 경우 모듈을 바로 로드하지 않고 버튼을 누를 때와 같이 필요할 때만 로드합니다. 또한 청크 분할을 활성화할 수 있습니다. 이는 다른 모듈 간의 공통 코드를 분리하는 데 유용합니다.
webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
},
},
};
5. </> 코드 압축
컴파일 중에 코드를 압축하여 번들 크기를 줄일 수도 있습니다. 이것이 아마도 사용할 수 있는 방법 중 가장 좋은 방법일 것입니다.
uglifyjs file.js -c toplevel,sequences=false
이를 위해 Uglify.js를 사용할 수 있는데, 이는 코드 압축을 위한 가장 인기 있는 도구 중 하나입니다. 물론 번들러에 기본적으로 포함되어 있지 않다면, 번들러와 함께 사용할 수도 있습니다.
결론
우선 위에서 나열한 내용들은 대부분의 웹 애플리케이션에 바로 적용할 수 있는 가장 일반적인 방법들입니다. 이 방법들은 실제 웹 개발 작업에서 많은 사람들이 공통적으로 사용하는 인기 있는 방식들입니다. 또한 이미 널리 알려진 DRY*(Don't Repeat Yourself)나 KISS*(Keep It Simple, Stupid)와 같은 원칙보다는, 실무에서 더 구체적이고 현실적으로 활용할 수 있는 실용적인 조언을 드리고자 했습니다.
이러한 방법들을 잘 적용하시면 여러분의 웹사이트는 더욱 가벼워지고, 로딩 속도도 훨씬 빨라질 것입니다. 이 글이 더 효과적이고 효율적인 웹 개발에 도움이 되길 바랍니다. 읽어주셔서 감사합니다.
*DRY(Don’t Repeat Yourself): 소프트웨어 개발에서 중복을 최소화하는 원칙을 의미
*KISS(Keep It Simple, Stupid): 디자인이나 시스템 개발에서 단순함을 추구하는 설계 원칙을 의미
<원문>
How to Reduce Web Application Bundle Size?
©위 번역글의 원 저작권은 Anthony Max에게 있으며, 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다