Shimming: 본질, 접근 방식 및 작동 원리

저자: IT Sectr 게시일: 2026-05-19 읽는 시간: 8 분

Shimming은 특정 전역 변수나 API를 기대하는 모듈의 호환성을 보장하는 기법입니다. Webpack 생태계에서 shimming은 ProvidePlugin, imports-loader, exports-loader를 통해 구현되며, 소스 코드를 변경하지 않고 레거시 라이브러리를 연결할 수 있습니다. Webpack Documentation (2026)에 따르면, shimming은 모듈 시스템을 지원하지 않는 jQuery 플러그인 및 기타 종속성을 통합하기 위한 핵심 도구로 남아 있습니다.

핵심 요점

  • Shimming은 빌드에서 모듈 호환성을 보장하기 위해 전역 변수와 API를 대체하는 기법입니다.
  • ProvidePlugin은 코드에서 전역 변수에 대한 참조를 감지하면 모듈을 자동으로 가져옵니다.
  • imports-loaderexports-loader는 인터페이스를 추가하거나 변경하여 모듈의 스코프를 관리합니다.
  • Shim은 누락된 기능을 구현하지 않고 기존 호출을 리디렉션한다는 점에서 polyfill과 다릅니다.
  • Webpack은 추가 패키지를 설치할 필요 없이 내장된 shimming 메커니즘을 제공합니다.

Shimming이란 무엇인가?

Shimming은 모듈의 소스 코드를 변경하지 않고 코드와 환경 사이에 호환성 계층을 삽입하는 소프트웨어 기법입니다. JavaScript 빌드의 맥락에서 shimming은 모듈이 모듈 환경에 존재하지 않는 전역 변수(window.$, global.process)에 접근할 때 문제를 해결합니다.

Shim과 polyfill: 주요 차이점

Polyfill은 누락된 기능을 처음부터 구현하여 환경에 새로운 기능을 추가합니다. 예를 들어 core-js는 오래된 브라우저를 위해 Array.prototype.flatMap을 추가합니다. Shim은 기존 호출을 사용 가능한 구현으로 리디렉션하거나 예상되는 전역 객체를 대체합니다. Webpack에서 ProvidePlugin은 전역 변수 $에 대한 참조가 발견되는 모든 곳에 import $ from 'jquery'를 자동으로 삽입하며 코드 변경이 필요하지 않습니다.

주요 차이점은 목적에 있습니다. Polyfill은 존재하지 않는 것을 추가하고, shim은 기존 코드를 실행 환경과 호환되게 만듭니다. 둘 중 선택은 해결하려는 문제, 즉 API 부재 또는 인터페이스 비호환성에 따라 달라집니다.

Webpack에서 Shimming이 작동하는 방식

Webpack은 각 모듈을 고유한 스코프를 가진 격리된 단위로 처리합니다. 라이브러리가 전역 변수 jQuerywindow.$로 참조하면, 모듈 컨텍스트에 이 변수가 없으므로 빌드가 오류로 종료됩니다. ProvidePlugin은 컴파일 단계에서 문제를 해결합니다. 코드에서 식별자 $가 감지되면 플러그인은 파일 시작 부분에 import $ from 'jquery'를 자동으로 삽입합니다.

js
// 원래 코드(레거시 모듈이 전역 jQuery에 접근)
$('.element').hide();

// ProvidePlugin 처리 후(Webpack이 import를 삽입)
import $ from 'jquery';
$('.element').hide();

또한 imports-loader를 사용하면 모듈이 받아야 할 종속성을 명시적으로 지정할 수 있습니다. 라이브러리가 최상위 레벨에서 this를 사용하고 thismodule.exports가 아닌 window를 가리키기를 기대할 때 유용합니다.

ProvidePlugin: 모듈용 전역 변수

ProvidePlugin은 지정된 식별자에 대한 참조를 감지하면 모듈을 자동으로 로드하는 Webpack의 내장 플러그인입니다. 구성은 객체이며, 키는 변수 이름이고 값은 모듈 경로와 내보내는 필드입니다.

플러그인 구성

js
// webpack.config.js
const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      _: 'lodash',
      'window.$': 'jquery',
    }),
  ],
};

ProvidePlugin은 배열 구문을 통한 부분 가져오기를 지원합니다. 예를 들어 [lodash, debounce]는 lodash에서 debounce 함수만 가져오므로 최종 번들의 크기가 줄어듭니다. 이는 모든 킬로바이트가 로드 시간에 영향을 미치는 모바일 프로젝트에서 특히 중요합니다.

imports-loader와 exports-loader

imports-loader는 모듈 시작 부분에 필요한 가져오기를 추가하고, exports-loadermodule.exports를 명시적으로 사용하지 않는 모듈의 내보내기 값을 정의합니다. 이러한 로더는 ProvidePlugin처럼 전역이 아니라 개별 파일 수준에서 작동합니다.

imports-loader로 종속성 수정

js
// webpack.config.js — imports-loader 설정
module.exports = {
  module: {
    rules: [
      {
        test: /legacy-module\.js$/,
        use: [
          {
            loader: 'imports-loader',
            options: {
              imports: [
                'jquery',
                '$',
              ],
            },
          },
        ],
      },
    ],
  },
};

exports-loader는 라이브러리가 전역 변수에 값을 할당하지만 모듈 시스템을 통해 내보내지 않을 때 사용됩니다. 로더는 값을 추출하여 모듈 내보내기로 변환하므로 다른 모듈이 import로 가져올 수 있습니다.

Webpack 구성에서 shimming 설정

Shimmingwebpack.config.js에서 플러그인과 로더의 조합으로 구성됩니다. 일반적인 시나리오에는 전역 변수용 ProvidePlugin과 스코프 변경이 필요한 특정 모듈용 imports-loader가 포함됩니다.

shimming을 위한 Webpack 기본 구성

js
const webpack = require('webpack');
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    globalObject: 'this',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules\/(?!legacy-lib)/,
        use: [
          {
            loader: 'imports-loader',
            options: {
              type: 'commonjs',
              imports: ['jquery', '$'],
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
    }),
  ],
};

output의 globalObject 필드는 최상위 레벨에서 this 참조의 컨텍스트를 정의합니다. 브라우저 환경에서는 'this' 값이 window를 가리키고, React Native 또는 Node.js에서는 global을 가리킵니다. 올바른 값을 선택하면 대상 환경에서 런타임 오류를 방지할 수 있습니다.

shimming 시 발생하는 일반적인 오류

Shimming은 강력하지만 위험한 도구입니다. 잘못된 구성은 번들 내 코드 중복, 이름 충돌, 예기치 않은 런타임 오류로 이어집니다. 개발자들은 ProvidePlugin이 컴파일 단계에서 작동하며 변수에 대한 동적 참조를 처리할 수 없다는 사실을 자주 잊습니다.

전역 변수 충돌

두 플러그인이 서로 다른 버전의 jQuery를 사용하는 경우, ProvidePlugin은 구성에서 먼저 지정된 하나만 대체합니다. 두 번째 라이브러리는 호환되지 않는 버전을 받게 되어 디버깅하기 어려운 오류가 발생합니다. 해결책은 각 라이브러리에 버전을 명시하여 exports-loader를 사용하거나, 중복 모듈을 제외하기 위해 webpack.IgnorePlugin을 적용하는 것입니다.

또 다른 일반적인 오류는 동적 컨텍스트에서 CommonJS의 동기 require 호출을 사용하는 모듈을 shim하려는 것입니다. ProvidePlugin은 정적 식별자만 처리하므로 동적 참조는 수동으로 또는 NormalModuleReplacementPlugin으로 대체해야 합니다.

잘못된 shimming으로 인한 성능 문제

shimming의 잘못된 구성은 번들 크기를 크게 증가시킬 수 있습니다. ProvidePlugin이 수십 개의 전역 변수에 대해 구성된 경우, Webpack은 각 파일에서 해당 변수가 사용되는지 여부와 관계없이 모든 프로젝트 파일에 해당 가져오기를 삽입합니다. 이는 특히 수천 개의 모듈을 가진 대규모 프로젝트에서 중복 코드를 만듭니다.

shimming 문제를 진단하려면 번들 구성을 시각화하는 도구인 webpack-bundle-analyzer를 사용하세요. jQuery 또는 다른 라이브러리가 번들에 여러 번 나타나면 서로 다른 버전이 충돌하거나 ProvidePlugin이 패키지의 서로 다른 버전으로 이어지는 여러 식별자에 대해 구성되었을 가능성이 높습니다. 해결책은 resolve.alias로 종속성 버전을 통합하고 모든 shim 처리된 식별자가 동일한 모듈을 가리키는지 확인하는 것입니다.

shimming의 대안: 리팩토링 및 종속성 업데이트

shimming을 적용하기 전에 라이브러리를 모듈 시스템을 지원하는 버전으로 업데이트할 수 있는지 평가하세요. 많은 레거시 패키지에는 shimming이 필요 없는 현대적인 대안이 있습니다. 예를 들어 jQuery 플러그인은 브라우저 네이티브 API로 대체할 수 있습니다: $.ajaxfetch, $.eachArray.forEach. 리팩토링은 유지보수에 장기적인 이점을 주는 반면, shimming은 구성을 복잡하게 만드는 임시 해결책입니다.

업데이트가 불가능한 경우, 소스 코드를 변경하지 않고 해석 수준에서 모듈을 다른 모듈로 대체할 수 있는 NormalModuleReplacementPlugin을 고려하세요. 이 플러그인은 로더가 적용되기 전인 종속성 그래프 구축 단계에서 작동하며, 컨텍스트와 관계없이 모듈에 대한 모든 참조를 처리합니다. 이는 개별 로더보다 전체 라이브러리를 대체하는 더 깔끔한 해결책입니다.

현대 JavaScript에서의 shimming: ESM과 import maps

브라우저에서 네이티브 ES 모듈의 발전과 import maps의 등장으로 일부 shimming 시나리오는 Webpack 없이 해결할 수 있습니다. Import maps는 빌드 단계 없이 브라우저 수준에서 모듈 이름을 동적으로 다시 매핑할 수 있습니다. 그러나 이 접근 방식은 React Native 및 브라우저 ESM이 없는 다른 환경에서는 지원되지 않으므로, 종속성과 그 버전을 완전히 제어해야 하는 프로덕션 빌드에서는 Webpack을 통한 shimming이 여전히 중요합니다. Import maps와 Webpack shims 중 선택은 대상 플랫폼과 구형 브라우저와의 호환성 요구 사항에 따라 달라집니다.

자주 묻는 질문

shimming은 tree shaking과 어떻게 다릅니까?

Shimming은 호환성을 보장하기 위해 코드를 추가하고, tree shaking은 사용되지 않는 코드를 제거합니다. 이 두 기법은 목적이 반대입니다. shimming은 번들 크기를 늘리고 tree shaking은 줄입니다. 프로덕션 빌드에서는 두 가지가 순차적으로 적용됩니다.

Webpack 없이 shimming을 사용할 수 있습니까?

네, shimming은 Webpack과 독립적인 기법으로 존재합니다 — 예를 들어 HTML의 전역 스크립트나 재내보내기가 있는 ES 모듈을 통해. 그러나 Webpack은 가장 편리한 자동화 도구를 제공합니다: ProvidePlugin과 코드 수동 변경이 필요 없는 로더입니다.

shimming은 빌드 성능에 어떤 영향을 미칩니까?

ProvidePlugin은 AST 컴파일 단계에서 작동하므로 빌드 속도에 영향을 미치지 않습니다. imports-loaderexports-loader는 각 파일 처리에 약간의 시간을 추가합니다. 수백 개의 파일에 사용하면 차이는 전체 빌드 시간의 5~15%가 될 수 있습니다.

언제 shimming을 포기해야 합니까?

모든 종속성이 ES 모듈과 모듈 시스템을 지원한다면 shimming은 불필요합니다. shimming을 포기하면 구성이 단순해지고 번들 크기가 줄어들며 이름 충돌 위험이 낮아집니다. caniuse.com에서 종속성을 확인하는 것이 좋습니다.

shimming은 TypeScript에서 어떻게 작동합니까?

TypeScript에서는 shim 처리된 변수에 대한 추가 타입 선언이 필요합니다. declare const $: any를 추가하거나 @types/jquery로 타입을 설치해야 합니다. ProvidePlugin은 TypeScript 컴파일 후 JavaScript 수준에서 가져오기를 삽입하므로 타입은 별도로 검사됩니다.

요약

  • Shimming은 전역 변수와 API를 대체하여 모듈과 환경의 호환성을 보장하는 기법입니다.
  • ProvidePlugin은 코드에서 지정된 식별자에 대한 참조를 감지하면 모듈을 자동으로 가져옵니다.
  • imports-loader는 특정 파일 시작 부분에 가져오기를 추가하고, exports-loader는 내보내기 값을 정의합니다.
  • Shim은 기능을 구현하지 않고 기존 구현에 대한 호출을 리디렉션한다는 점에서 polyfill과 다릅니다.
  • ProvidePlugin은 컴파일 단계에서 작동하며 변수에 대한 동적 참조를 처리하지 않습니다.
  • output의 globalObject 필드는 대상 환경에서 최상위 레벨의 올바른 컨텍스트를 정의합니다.
  • 현대 모듈 시스템을 지원하지 않는 모듈에만 shimming을 사용하고, ES 모듈이 완전히 지원되면 포기하세요.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기