🟨 1-7. 스코프(Scope)와 호이스팅(Hoisting) — 변수와 함수가 살아 움직이는 방식
자바스크립트를 배우면서 가장 많은 혼란을 주는 개념 중 하나가 바로
스코프(Scope) 와 호이스팅(Hoisting) 이다.
변수를 선언했는데 값이 예상과 다르게 나오거나,
함수를 아래쪽에 적었는데도 실행되는 이유가 바로 이 두 개념 때문이다.
이번 글에서는
‘자바스크립트가 코드를 실행할 때 실제로 무슨 일이 일어나는지’를
눈앞에 그리듯 차근히 설명해보자.
1. 스코프(Scope)란 무엇인가
스코프란 “변수와 함수가 유효한 범위”를 말한다.
즉, 어떤 변수를 어디서 접근할 수 있고 어디서는 접근할 수 없는지를 결정하는 규칙이다.
예를 들어, 아래 코드를 보자.
let name = "기범"; // 전역 변수
function greet() {
let message = "안녕하세요"; // 지역 변수
console.log(message + ", " + name);
}
greet();
console.log(message); // ❌ ReferenceError
name은 함수 바깥에서 선언되었으므로 어디서나 접근 가능하지만,
message는 함수 안에서만 선언되었기 때문에 함수 밖에서는 사용할 수 없다.
이렇게 “변수가 살아 있는 범위”가 바로 스코프다.
2. 전역 스코프와 지역 스코프
자바스크립트에서 스코프는 크게 두 가지로 나뉜다.
전역 스코프(Global Scope)
코드의 어디서든 접근 가능한 영역이다.
전역 변수는 편리하지만, 관리가 어렵다.
다른 함수나 파일에서 쉽게 덮어쓰기(overwrite)될 수 있기 때문이다.
let user = "홍길동";
function printName() {
console.log(user); // 접근 가능
}
printName();
전역 변수는 유지보수성 측면에서 좋지 않다.
그래서 대부분의 실무에서는 변수 충돌을 막기 위해 모듈, 함수 스코프를 활용한다.
지역 스코프(Local Scope)
함수나 블록 내부에서 선언된 변수는 해당 블록 내부에서만 접근할 수 있다.
function test() {
let x = 10;
console.log(x); // 10
}
console.log(x); // ❌ ReferenceError
지역 변수는 함수가 종료되면 사라진다.
이걸 흔히 변수의 생명주기(Lifecycle) 라고 부른다.
3. 블록 스코프 vs 함수 스코프
자바스크립트는 과거엔 함수 스코프만 존재했다.
즉, 중괄호 {} 로 감싸더라도 함수가 아니면 스코프가 만들어지지 않았다.
if (true) {
var a = 10;
}
console.log(a); // 10 (❗ var는 블록을 무시함)
하지만 let과 const가 도입된 이후부터는 블록 스코프가 생겼다.
if (true) {
let b = 20;
}
console.log(b); // ❌ ReferenceError
이제는 중괄호 {} 하나하나가 독립적인 스코프가 될 수 있다.
이 차이는 var와 let/const를 구분할 때 아주 중요하다.
4. 스코프 체인(Scope Chain)
자바스크립트는 변수를 찾을 때 안에서부터 바깥으로 단계적으로 탐색한다.
이 구조를 “스코프 체인”이라고 부른다.
let name = "전역";
function outer() {
let name = "outer";
function inner() {
let name = "inner";
console.log(name);
}
inner();
}
outer(); // inner
console.log(name)은 inner → outer → 전역 순서로 탐색한다.
즉, 가장 가까운 스코프의 변수가 우선된다.
이것이 변수 섀도잉(variable shadowing) 이라고 불리는 현상이다.
(같은 이름의 변수가 더 안쪽에서 외부 변수를 가린다.)
5. 렉시컬 스코프(Lexical Scope)
자바스크립트의 스코프는 렉시컬 스코프(정적 스코프) 를 따른다.
즉, 함수를 “어디서 선언했는지” 에 따라 스코프가 결정된다.
let x = 1;
function first() {
let x = 2;
second();
}
function second() {
console.log(x);
}
first(); // 1
second()는 first() 안에서 호출되었지만,
선언된 위치를 기준으로 상위 스코프(전역)에서 x를 찾는다.
이건 “함수 호출 위치”가 아니라 “함수 정의 위치”로 스코프가 정해진다는 뜻이다.
이 렉시컬 스코프 개념은 클로저(Closure) 를 이해할 때 핵심적인 전제다.
6. 호이스팅(Hoisting) 이란
이제 자바스크립트의 또 다른 독특한 실행 방식인 호이스팅을 살펴보자.
호이스팅이란, 자바스크립트 엔진이 코드를 실행하기 전에
변수와 함수 선언을 메모리의 최상단으로 끌어올리는 동작을 말한다.
쉽게 말해서, “자바스크립트는 코드를 한 줄씩 읽기 전에, 변수 선언을 미리 스캔해둔다.”
예시를 보자.
console.log(message);
var message = "안녕";
결과는 undefined다.
왜냐하면 실제로는 이렇게 동작하기 때문이다 👇
var message; // 선언이 위로 끌어올려짐
console.log(message); // undefined
message = "안녕"; // 값 대입
즉, 선언은 위로 올라가지만 값의 초기화는 올라가지 않는다.
7. let과 const는 다르게 호이스팅된다
let과 const 또한 호이스팅되지만,
“일시적 사각지대(TDZ, Temporal Dead Zone)” 때문에 선언 전 접근이 불가능하다.
console.log(count); // ❌ ReferenceError
let count = 10;
엔진 입장에서는 count 변수를 인식하고 있지만,
초기화가 끝나기 전에는 접근할 수 없게 막아둔 것이다.
이 덕분에 “선언 전에 사용하는 버그”를 방지할 수 있다.
8. 함수의 호이스팅
함수 선언문과 함수 표현식의 차이를 복습해보자.
sayHello(); // ✅ 실행됨
function sayHello() {
console.log("Hello!");
}
함수 선언문은 전체가 호이스팅되기 때문에,
코드 어디에서든 호출할 수 있다.
반면에 함수 표현식은 다르다.
greet(); // ❌ ReferenceError
const greet = function() {
console.log("Hi!");
};
변수 선언만 호이스팅되고, 실제 함수 대입은 실행 시점에 이루어진다.
즉, “선언문은 메모리에 미리 등록되고, 표현식은 런타임에 생성된다.”
9. 실행 컨텍스트(Execution Context)와의 관계
호이스팅은 사실 “자바스크립트의 실행 컨텍스트” 와 깊은 관련이 있다.
엔진이 코드를 실행할 때는 다음 순서를 따른다.
- 변수 및 함수 선언 스캔 (메모리 생성 단계)
- 코드 실행 (실제 값 할당 및 로직 수행)
즉, 선언이 먼저 인식되고, 실행은 나중에 진행된다.
그래서 우리가 선언문보다 위에 코드를 써도 에러가 안 나는 것이다.
10. 실무에서의 권장 패턴
- 변수는 항상 사용하기 직전에 선언
- let/const를 사용해 TDZ의 보호를 받자
- 전역 변수는 최소화하고, 함수 또는 블록 내부에서 선언
- 함수는 선언문보다 표현식(또는 화살표 함수) 으로 정의하는 것이 명확
아래 코드는 그 원칙을 모두 지킨 깔끔한 예시다.
function init() {
const userName = "기범";
const greet = () => console.log(`안녕하세요, ${userName}님!`);
greet();
}
init();
11. 정리
개념 설명 주의할 점
| 스코프 | 변수가 접근 가능한 영역 | 전역 변수 남발 금지 |
| 함수 스코프 | var만 적용됨 | let/const는 블록 단위 |
| 렉시컬 스코프 | 선언 위치 기준으로 상위 스코프 결정 | 호출 위치와 혼동 금지 |
| 호이스팅 | 선언이 코드 위로 끌어올려짐 | let/const는 TDZ로 보호 |
| 실행 컨텍스트 | 코드 실행 전 메모리 등록 단계 | 선언 → 실행 순서 이해 필수 |
12. 마무리
스코프와 호이스팅은 자바스크립트의 실행 원리를 이해하는 데 있어 가장 중요한 개념이다.
이 두 가지를 제대로 이해하면
왜 변수 참조 에러가 나는지, 왜 함수가 선언 전에 실행되는지 자연스럽게 설명할 수 있다.
“코드를 읽는 사람보다 자바스크립트 엔진이 먼저 변수와 함수를 인식한다.”
이 말이 바로 스코프와 호이스팅의 본질이다.
다음 편에서는
1-8. 객체(Object)와 배열(Array) — 구조, 조작, 메서드 완벽 이해
를 통해 실질적인 데이터 구조 다루는 법을 알아보자.