안녕하세요! 오늘은 웹 애플리케이션의 로딩 속도를 획기적으로 개선할 수 있는 중요한 최적화 기법, **코드 스플리팅(Code Splitting)**에 대해 이야기하려 합니다. 특히 Vue.js 프로젝트에 실제로 적용했던 경험을 바탕으로 실용적인 방법을 공유해 드릴게요.
웹 애플리케이션을 개발하고 빌드하면, 모든 JavaScript 파일이 하나의 거대한 번들 파일로 묶이는 경우가 많습니다. 이 번들 파일은 사용자가 처음 웹사이트에 접속할 때 한꺼번에 다운로드되므로, 파일 크기가 클수록 초기 로딩 시간이 길어지는 문제가 발생합니다.
코드 스플리팅은 바로 이 문제를 해결하기 위한 기술입니다. 애플리케이션의 번들을 여러 개의 작은 **청크(Chunk)**로 나누어, 필요한 시점에만 해당 청크를 비동기적으로 로드하는 방식이죠. 마치 거대한 백과사전 전체를 한 번에 들고 다니는 대신, 필요한 페이지(청크)만 그때그때 꺼내 보는 것과 같습니다.
저희 프로젝트도 초기에는 모든 컴포넌트와 모듈이 하나의 번들에 포함되어 있었습니다. 이로 인해 초기 로딩 시간이 길어졌고, 사용자 경험이 저하되는 문제가 있었죠. 아래 코드를 보며 어떤 문제점이 있었고, 어떻게 개선했는지 살펴보겠습니다.
현재 라우터 설정은 모든 뷰(View) 컴포넌트를 미리 import
하고 있습니다. 사용자가 대시보드
페이지에만 접속하더라도, 토픽 관리
, 메시지 관리
, 메트릭스
등 모든 페이지의 코드를 미리 다운로드해야 하는 비효율적인 구조입니다.
// 개선 전 - 모든 컴포넌트가 즉시 로드됨
import Dashboard from '@/views/Dashboard.vue'
import Connections from '@/views/Connections.vue'
import Topics from '@/views/Topics.vue'
// ...
const routes = [
{ path: '/', name: 'Dashboard', component: Dashboard },
{ path: '/connections', name: 'Connections', component: Connections },
// ...
]
✅ 라우트 기반 코드 스플리팅
Vue Router의 동적 임포트(Dynamic Import) 기능을 활용해 각 라우트별로 컴포넌트를 분리했습니다. () => import(...)
문법을 사용하면, 해당 라우트로 이동할 때만 관련 컴포넌트의 청크 파일을 로드하게 됩니다.
// 개선 후 - 라우트 진입 시점에만 컴포넌트 로드
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'Dashboard',
component: () => import('@/views/Dashboard.vue'), // 대시보드 진입 시 로드
},
{
path: '/connections',
name: 'Connections',
component: () => import('@/views/Connections.vue'), // 연결 관리 진입 시 로드
},
// ... 나머지 라우트도 동일하게 적용
]
})
토픽 모니터링
페이지를 예로 들겠습니다. 이 페이지는 여러 차트 컴포넌트를 포함하고 있는데, 사용자가 페이지에 진입하자마자 모든 차트 컴포넌트가 로드됩니다. 만약 사용자가 특정 차트를 보지 않는다면 불필요한 리소스를 낭비하게 되는 것이죠.
<script setup>
import BarChart from '@/components/charts/BarChart.vue'
import PieChart from '@/components/charts/PieChart.vue'
// ...
</script>
<template>
<div>
<BarChart :data="data" />
<PieChart :data="data" />
</div>
</template>
✅ defineAsyncComponent
와 Suspense
활용
Vue 3의 defineAsyncComponent
와 Suspense
컴포넌트를 사용하면 필요한 컴포넌트만 지연 로딩할 수 있습니다. v-if
조건에 따라 차트를 로드하게 만들어, 사용자가 '차트 보기' 버튼을 클릭하거나 특정 조건이 만족될 때만 차트 컴포넌트를 비동기로 가져오게 했습니다.
<template>
<div class="topic-monitoring">
<button @click="showCharts = true">차트 로드하기</button>
<div v-if="showCharts">
<Suspense>
<template #default>
<div class="charts-container">
<BarChart :data="topicThroughputData" />
<PieChart :data="partitionDistributionData" />
</div>
</template>
<template #fallback>
<div class="loading-charts">
<span>차트 로딩 중...</span>
</div>
</template>
</Suspense>
</div>
</div>
</template>
<script setup>
import { ref, defineAsyncComponent } from 'vue'
// 지연 로딩으로 차트 컴포넌트 정의
const BarChart = defineAsyncComponent(() => import('@/components/charts/BarChart.vue'))
const PieChart = defineAsyncComponent(() => import('@/components/charts/PieChart.vue'))
const showCharts = ref(false)
</script>
Suspense
는 비동기 컴포넌트가 로딩되는 동안 fallback
콘텐츠를 보여주는 기능으로, 사용자에게 더 나은 로딩 경험을 제공하는 데 유용합니다.
사용자마다 접근 권한이 다를 수 있습니다. 예를 들어, 관리자는 메트릭스
기능을 볼 수 있지만 일반 사용자는 볼 수 없다고 가정해 봅시다. 기존 방식에서는 일반 사용자에게도 메트릭스
관련 컴포넌트가 다운로드되어 불필요한 트래픽과 리소스 낭비를 초래했습니다.
✅ 조건부 코드 스플리팅
사용자의 권한에 따라 필요한 컴포넌트만 v-if
로 렌더링하고, 이를 Suspense
와 함께 사용하여 비동기 로딩을 적용했습니다.
<template>
<div class="dashboard">
<ConnectionList />
<Suspense v-if="userPermissions.canViewTopics">
<template #default>
<TopicList />
</template>
<template #fallback>
<div>토픽 관리 로딩 중...</div>
</template>
</Suspense>
<Suspense v-if="userPermissions.canViewMetrics">
<template #default>
<MetricsDashboard />
</template>
<template #fallback>
<div>메트릭스 로딩 중...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const userPermissions = computed(() => userStore.permissions)
</script>
이 방식은 권한이 없는 사용자에게는 아예 해당 기능의 코드 청크가 다운로드되지 않으므로, 보안과 성능 두 마리 토끼를 모두 잡을 수 있습니다.
코드 스플리팅을 적용한 후에는 그 효과를 객관적으로 측정하는 것이 중요합니다. Vite를 사용하고 있다면 rollup-plugin-visualizer
같은 번들 분석 도구를 활용하면 좋습니다.
// vite.config.js
import { defineConfig } from 'vite'
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
vue(),
visualizer({
filename: 'dist/stats.html', // 번들 분석 결과를 html 파일로 생성
open: true, // 빌드 완료 후 자동으로 브라우저에서 열기
gzipSize: true, // gzip 압축 크기 표시
brotliSize: true // brotli 압축 크기 표시
})
]
})
이 플러그인을 사용하면 아래와 같이 번들 파일을 시각적으로 분석할 수 있습니다.
저희 프로젝트의 경우, 이 도구를 통해 초기 번들 크기를 68% 감소시키고, 첫 로딩 시간을 66% 개선하는 놀라운 결과를 얻었습니다.
항목 | 개선 전 | 개선 후 | 개선율 |
---|---|---|---|
초기 번들 크기 | 2.5MB | 800KB | 68% 감소 |
첫 로딩 시간 | 3.2초 | 1.1초 | 66% 개선 |
코드 스플리팅은 사용자가 웹사이트를 더 빠르고 쾌적하게 이용할 수 있도록 하는 강력한 무기입니다. 여러분의 프로젝트에도 이 기술을 적용해 웹 성능을 한 단계 끌어올려 보세요!