디자인 시스템에도 다이어트가 필요하다.

번들 사이즈 최적화 기법 - UI Kit

9 min read
Created

2025.07.02.

Last updated

2025.07.05.

엔지니어의 숙명

과학이랑 공학의 결정적인 차이는 비용이라고 어렴풋이 들었던 기억이 난다.

과학자들이 연구한 결과들에 대해 비용을 최대한 줄이는 것이 공학도의 숙명이라고 누가 말해줬는데, 맞는 것 같다.

특히 프론트엔드 엔지니어들은 뭘 만들든 번들 다이어트를 항상 신경써야한다. 그게 숙명이니깐!

UI Kit를 다양한 방식대로 만들면서 느낀, 번들 사이즈를 줄이는 기법에 대해서 정리해보려 한다! 다이어트 생각보다 쉽지 않다는데...

장인은 도구를 탓한다

장인은 도구를 탓하지 않는다?

반은 맞고 반은 틀린 것 같은 게, 애초에 편리한 도구가 나오면 장인의 화려한 솜씨와 함께 시나지가 배로 상승하지 않을까라는 생각이다.

번들러 역시 마찬가지다. 요즘엔 번들러들 성능이 아주 좋다. 라뗴는 말이야 웹팩으로 어쩌고 저쩌고....

Webpack은 장인의 영역

처음엔 webpack으로 시작했다. create-react-app으로 첫 react 앱을 만들면서 익숙해졌다.

하지만 이 녀석으로 라이브러리를 만들 때, 보일러플레이트가 괜히 있는게 아니라는 걸 느꼈다.

이 녀석으로 최적화된 번들링을 얻으려면 설정을 정말 복잡하게 잘 해줘야한다. 진짜 장인의 영역이다.

그 당시 나는 장인이 아니었다... webpackcreate-react-app에만 쓰기로 했다.

구원투수 Rollup

그러던 와중 rollup이 라이브러리를 만들기 좋은 번들러라는 것을 알게되었다.

그 당시, SPA는 webpack, 라이브러리는 rollup로 번들링하면 된다는 것이 업계의 점심(?)이었고

나는 컴포넌트 라이브러리를 구축하기 위해 rollup을 사용해보기로 했다.

ESM을 지원하는 것도 좋지만, 플러그인 중심으로 설정을 구성하는 형식이라 복잡하지 않았던 것이 가장 좋았다.

잘 만들어진 플러그인들이 많아서, 가만히 있는데 알아서 다 해주는 느낌이 났던건 안비밀이다.

세상엔 마법사들이 참 많다는 걸 이걸 사용하면서 깨닫게 되었다.

프론트엔드의 희망 Vite

개인적으로 Vite의 등장은 프론트엔드 생태계의 또 다른 전환점이 된다고 생각한다.

Vite가 인기있었던 이유 중 하나가 Rollup의 플러그인을 그대로 사용할 수 있는 것이었는데, 그렇다는 건?

이것을 가지고 아주 편하게 라이브러리를 만들 수 있다.

게다가 대놓고 라이브러리를 이걸로 만들라는건지, 라이브러리 모드를 제공한다.

그래서 그런지, 요즘에는 컴포넌트 라이브러리를 만들려고 할 때, 고민이 없는 편이다. 그냥 이거 쓰면 된다.

의지를 줄여야 한다

패키지 의존도를 최대한 줄여야한다.다이어트 의지를 줄이는 것이 아니다!

라이브러리는 다른 Node 패키지에 의존하지 않을 때, 가장 번들 사이즈가 작게 나온다.

하지만 어떻게 맨날 퍽퍽한 닭찌찌살만 먹겠는가... 한 번씩 갓 튀겨나온 김이 모락모락 나는 후라이드 치킨도 먹어줘야한다.

손맛이 최고

유틸리티 함수를 작성하거나 비즈니스 로직을 적용할 때, 서드파티 라이브러리들이 생각날 수밖에 없다.

하지만 서드파티 라이브러리를 사용하는 순간 번들에 포함 되기 때문에, 도입할 때 신중해야한다.

트리 쉐이킹을 지원하는 라이브러리를 사용하는 것이 중요하며, 쬐끔 복잡하고 귀찮더라도 프로토타입 메서드를 활용할 수 있다면, 라이브러리를 사용하지 않고 직접 구현하는 것도 번들 사이즈를 줄일 수 있는 방법이다.

특히 스타일링의 경우 CSS Module을 활용하는 것이 가장 이상적이지만, 이미 styled-components 같은 것에 익숙해졌다면 이것 마저 쉽지 않다...

하지만 순수 CSS로 구현하는 것이 호환성은 물론이고 성능이 가장 좋다. 프로젝트가 어떤 스타일링을 채택했는지 상관없이 일관된 스타일을 적용할 수 있기 때문이다.

프로젝트에 떠넘기기

라이브러리를 사용하고자 할때, 필수적으로 필요한 라이브러리라면 피어 의존성으로 구분해서, 프로젝트의 라이브러리를 사용하게끔 할 수 있다.

예를 들어 React 컴포넌트 라이브러리는 react를 사용하는 프로젝트에서 사용할 것이 자명하므로, reactreact-dompeerDependencies로 분류하면 된다.

package.json

{
  "peerDependencies": {
    "react": ">=16.8",
    "react-dom": ">=16.8"
  },
}

호환성을 위해 최소 버전을 최대한 낮게 지정해야하는 것도 중요하다.

예를 들어 Hooks를 사용한다면 16.8 버전 이상을 사용하도록 구성해서, 라이브러리를 사용하는 프로젝트에서 호환되는 버전을 사용하도록 유도할 수 있다.

만약 기술스택이 정해져서 변동이 없는 상황이라면, 기술스택에 해당하는 라이브러리를 모두 피어 의존성으로 추가해서 번들 사이즈를 더 줄일 수 있다.

마지막으로 꼭 필요한 서드파티 라이브러리지만 번들사이즈가 생각보다 클 때, 프로젝트에 떠넘기면 좋다.

대표적으로 애니메이션 라이브러리들이 여기에 해당되는데, 이걸 번들에 포함시키면 요요온거마냥 불어나는 체급에 적지않게 당황할 수 있다.

다이어트 요령

라이브러리를 구축할 때, 번들 사이즈를 줄이는 데 도움이 될 만한 여러가지 요령들이 있다.

다중 엔트리 구성

엔트리를 여러개 구성하면 chunk도 엔트리에 맞게 자동으로 구성해준다.

예를 들어 컴포넌트 모듈 그룹과 스타일이 정의된 모듈 그룹을 각각의 엔트리로 구성하면, react 라이브러리를 사용하지 않는 스타일 모듈에는 react 패키지가 포함되지 않도록 chunk가 분리된다.

vite.config.ts

export default defineConfig({
  // ..
  build: {
    lib: {
      entry: {
        main: resolve(__dirname, 'lib/main.ts'),
        styles: resolve(__dirname, 'lib/core/styles.ts'),
      },
      // ..
    },
  // ..
  },
  // ..
});

React 컴포넌트 라이브러리에는 번들 사이즈에 영향이 미미하지만, 트리 쉐이킹을 최대한 활용되는 특성을 가진 라이브러리라면 효과를 기대해 볼 만하다.

열량 검사

번들 결과를 시각적으로 분석가능하게 해주는 도구들도 좋지만, vite를 사용한다면 rollupOptionsoutput 옵션을 활용해서, 어떤 의존 패키지가 용량을 많이 차지하는지 빠르게 확인 할 수 있다.

vite.config.ts

export default defineConfig({
  // ..
  build: {
    // ..
    rollupOptions: {
      // ..
      output: {
        chunkFileNames: () => `bundle/[name]-[hash].js`,
        manualChunks: (id) => {
          if (id.includes('node_modules')) {
            const module = id.split('node_modules/').pop().split('/')[0];
            return `${module}`;
          }
        },
        // ..
      },
      // ..
    },
  },
  // ..
});

이렇게 구성한 뒤, 빌드를 수행하면, 사이즈와 함께 의존 패키지 이름이 그대로 붙어서 출력된다!

여기서 피어 의존성으로 구분할지, 그냥 안고 갈지 선택하면 된다.

피어 의존성을 너무 많이 요구하면, 프로젝트에서 사용하지 않는 라이브러리를 억지로 설치해야할 수도 있으므로, 그냥 안고 가는 것도 나쁘지 않을 수 있다!

정리

세 줄 요약하면

  • 대부분의 경우 Vite를 채택하면 아주 순조롭게 빌드 할 수 있다.
  • 최대한 서드파티에 의존하지 않고 모듈들을 구성하려고 해야한다.
  • 피어 의존성, 다중 엔트리를 활용하면, 번들 사이즈를 많이 줄일 수 있고 최적화도 더 잘 된다.