본문 바로가기
기술의기록

JavaScript 스코프 호이스팅, 정말 필요할까? 번들러의 숨겨진 함정

by Jeremy Winchester 2025. 8. 8.
반응형

안녕하세요, 개발자 여러분! 오늘은 정말 흥미로운 주제를 가지고 왔어요. 혹시 여러분은 웹팩, 롤업, 파셀 같은 번들러를 사용하면서 '스코프 호이스팅'이라는 최적화 기법에 대해 들어보신 적 있나요?

처음 들으면 뭔가 엄청 좋은 최적화처럼 느껴지는데... 정말 그럴까요? 사실 이 기법에는 우리가 모르는 큰 함정들이 숨어있답니다. 지금부터 하나씩 파헤쳐볼게요! 💡

🔍 스코프 호이스팅이 뭔데?

먼저 스코프 호이스팅이 무엇인지 간단히 알아볼까요?

예를 들어 이런 코드가 있다고 해봅시다:

 
 
javascript
// math.js
export function add(a, b) {
  return a + b;
}

// main.js  
import {add} from './math';
console.log(add(2, 3));

번들러가 스코프 호이스팅을 적용하면 이렇게 변해요:

 
 
javascript
function add(a, b) {
  return a + b;
}
console.log(add(2, 3));

각 모듈을 함수로 감싸는 대신, 하나의 스코프에 모든 코드를 합쳐버리는 거죠. 얼핏 보면 번들 크기도 줄어들고 성능도 좋아질 것 같은데요...

💥 문제의 시작: 코드 스플리팅과의 충돌

여기서 문제가 시작돼요. 실제 프로젝트에서는 코드 스플리팅을 많이 사용하잖아요? 여러 페이지가 있고, 공통 라이브러리들(React, lodash 등)을 따로 번들로 분리하는 경우가 많죠.

예를 들어 이런 상황을 생각해보세요:

 
 
javascript
// entry-a.js
import React from 'react';
import './a1';
import './a2';

// entry-b.js  
import React from 'react';
import './b1';
import './b2';

번들러는 똑똑하게 React 같은 공통 의존성을 별도 번들로 분리해줘요. 그런데 여기서 스코프 호이스팅이 문제를 일으켜요!

🚨 가장 심각한 문제: 실행 순서 꼬임

JavaScript 모듈의 가장 중요한 특징 중 하나가 바로 실행 순서예요. 모듈들이 올바른 순서로 실행되어야 하는데, 스코프 호이스팅이 이를 망가뜨리는 경우가 있어요.

실제 예시를 보여드릴게요:

 
 
javascript
// 원래 실행 순서 (번들링 전)
// shared1 → a1 → shared2 → a2

// 스코프 호이스팅 후  
// shared1 → shared2 → a1 → a2

보시다시피 실행 순서가 완전히 바뀌어버려요! 이는 예상치 못한 버그를 일으킬 수 있어요. 특히 모듈에서 전역 상태를 변경하거나 부수 효과(side effect)가 있는 코드들은 심각한 문제가 생길 수 있답니다.

🎯 다른 숨겨진 문제들

this 값이 사라져요!

또 다른 문제는 this 값이 엉뚱하게 바뀌는 거예요:

 
 
javascript
// 원래 코드
import * as foo from './foo';
foo.bar(); // foo 객체가 this가 됨

// 스코프 호이스팅 후
function bar() {
  console.log(this); // undefined가 됨!
}
bar();

모듈 시스템에서 this는 해당 모듈 객체를 가리켜야 하는데, 스코프 호이스팅 후에는 undefined가 되어버려요.

💡 해결책은 있어요!

그렇다면 어떻게 해결할까요?

Parcel의 접근 방식이 흥미로워요. 공유되는 모듈들을 함수로 감싸서 필요할 때 호출하는 방식이에요:

 
 
javascript
import modules from 'shared.bundle.js';

modules['shared1']();
console.log('a1');
modules['shared2']();
console.log('a2');

Webpack은 조건부 모듈 연결(module concatenation)을 구현해서, 같은 번들 내에서만 안전하게 합칠 수 있는 모듈들만 스코프 호이스팅을 적용해요.

🤔 정말 스코프 호이스팅이 필요할까?

솔직히 말하면... 글쎄요? 🤷‍♀️

초기에 Rollup이 등장했을 때는 코드 스플리팅이 없었어서 스코프 호이스팅이 유용했어요. 하지만 요즘처럼 복잡한 애플리케이션에서는:

  • 실제로 스코프 호이스팅의 혜택을 받는 모듈이 5% 미만
  • 대부분의 모듈은 결국 함수로 감싸져야 함
  • 복잡성 대비 성능 향상이 미미함

🌟 앞으로의 방향

Parcel v3에서는 아예 스코프 호이스팅을 제거하는 것도 고려하고 있다고 해요. 대신 다른 최적화 기법들에 집중하는 거죠:

  • 트리 셰이킹 (Tree shaking)
  • 데드 코드 제거 (Dead code elimination)
  • 상수 폴딩 (Constant folding)

🎉 마무리하며

개발하다 보면 '최신이고 좋아 보이는' 기술이 항상 정답은 아니라는 걸 느껴요. 스코프 호이스팅도 마찬가지죠. 이론적으로는 좋아 보이지만, 실제 사용해보면 예상치 못한 문제들이 많이 생겨요.

여러분도 새로운 기술을 도입할 때는 항상 신중하게 검토해보세요. 특히 번들러 설정을 만질 때는 더욱 조심스럽게! 🔧

오늘 포스팅이 도움이 되셨나요? 댓글로 여러분의 경험도 공유해주세요!

반응형