
1. 스코프란?
스코프는 유효범위다. 쉽게 말해서, 변수나 함수가 어디까지 사용될 수 있는지를 정하는 범위라고 할 수 있다.
우리는 이미 함수를 통해 스코프를 경험했다. 예를 들어, 함수의 매개변수는 함수 안에서만 사용할 수 있다고 했는데, 이것이 바로 스코프의 개념이다. 매개변수가 유효한 범위가 함수 내부로 한정되기 때문이다.
모든 식별자(변수명, 함수명 등)는 자신이 선언된 위치에 따라 사용할 수 있는 범위가 결정된다. 이것이 스코프의 기본 원리다. 자바스크립트 엔진은 이 스코프를 보고 어떤 변수를 사용해야 할지 찾아내는데, 이를 식별자 결정이라고 한다.
코드가 실행될 때는 항상 주변 환경이 고려되는데, 이를 렉시컬 환경이라고 한다. 쉽게 말해:
* 렉시컬 환경은 코드가 어디서 실행되고 주변에 어떤 코드들이 있는지를 나타낸다
* 이런 코드의 문맥은 렉시컬 환경으로 구성되며, 이를 실제로 구현한 것이 실행 컨텍스트다
* 모든 코드는 이 실행 컨텍스트 안에서 평가되고 실행된다
스코프가 왜 필요할까? 만약 스코프가 없다면, 같은 이름의 변수를 프로그램 전체에서 단 하나만 사용할 수 있을 것이다. 하지만 스코프 덕분에 같은 이름의 변수도 다른 범위에서는 충돌 없이 사용할 수 있다.
변수 선언 시 주의할 점:
* var로 선언한 변수는 같은 스코프 안에서 중복 선언이 가능하다 (이는 예상치 못한 문제를 일으킬 수 있음)
* let과 const로 선언한 변수는 같은 스코프 안에서 중복 선언이 불가능하다 (더 안전한 코드 작성 가능)
2. 스코프의 종류
스코프는 크게 전역 스코프와 지역 스코프로 나눌 수 있다. 변수는 선언된 위치에 따라 전역 또는 지역 스코프를 갖게 된다.
2-1) 전역과 전역 스코프
전역은 코드의 가장 바깥 영역을 말한다. 전역에서 선언된 변수를 전역 변수라고 하며, 이는 프로그램 어디서든 사용할 수 있다.
let globalVar = "나는 전역 변수"; // 전역 스코프
function someFunction() {
console.log(globalVar); // 전역 변수는 함수 내부에서도 접근 가능
}
2-2) 지역과 지역 스코프
지역은 함수 내부 영역을 말한다. 지역 변수는 자신이 속한 지역과 그 하위 지역에서만 사용할 수 있다.
function outer() {
let localVar = "나는 지역 변수"; // 지역 스코프
console.log(localVar); // 접근 가능
}
console.log(localVar); // 오류! 지역 변수는 함수 외부에서 접근 불가
2-3) 스코프 체인
함수 안에 또 다른 함수가 있는 것을 함수의 중첩이라고 한다. 이때
- 안쪽에 있는 함수를 중첩 함수
- 바깥쪽 함수를 외부 함수라고 부른다
function outer() { // 외부 함수
let message = "외부";
function inner() { // 중첩 함수
let greeting = "안녕하세요";
console.log(message); // 외부 함수의 변수도 사용 가능
console.log(greeting); // 자신의 변수 사용 가능
}
inner();
// console.log(greeting); // 오류! 중첩 함수의 변수는 외부에서 접근 불가
}
이렇게 함수가 중첩되면 스코프도 계층 구조를 갖게 되는데, 이를 스코프 체인이라고 한다. 스코프 체인의 특징은
1. 안쪽 함수는 바깥쪽 함수의 변수를 사용할 수 있다
2. 바깥쪽 함수는 안쪽 함수의 변수를 사용할 수 없다
3. 모든 스코프는 결국 가장 바깥인 전역 스코프로 연결된다
자바스크립트 엔진은 변수를 찾을 때 현재 스코프에서 시작해서 점점 바깥쪽으로 찾아가는데, 이것이 바로 스코프 체인을 통한 변수 검색이다.
let global = "전역";
function outer() {
let outer = "외부";
function inner() {
let inner = "내부";
console.log(inner); // "내부" (현재 스코프)
console.log(outer); // "외부" (상위 스코프)
console.log(global); // "전역" (최상위 스코프)
}
inner();
}
3. 스코프 체인의 식별자 검색 방식
3-1) 스코프 체인에 의한 변수 검색
자바스크립트 엔진은 변수를 찾을 때 스코프 체인을 따라 현재 스코프에서 시작해서 위로 올라가며 찾는다. 절대 아래로 내려가면서 찾지는 않는다.
쉽게 말하면
let greeting = "안녕!"; // 전역 변수
function outer() {
let count = 0; // outer 함수의 지역 변수
function inner() {
let message = "반가워!"; // inner 함수의 지역 변수
console.log(greeting); // "안녕!" - 전역 변수 접근 가능
console.log(count); // 0 - outer의 변수 접근 가능
console.log(message); // "반가워!" - 자신의 변수 접근 가능
}
inner();
console.log(message); // 오류! inner의 변수는 접근 불가
}
위 코드에서 볼 수 있듯이
- 안쪽(하위) 스코프에서는 바깥(상위) 스코프의 변수를 자유롭게 사용할 수 있다
- 하지만 바깥(상위) 스코프에서는 안쪽(하위) 스코프의 변수를 사용할 수 없다
3-2) 스코프 체인에 의한 함수 검색
함수도 결국 변수에 할당되는 값이기 때문에 똑같은 규칙이 적용된다. 즉, 함수를 찾을 때도 동일한 방식으로 스코프 체인을 따라 검색한다.
function greeting() {
console.log("안녕!");
}
function outer() {
function greeting() {
console.log("반가워!");
}
greeting(); // "반가워!" - 현재 스코프에서 찾은 함수를 실행
}
greeting(); // "안녕!" - 전역 스코프의 함수를 실행
outer(); // "반가워!" - outer 안의 greeting 함수가 실행됨
이처럼 함수도 변수와 마찬가지로
- 가장 가까운 스코프에서부터 찾기 시작한다
- 현재 스코프에서 찾지 못하면 상위 스코프로 올라가며 검색한다
- 전역 스코프까지 올라가도 찾지 못하면 오류가 발생한다
따라서 스코프는 "변수를 검색하는 규칙"이라고 하기보다는, "식별자(변수명, 함수명 등)를 검색하는 규칙"이라고 표현하는 것이 더 정확하다.
4. 함수 레벨 스코프와 블록 레벨 스코프
함수 레벨 스코프는 오직 함수의 중괄호 {} 안에서만 지역 스코프가 만들어지는 것을 말한다. var로 선언한 변수는 이 함수 레벨 스코프를 따른다.
function example() {
var x = 1;
if (true) {
var x = 2; // 같은 변수 x를 덮어씀
console.log(x); // 2
}
console.log(x); // 2 - if문 블록은 새로운 스코프를 만들지 않음
}
example();
반면, 블록 레벨 스코프는 모든 중괄호 {} (함수, if문, for문, while문 등)에서 새로운 스코프가 생성되는 것을 말한다. let과 const는 이 블록 레벨 스코프를 따른다.
function example() {
let x = 1;
if (true) {
let x = 2; // 새로운 변수 x를 생성
console.log(x); // 2
}
console.log(x); // 1 - 바깥의 x는 그대로 유지
}
example();
5. 렉시컬 스코프 (정적 스코프)
렉시컬 스코프는 함수가 선언된 위치에 따라 상위 스코프가 결정되는 것을 말한다. 즉, 함수를 어디서 호출하는지가 아니라, 어디에 선언했는지가 중요하다.
let value = "전역";
function outer() {
let value = "외부";
function inner() {
console.log(value); // "외부"
}
return inner;
}
let innerFn = outer();
innerFn(); // inner 함수는 자신이 선언된 outer 함수의 value를 기억하고 있음
중요한 특징:
1. 함수의 상위 스코프는 함수가 정의된 위치에 따라 고정된다
2. 함수가 어디서 호출되든 이 상위 스코프는 변하지 않는다
3. 함수는 자신이 정의될 때의 환경(스코프)을 기억한다
예를 들어 위 코드에서:
- inner 함수는 outer 함수 안에서 정의되었다
- 따라서 inner 함수의 상위 스코프는 outer 함수의 스코프다
- inner 함수를 어디서 호출하든, outer 함수의 value인 "외부"를 참조한다
이런 방식은 코드를 예측하기 쉽게 만들어주고, 유지보수를 용이하게 한다.
'JS' 카테고리의 다른 글
모던 자바스크립트 딥다이브 15장 - let, const 키워드와 블록 레벨 스코프 (0) | 2025.02.20 |
---|---|
모던 자바스크립트 딥다이브 14장 - 전역 변수의 문제점 (0) | 2025.02.19 |
모던 자바스크립트 딥다이브 12장 - 함수 (0) | 2025.02.17 |
모던 자바스크립트 딥다이브 11장 - 원시 값과 객체의 비교 (1) | 2025.02.16 |
모던 자바스크립트 딥다이브 10장 - 객체 리터럴 (1) | 2025.02.15 |