frontend/javascript

🟨 1-7. 스코프(Scope)와 호이스팅(Hoisting) — 변수와 함수가 살아 움직이는 방식

mirabo01 2025. 11. 6. 22:05

자바스크립트를 배우면서 가장 많은 혼란을 주는 개념 중 하나가 바로
스코프(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)와의 관계

호이스팅은 사실 “자바스크립트의 실행 컨텍스트” 와 깊은 관련이 있다.
엔진이 코드를 실행할 때는 다음 순서를 따른다.

  1. 변수 및 함수 선언 스캔 (메모리 생성 단계)
  2. 코드 실행 (실제 값 할당 및 로직 수행)

즉, 선언이 먼저 인식되고, 실행은 나중에 진행된다.
그래서 우리가 선언문보다 위에 코드를 써도 에러가 안 나는 것이다.


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) — 구조, 조작, 메서드 완벽 이해
를 통해 실질적인 데이터 구조 다루는 법을 알아보자.