mimizae 님의 블로그
JavaScript의 Scope에 대해서 (1) 본문

Scope란?
자바스크립트의 스코프(Scope)는 변수나 함수에 접근할 수 있는 유효 범위를 의미한다.
크게 전역 스코프(Global Scope)와 지역 스코프(Local Scope)로 나눌 수 있다.
전역 스코프
- 코드의 어떤 곳에서든 접근이 가능한 최상위 스코프이다.
- 스크립트 모드에서 실행되는 모든 코드의 기본 범위가 된다.
- 전역 변수는 코드 어디에서든 참조할 수 있는 전역 스코프를 가지는 변수이다.
var globalVar = '전역 변수';
function foo() {
console.log(globalVar); // 접근 가능
}
console.log(globalVar); // 접근 가능
지역 스코프
- 함수 자신과 해당 함수의 하위 함수에서만 참조가 가능하다.
그렇기 때문에, 함수 내에서 선언된 매개변수와 변수 함수 외부에서는 유효하지 않다. - 지역 변수는 해당 변수가 선언된 함수 내에서만 접근이 가능한 지역 스코프를 갖는 변수이다.
function foo() {
let localVar = '지역 변수';
console.log(localVar); // 접근 가능
}
console.log(localVar); // ReferenceError
이때, JS는 기본적으로 함수 레벨 스코프를 따른다. 함수가 스코프의 기준이 된다는 뜻이다!
단, let과 const는 예외적으로 블록 레벨 스코프(block-level scope)를 따른다.
이건 아래에서 변수 선언 방식의 차이와 함께 자세히 설명하겠다.
☝🏻 잠깐!! 스크립트 모드(Script Mode)란?
브라우저에서 <script> 태그 안이나 Node.js에서 그냥 JS 파일을 실행할 때의 일반적인 JS 실행 환경을 말한다.
JS 실행에는 아래와 같이 두 가지 모드가 있다.
| 스크립트 모드 (script mode) | 브라우저에서 일반적으로 <script> 태그로 실행되는 기본 모드. |
| 모듈 모드 (module mode) | ES6의 type="module"로 실행되는 코드. 독립적인 모듈 스코프를 가짐. |
<script>
var a = 10;
console.log(a);
</script>
이렇게 <script> 태그 안에서 실행되는 방식이 바로 스크립트 모드이다.
이 모드에서는 자바스크립트가 “웹 브라우저의 전역 공간(window)” 안에서 실행된다.
즉, 전역 스코프 = window 객체의 스코프 라고 생각하면 된다.
<script>
var a = 10;
console.log(window.a); // 10 (전역 변수는 window에 자동 등록됨)
</script>
“스크립트 모드에서 실행되는 모든 코드의 기본 범위가 전역 스코프다.”
결국 이 말은 “일반 <script> 태그 안의 코드는 전역(window)에서 실행되니까, 거기서 선언한 변수는 전역 스코프에 들어간다.”
라는 말이다!
그러나 모듈 모드는 다르다.
ES6 이후로 import/export를 사용하는 모듈은 스크립트 모드와 다르게 독자적인 모듈 스코프를 가진다.
<script type="module">
var a = 10;
console.log(window.a); // undefined
</script>
모듈 모드일 때는 이 코드 전체가 자기만의 독립된 스코프(모듈 스코프) 안에서 실행된다. 즉, 더 이상 window랑 연결되지 않는다는 것이다.
모듈 안에서 선언한 변수는 전역 객체(window)에 붙지 않아 전역 스코프라고 할 수는 없지만, 이 변수들은 여전히 모듈 자체의 최상위 스코프(모듈 스코프) 안에 존재한다.
변수 var, let, const의 차이
세 가지 변수의 차이점은 크게 3가지로 나누어 생각해 볼 수 있다.
1. 변수 재선언 가능 여부
var는 같은 이름으로 변수를 다시 선언할 수 있다. 이 경우, 마지막에 할당된 값이 변수에 저장된다.
var x = 1; var x = 2; // (에러 없음)
기존에 선언해둔 변수의 존재를 잊고 값을 재할당하게 되는 등의 실수가 발생하기 쉬우니 사용에 주의해야 한다.
반면, let과 const는 같은 스코프 내에서 재선언이 불가능하다.
let x = 1; let x = 2; //(에러 발생)
var에 비해서 코드의 안정성을 높여줄 수 있는 방식이다.
2. 변수 값 재할당 가능 여부
var와 let은 변수의 값을 변경할 수 있다. 즉, 변수 선언 및 초기화 이후 반복해서 다른 값을 재할당 할 수 있다.
let x = 1; x = 2; //(에러 없음)
const는 선언 시 반드시 초기화해야 하며, 이후에는 값을 재할당할 수 없는 상수이다.
const x = 1; x = 2; //(에러 발생)
결론적으로 재할당이 필요없는 경우, const를 사용해 불필요한 변수의 재사용을 방지하고, 재할당이 필요한 경우 let을 사용하는 것이 좋다.
3. 스코프 유효 범위
var은 함수 레벨 스코프를 따른다.
함수 레벨 스코프를 따른다는 것은, 변수가 함수 내부에서 선언되었는지 여부에 따라 지역 변수인지 전역 변수인지가 결정된다는 것이다.
즉, 함수 내부에서 선언된 변수는 그 함수 안에서만 유효하고, 블록({ }) 안에 선언했더라도 함수 밖에서는 접근할 수 없다.
var candy = 123; // 전역 변수
console.log(candy); // 123
{
var jelly = 456; // 전역 변수 -> 블록 안에서 선언 됨
}
console.log(jelly); // 456
function () {
var chocolate = 20; // 지역 변수
};
console.log(chocolate); // "chocolate" is not defined -> 레퍼런스 에러
전역 영역에서는 전역 변수만이 참조 가능하고 함수 내 지역 영역에서는 전역과 지역 변수 모두 참조 가능하나
위 예제와 같이 변수명이 중복된 경우, 지역변수를 우선하여 참조한다.
var drink = 'water';
function favorite() {
var drink = 'coke';
console.log(drink);
}
favorite(); // ? coke
console.log(drink); // ? water
💢 var의 문제점!
var 는 자바스크립트 초창기부터 사용되어 왔지만, 여러 가지 구조적 문제점을 가지고 있다.
대표적인 문제점들은 다음과 같다.
1. 함수 레벨 스코프(Function Scope)
- var는 함수 단위로만 스코프를 구분한다. 즉, { } 블록 안에서 선언해도 그 블록 밖에서도 접근이 가능하다.
그 블록은 스코프를 만들지 않기 때문에 함수 전체에서 접근 가능한 것이다.
이로 인해 변수의 유효 범위가 불필요하게 넓어지고, 의도치 않은 변수 접근이나 값 변경이 일어나기 쉽다.
if (true) {
var x = 10;
}
console.log(x); // 10
2. 전역 변수의 남발
- var는 선언 위치에 따라 쉽게 전역 변수가 되어버린다.
특히, 함수 밖에서 선언하면 자동으로 window 객체의 프로퍼티로 등록된다.
var a = 1;
console.log(window.a); // 1
3. for 루프의 스코프 문제
- for문에서 var로 선언한 변수는 루프 블록이 끝난 뒤에도 외부에서 접근이 가능하다.
for (var i = 0; i < 3; i++) {}
console.log(i); // 3
- 반면 let은 블록 레벨 스코프를 따르기 때문에 외부에서 접근할 수 없다.
for (let j = 0; j < 3; j++) {}
console.log(typeof j); // "undefined"
4. 변수 선언 시 var 키워드 생략 허용
- var를 생략하고 변수를 선언해도 자바스크립트는 자동으로 전역 변수로 인식한다.
-> 이는 의도하지 않은 전역화를 초래한다.(내 생각에 이게 제일 문제)
function foo() {
x = 10; // var 키워드 생략
}
foo();
console.log(window.x); // 10 (자동 전역)
5. 변수 호이스팅(Variable Hoisting)
- var로 선언된 변수는 선언부가 스코프의 최상단으로 끌어올려진다.
따라서 선언문보다 앞에서 참조해도 에러가 나지 않고 undefined가 출력된다. 이는 버그의 원인이 되기 쉽다.
console.log(z); // undefined
var z = 10;
이러한 var의 단점을 보완하고자 ES6 때 추가된 것이 let, const이다.
let과 const는 블록 레벨 스코프를 따른다.
코드 블록 (함수, if문, for문, while문, try/catch문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다.
let a = 28; // 전역 변수
{
let a = 100; // 지역 변수
let b = 99; // 지역 변수
} // 블록 레벨
console.log(a); // 28
console.log(b); // ReferenceError: b is not defined -> 블록 밖에서는 접근 불가
그래서, 이 let과 const의 등장이 왜 var의 단점 보완을 의미하는가?
| var의 문제점 | 해결 |
| 블록 내부에서 의도치 않게 접근 가능 | 블록 레벨 스코프를 도입 -> { } 안에서만 유효 |
| 전역 변수 남발 | 선언하지 않으면 에러 발생 -> 의도치 않은 전역화 방지 |
| 중복 선언 | 재선언 불가 -> 같은 스코프에서 같은 이름 사용 불가 |
| 호이스팅 문제 | TDZ(Temporal Dead Zone) 적용 -> 선언 전에 접근하면 ReferenceError |
즉, let과 const는
- 변수의 유효 범위를 명확히 구분하고,
- 의도치 않은 전역 변수나 중복 선언을 방지하며,
- 호이스팅으로 인한 혼란을 줄이기 위해 도입된 개선된 선언 방식이다!!!!!
var, let, const 예제 모음
var flavor = 'choco'
if (true) {
var flavor = 'vanilla'; // if문 안에 넣어도 전역 스코프라 choco -> vanilla로 덮어 씌워짐
}
function changeFlavor() {
var flavor = 'strawberry'; // var는 함수 레벨 스코프이기 때문에 이 함수 안에서 선언된 건 지역 변수라서 함수 안에서만 유효
}
changeFlavor();
console.log(flavor); // vanilla -> changeFlavor()을 호출해도 전역에서는 전역만 접근 가능
let flavor = "choco";
if (true) {
let flavor = "vanilla"; // 블록 레벨이라 블록을 벗어나면 끝나는 녀석 = let, 즉 재할당 되지 않음.
}
function changeFlavor() {
let flavor = "strawberry"; // 블록 레벨이라 이 함수 안에서만 flavor가 strawberry이다.
}
changeFlavor();
console.log(flavor); // choco
@
@
@
다음 글에서 이어집니다. . .
참고자료 모음
모던 자바스크립트 딥 다이브
'JS' 카테고리의 다른 글
| this에 대해서 (0) | 2025.11.11 |
|---|---|
| 실행 컨텍스트 보충 (전역 객체와 전역 코드 평가) (0) | 2025.11.09 |
| 실행 컨텍스트란? (0) | 2025.11.03 |
| JavaScript의 Scope에 대해서 (3) (0) | 2025.11.03 |
| JavaScript의 Scope에 대해서 (2) (0) | 2025.10.26 |