번들 사이즈는 초기 로딩 속도에 직접적인 영향을 미칩니다. 사용하지 않는 코드를 제거하고, 필요한 시점에만 로드하는 것이 핵심입니다.

면접에서 "초기 로딩 속도를 어떻게 개선하나요?"라고 물으면, 번들 분석 → 코드 분할 → Tree-shaking 순서로 설명할 수 있어야 합니다.


번들 분석

BASH
# rollup-plugin-visualizer로 번들 구성 확인
npm install -D rollup-plugin-visualizer
TYPESCRIPT
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    visualizer({
      open: true,          // 빌드 후 자동으로 열기
      gzipSize: true,      // gzip 크기도 표시
      brotliSize: true
    })
  ]
})

코드 분할 전략

TYPESCRIPT
// router/index.ts — 라우트별 코드 분할
const routes = [
  {
    path: '/',
    component: () => import('@/views/HomeView.vue')
  },
  {
    path: '/dashboard',
    component: () => import('@/views/DashboardView.vue'),
    children: [
      {
        path: 'analytics',
        // 청크 이름 지정으로 관련 컴포넌트 그룹핑
        component: () => import(/* webpackChunkName: "dashboard" */ '@/views/AnalyticsView.vue')
      }
    ]
  }
]
VUE
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

// 조건부 로딩 — 사용자가 필요로 할 때만
const RichEditor = defineAsyncComponent(() =>
  import('@/components/RichEditor.vue')
)

const ChartComponent = defineAsyncComponent(() =>
  import('@/components/ChartComponent.vue')
)
</script>

<template>
  <RichEditor v-if="editMode" />
  <ChartComponent v-if="showChart" />
</template>

Tree-shaking 활용

TYPESCRIPT
// 나쁜 예: 전체 라이브러리 import
import _ from 'lodash'
_.debounce(fn, 300)

// 좋은 예: 필요한 함수만 import
import debounce from 'lodash/debounce'
debounce(fn, 300)

// 더 좋은 예: 전용 패키지
import { debounce } from 'lodash-es'  // ESM 버전

// Vue의 API도 tree-shakable
import { ref, computed } from 'vue'  // 사용한 것만 번들에 포함

큰 의존성 대체

PLAINTEXT
moment.js (72KB gzip) → date-fns (tree-shakable) 또는 dayjs (2KB)
lodash (72KB gzip) → lodash-es (tree-shakable)
chart.js → 필요한 컴포넌트만 등록
axios → 네이티브 fetch (별도 라이브러리 불필요)

Vite 빌드 최적화

TYPESCRIPT
// vite.config.ts
export default defineConfig({
  build: {
    // 매뉴얼 청크 분할
    rollupOptions: {
      output: {
        manualChunks: {
          // vendor 라이브러리를 별도 청크로
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'ui-vendor': ['@headlessui/vue']
        }
      }
    },

    // 청크 크기 경고 임계값
    chunkSizeWarningLimit: 500,  // KB

    // CSS 코드 분할
    cssCodeSplit: true,

    // 소스맵 (프로덕션에서는 비활성화 권장)
    sourcemap: false,

    // minify 옵션
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,  // console.log 제거
        drop_debugger: true
      }
    }
  }
})

동적 import와 prefetch

VUE
<script setup lang="ts">
import { onMounted } from 'vue'

onMounted(() => {
  // 유휴 시간에 미리 로드 (prefetch)
  if ('requestIdleCallback' in window) {
    requestIdleCallback(() => {
      import('@/views/HeavyView.vue')
    })
  }
})
</script>

면접 팁

  • 번들 분석이 첫 단계 입니다. 무엇이 크기를 차지하는지 모르면 최적화할 수 없습니다
  • import() 동적 import가 코드 분할의 핵심이라는 것을 알고 있어야 합니다
  • Tree-shaking이 동작하려면 ESM(ES Modules) 형식 이어야 한다는 점을 설명할 수 있으면 좋습니다

요약

번들 사이즈 최적화는 분석(visualizer) → 코드 분할(라우트/컴포넌트) → Tree-shaking(named import) → 의존성 최적화(경량 대체) → 빌드 설정(manualChunks, minify) 순서로 진행합니다. Vue 3와 Vite는 기본적으로 Tree-shaking을 지원하므로, 올바른 import 방식만으로도 큰 효과를 볼 수 있습니다.

댓글 로딩 중...