자바스크립트 공부하다 보면 '호이스팅(Hoisting)'이라는 말을 꼭 듣게 되죠? 뭔가 '끌어올린다'는 뜻 같은데, 이게 대체 뭘까요? 🤔
간단히 말하면, 자바스크립트 엔진이 코드를 읽을 때 변수나 함수의 선언 부분을 마치 코드 맨 위로 끌어올리는 것처럼 동작하는 독특한 특징이에요. 그런데 여기서 중요한 점! 선언만 끌어올려지고, 값을 할당하는 부분은 원래 코드 위치에 그대로 남아있답니다. 이게 좀 헷갈릴 수 있는데, 같이 한번 차근차근 살펴볼까요?
var
의 호이스팅var
키워드로 선언된 변수는 호이스팅이 아주 활발하게 일어나요. 사용자의 이름을 출력하는 간단한 코드를 예로 들어볼게요.
printName(); // 어? 아직 userName 만들기 전인데? -> undefined
var userName = "영수"; // 여기서 이름을 "영수"로 할당
function printName() {
console.log(userName);
}
분명 printName()
함수를 호출하는 시점에는 userName
변수에 "영수"라는 값을 넣기 전인데, 에러가 나지 않고 undefined
가 출력돼요. 이게 왜 그럴까요? 바로 호이스팅 때문이에요! 자바스크립트 엔진은 코드를 이렇게 해석하거든요.
🧐 호이스팅 후 실제 동작 순서 (엔진의 생각)
var userName; // 1. userName 선언 부분을 맨 위로 끌어올려요! (값 할당은 아직 X)
function printName() { // 2. 함수 선언도 통째로 위로!
console.log(userName);
}
printName(); // 3. 함수 호출! 이때 userName은 선언은 됐지만 값이 없어서 undefined 출력
userName = "영수"; // 4. 원래 위치에서 드디어 값 할당!
신기하죠? 선언만 쏙! 위로 올라가는 거예요.
let
과 const
도 호이스팅 된다고요? (feat. TDZ)"그럼 let
이랑 const
는 호이스팅 안 되나요?" 하고 궁금하실 수 있어요. 놀랍게도, let
과 const
도 호이스팅이 되긴 해요! 😮 하지만 var
랑은 좀 다르게 동작해요. 바로 TDZ (Temporal Dead Zone) 라는 개념 때문인데요, 우리말로는 '일시적 사각지대' 정도로 부를 수 있겠네요.
TDZ는 변수가 선언된 위치부터 실제 초기화(값 할당) 코드 전까지의 구간을 말해요. let
이나 const
로 선언된 변수는 호이스팅되어서 스코프 최상단으로 올라가긴 하지만, 이 TDZ 구간 안에서는 접근할 수가 없어요. 접근하려고 하면 에러가 퐝!💥 하고 터지죠.
printAge(); // 에러! ReferenceError: Cannot access 'userAge' before initialization
let userAge = 25; // 여기서 userAge 초기화
function printAge() {
console.log(userAge);
}
이 코드도 엔진은 호이스팅을 하긴 해요.
🧐 호이스팅 후 실제 동작 순서 (엔진의 생각)
let userAge; // 1. userAge 선언도 위로 올라가긴 했어요. 하지만 TDZ 시작!
function printAge() { // 2. 함수 선언도 위로!
console.log(userAge);
}
// ---- 여기부터 TDZ 구간 ----
printAge(); // 3. TDZ 안에서 userAge에 접근 시도! -> ReferenceError 발생! 🚨
// ---- 여기까지 TDZ 구간 ----
userAge = 25; // 4. TDZ 끝! 여기서부터 userAge에 접근 가능해요.
let
과 const
는 var
와 달리 선언 전에 접근하면 에러를 내뱉기 때문에, 코드 예측 가능성을 높여주는 더 안전한 방법이라고 할 수 있어요.
함수를 만드는 방법 중 '함수 선언식' ( function 함수이름() { ... }
형태) 은 아주 착하게 호이스팅돼요. 선언 부분만 아니라 함수 내용 전체가 통째로 끌어올려진답니다. 그래서 함수를 정의한 코드보다 앞에서 호출해도 아무 문제 없이 잘 돌아가요!
greet(); // "안녕하세요!" (잘 나오네요?)
function greet() {
console.log("안녕하세요!");
}
// 엔진은 greet 함수 전체를 코드 상단으로 옮겨놓고 실행해요.
이건 정말 편리할 때가 많죠!
이번엔 '함수 표현식' (변수에 함수를 할당하는 형태) 의 호이스팅을 볼게요. 이건 함수 선언식이랑은 좀 다르게 동작해요. 왜냐하면 함수 표현식은 결국 변수에 함수 값을 할당하는 거라서, 변수 호이스팅 규칙을 그대로 따라가거든요.
var
로 선언된 경우var
로 함수 표현식을 만들면, var
변수 호이스팅 규칙에 따라 변수 선언만 위로 올라가고 함수 할당은 제자리에 남아요.
sayHi(); // 에러! TypeError: sayHi is not a function
var sayHi = function() { // 함수를 변수에 할당
console.log("안녕!");
};
🧐 호이스팅 후 실제 동작 순서 (엔진의 생각)
var sayHi; // 1. sayHi 변수 선언만 위로! (아직 함수가 아니라 undefined 상태)
sayHi(); // 2. sayHi는 함수가 아닌 undefined인데 호출하려고 하니 TypeError! 😭 "함수가 아닌데요?"
sayHi = function() { // 3. 원래 위치에서 함수 할당!
console.log("안녕!");
};
let
과 const
로 선언된 경우let
이나 const
로 함수 표현식을 만들면 어떨까요? 네, 예상하신 대로 let
/const
변수 호이스팅 규칙과 TDZ가 적용돼요!
sayHello(); // 에러! ReferenceError: Cannot access 'sayHello' before initialization
let sayHello = function() { // 함수를 변수에 할당
console.log("Hello!");
};
// TDZ 때문에 초기화 전에 접근하면 ReferenceError가 발생해요!
호이스팅, 처음엔 좀 낯설고 헷갈릴 수 있지만 알고 보면 자바스크립트가 코드를 어떻게 이해하고 실행하는지 엿볼 수 있는 재미있는 특징이에요.
var
변수는 선언만 위로 슝~ (값 할당 전에는 undefined
)
let
, const
변수도 선언은 위로 가지만, TDZ 때문에 초기화 전 접근 시 에러! 💥
함수 선언식 (function hi(){}
) 은 통째로 위로 슝~ (선언 전에 호출 가능 👍)
함수 표현식 (const hi = function(){}
) 은 변수 규칙 따라가요 (var
는 TypeError, let
/const
는 ReferenceError 주의!)
호이스팅 동작 방식을 이해하면 코드에서 예상치 못한 오류를 줄이고, 왜 그렇게 동작하는지 더 깊게 파악할 수 있답니다. 물론 가장 좋은 습관은, 헷갈릴 일 없도록 변수나 함수를 사용하기 전에 미리 선언하고 할당하는 것이겠죠? 😉