자바스크립트의 var / let / const 차이와 Babel 변환 방식 정리
ES6 이후에는 let과 const가 기본 선택지입니다.
스코프가 명확하고 동작이 직관적이기 때문입니다.
하지만 브라우저 호환성을 위해 Babel은 여전히 이들을 var로 변환합니다.
이 과정에서 스코프 모델의 차이를 맞추기 위해 여러 보정 작업이 들어갑니다.
이 글에서는 다음 내용을 다룹니다.
var,let,const의 핵심 차이var가 문제가 많은 이유Babel이 ES6 변수를 ES5로 변환하는 방식
Babel이 반복문에서 _loop 헬퍼를 만드는 이유
핵심만 빠르게 정리하여 동작을 이해하는 데 도움이 되는 것을 목표로 합니다.
1. var, let, const의 핵심 차이
var
함수 스코프를 가집니다. 블록 스코프가 적용되지 않습니다.
호이스팅 + 자동 초기화가 이루어집니다. 선언이 위로 끌어올려지고 즉시
undefined가 할당됩니다.재선언이 가능합니다.
TDZ(Temporal Dead Zone)가 존재하지 않습니다.
let / const
블록 스코프를 가집니다.
호이스팅되지만 TDZ가 존재합니다. 초기화 이전 접근 시 ReferenceError가 발생합니다.
재선언이 불가능합니다.
const는 재할당이 불가능합니다.
이 때문에 대부분의 현대 코드에서는 var 대신 let과 const를 사용하는 것이 자연스럽습니다.
2. var가 그렇게 설계된 이유
초기 자바스크립트(1995)는 매우 짧은 일정과 제약 속에서 개발된 언어였습니다.
브라우저 탑재까지 10일의 개발 기간이라는 일정 제약이 있었습니다.
당시에는 C 계열 언어 기반의 함수 스코프 중심 설계 문화가 일반적이었습니다.
자바스크립트는 초기에는 규모가 작은 스크립팅 언어로만 사용될 것으로 예상되었습니다.
이러한 이유로 지금 관점에서는 부족해 보이는 스펙인 var가 등장하게 되었고, 이후 실제 웹 애플리케이션 규모가 커지면서 문제점이 드러난 것입니다.
3. Babel에서 let/const를 var로 변환할 때 문제가 없는가?
일반적인 코드에서는 문제 없이 변환이 가능합니다.
ES6 스코프 규칙이 var만으로는 제대로 표현되지 않는 경우가 있어도 Babel이 내부적으로 헬퍼 코드를 추가해 정확한 의미를 보존합니다.
즉, 의미가 달라져서 변환을 할 수 없는 경우는 거의 없습니다.
대신 반복문에서의 스코프 처리처럼 ES6에서만 가능한 동작은 Babel이 보조 함수를 통해 에뮬레이트합니다.
4. 문제 없이 변환되는 예시
입력 (ES6)
let count = 0;
const name = "JS";
function inc() {
count++;
}
출력 (Babel → ES5)
"use strict";
var count = 0;
var name = "JS";
function inc() {
count++;
}
단순한 변수 선언과 함수는 그대로 var로 전환해도 스펙이 변하지 않습니다.
5. 렉시컬 바인딩(Lexical Binding)이란?
렉시컬 바인딩은 변수가 선언된 문맥(스코프)에 따라 그 변수의 생명 주기와 값이 결정되는 방식을 의미합니다.
let과const는 블록이 실행될 때 각 블록마다 별도의 변수 환경(Lexical Environment) 을 생성합니다.특히 반복문에서는 매 반복마다 새로운 렉시컬 환경이 만들어집니다.
이로 인해 반복문 내부에서 생성된 클로저는 각 iteration의 값을 독립적으로 보존할 수 있습니다.
반면 var는 함수 스코프이기 때문에 이러한 동작을 구현할 수 없습니다.
6. Babel이 반복문에서 _loop 헬퍼를 만드는 이유
아래와 같은 ES6 코드는 반복문 내부에서 클로저가 각 iteration의 값을 캡처하며 동작합니다.
원본 ES6 코드
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i));
}
Babel이 생성하는 코드(단순화)
"use strict";
for (var i = 0; i < 3; i++) {
_loop(i);
}
function _loop(i) {
setTimeout(function () {
console.log(i);
});
}
왜 이렇게 되는가?
ES5에는 반복마다 새로운 스코프를 생성할 방법이 없습니다.
Babel은 iteration 값을
_loop함수의 인자로 넘겨 스코프를 복제한 것처럼 보이도록 처리합니다.덕분에 ES6의 렉시컬 바인딩 동작을 정확하게 재현할 수 있습니다.
7. _loop 없이 var만 사용했을 때의 동작
예를 들어 동일한 코드를 수동으로 var 기반으로 바꾸면 다음과 같습니다.
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i));
}
출력 결과는 다음과 같습니다.
3
3
3
이유는 다음과 같습니다.
콜백 함수들이 모두 하나의 i(최종 값 3) 를 참조하기 때문입니다.
ES5에서 반복마다 새로운 스코프가 만들어지지 않기 때문입니다.
반면 Babel은 _loop 헬퍼 덕분에 ES6과 동일하게 아래처럼 동작합니다.
0
1
2
결론
var는 초기 설계 제약으로 인해 현재 기준에서는 문제가 많은 스펙입니다.ES6의
let과const는 블록 스코프와 렉시컬 바인딩을 통해 안정적인 변수 관리 방식을 제공합니다.Babel은 이러한 ES6 동작을 정확하게 에뮬레이트하기 위해 필요 시
_loop등의 헬퍼 함수를 생성합니다.대부분의 경우
let/const는 의미 손상 없이var로 변환되며, 실제 런타임 동작도 동일하게 유지됩니다.
🧾 작성 참고
이 글은 ChatGPT의 도움을 받아 내용을 정리하였습니다.