3월 27일 테코톡을 앞두고, 발표 내용을 정리해봤습니다.
주제: 호이스팅이란?
부제: 편리함일까, 함정일까?
여러분들은 위 키워드들을 한 번쯤은 사용해보셨을 거예요.
이 키워드들마다 호이스팅이 다르게 동작하는데요,
호이스팅이란 "코드를 실행하기 전에, 선언문이 해당 범위의 맨 위로 끌어올려지는 것처럼 보이는 현상"을 말합니다.
var
호이스팅을 통해 먼저 호이스팅이 어떻게 동작하는지 알아보겠습니다.
다음 구문의 결과는 어떻게 될까요?
console.log(score); // 결과는❓
var score = 80;
undefined
가 출력됩니다.
console.log(score); // undefined
var score = 80;
왜 undefined
가 출력될까요?
앞에서 호이스팅은 "선언문이 해당 범위의 맨 위로 끌어올려지는 것처럼 동작한다"고 했는데요, 이를 코드로 표현해보면 다음과 같습니다.
var score; 👈 선언문
console.log(score); // undefined
var score = 80;
이미 선언이 되어 있기 때문에 에러 없이 score
를 호출할 수 있고, 값을 얻을 수 있는 것입니다.
그럼 왜 값이 undefined
인지는 메모리를 보면서 이해해보겠습니다.
호이스팅이 발생하면 선언문이 먼저 실행됩니다.
그래서 score
라는 식별자가 메모리에 할당되죠.
그리고 var
호이스팅은 값을 undefined
로 초기화합니다.
그래서 출력을 했을 때 값이 undefined
가 나오는 것입니다.
이후에 실제 할당 구문을 거치면 80
이라는 의도한 값이 메모리에 정상적으로 저장되고,
이후에 출력을 하게 되면 80
이 출력됩니다.
다음 결과는 어떻게 예상하시나요?
var
호이스팅과 마찬가지로, 선언문보다 위에서 함수를 호출하고 있습니다.
foo(); // 결과는❓
function foo() {
console.log("foo");
}
결과는 "foo"
가 출력됩니다.
var
호이스팅과 똑같이 동작했다면 undefined
가 출력돼야 할 것 같은데,
정상적으로 값이 나오네요. 왜 그럴까요?
foo(); // "foo"
function foo() {
console.log("foo");
}
메모리를 보면서 이해해봅시다.
호이스팅이 발생하면서 foo
식별자가 메모리에 등록됩니다.
하지만 var
호이스팅과는 다르게, 값이 undefined
가 아닌 함수의 본문으로 초기화됩니다.
그래서 선언문보다 먼저 호출하더라도 정상적으로 값이 출력되는 것입니다.
정리해보면,
선언 전에 접근할 수 있다는 공통점이 있지만,
var
호이스팅은 undefined로 초기화되고,함수 선언식
호이스팅은 함수 본문, 즉 실제 값으로 초기화된다는 차이점이 있습니다.다음 결과는 어떻게 예상하시나요?
이번에는 함수인데 var
키워드를 사용했습니다.
foo(); // 결과는❓
var foo = () => {
console.log("foo");
}
결과는 TypeError
가 발생합니다.
var
호이스팅을 따랐다면 undefined
가 출력될 것 같고,
함수 선언식의 호이스팅을 따랐다면 "foo"
가 정상적으로 출력될 것 같은데 말이죠.
foo(); // TypeError: foo is not a function
var foo = () => {
console.log("foo");
}
일단 정답은 var
호이스팅을 따른다는 것입니다.
그렇다면, 왜 에러가 발생하는지 메모리를 통해 살펴보겠습니다.
일단 호이스팅이 발생하면서 foo
식별자가 메모리에 등록됩니다.
그리고 var
호이스팅을 따르기 때문에, undefined
로 초기화되죠.
그래서 에러가 발생하는 것입니다.
undefined
는 함수가 아니기 때문에 함수로 호출할 수 없습니다.
이것이 바로 "foo is not a function"
에러가 발생하는 이유입니다.
실제 할당 구문에서 함수 본문이 등록된 이후에 호출해야, 정상적으로 값이 출력됩니다.
지금까지 봤듯이, undefined
는 예상치 못한 버그를 발생시킬 수 있습니다.
개발자가 변수에 실제 값이 할당되었다고 착각할 수 있기 때문이죠.
그렇다면, undefined
로 초기화되지 않게 하는 방법은 없을까요?
그래서 등장한 개념이 바로 TDZ (Temporal Dead Zone)
입니다.
TDZ
는 일시적 사각지대로,
선언되기 전에 변수에 접근하는 것을 금지합니다.
TDZ
가 적용되는 키워드는 다음과 같습니다:
이번에는 함수 표현식에 const
키워드를 사용했습니다.
결과가 어떻게 될까요?
foo(); // 결과는❓
const foo = () => {
console.log("foo");
}
참조 에러(ReferenceError)가 발생합니다.
foo(); // ReferenceError
const foo = () => {
console.log("foo");
}
좀 더 자세한 에러 메시지를 보면,
초기화 전에 'foo'에 접근할 수 없습니다.
라고 나옵니다.
이것은 바로 TDZ(Temporal Dead Zone)
때문에 발생한 에러입니다.
메모리를 보면서 더 깊이 이해해봅시다.
호이스팅이 발생하면서 foo
식별자는 메모리에 등록됩니다.
그리고 메모리에 있다는 것은, 선언문보다 앞서 접근할 수 있다는 의미이기도 하죠.
하지만 접근할 수는 없습니다.
왜냐하면 TDZ
가 실제 선언문이 실행되기 전까지 해당 식별자를 보호하기 때문입니다.
따라서 이전에 접근을 시도하면 바로 참조 에러(ReferenceError)
가 발생하게 됩니다.
이렇게 실제 선언문이 실행되어 함수 본문이 메모리에 올라간 이후에야
비로소 foo
함수에 접근할 수 있습니다.
그 결과, 예상한 대로 "foo"
가 잘 출력됩니다.
지금까지는 이론 중심으로 호이스팅에 대해 살펴봤습니다.
이제는 호이스팅을 공부하면서 들었던 의문들에 대해
함께 이야기해보려고 합니다.
"호이스팅이 존재하는 이유는 무엇일까요?"
다른 말로, 호이스팅은 왜 탄생하게 되었을까요?
물론 이는 자바스크립트를 설계한 개발자의 의도에 가까운 부분이라,
정확한 이유를 단정짓기는 어렵습니다.
하지만 여러 가설 중에서 저는 다음 설명에 가장 공감이 되었습니다.
초창기 자바스크립트는 초보자도 쉽게 사용할 수 있도록 설계되었습니다.
실행 도중에 발생하는 오류를 줄이고,
코드 작성 순서에 덜 민감하게 하여 유연성을 제공하는 것이 목표였다고 합니다.
호이스팅은 이러한 설계 철학에서 자연스럽게 나온 특징이라고 볼 수 있습니다.
실제로 저희가 자바스크립트로 프로그래밍을 하면서 에러를 많이 마주하지 않는 것도,
이러한 철학에서 비롯된 결과라고 생각할 수 있습니다.
앞서 살펴봤듯이, undefined로 초기화되어 버그를 유발할 수 있는 문제점도 있었고,
이를 방지하기 위해 TDZ(일시적 사각지대)라는 개념까지 등장했습니다.
그렇다면, 차라리 호이스팅 자체를 없애버리면 되지 않을까요?
그런데도 자바스크립트는 여전히 호이스팅을 유지하고 있습니다. 왜일까요?
그 이유는 두 가지입니다.
첫 번째 이유는, 레거시 코드와 하위 호환성 유지 때문입니다.
이미 세상에는 호이스팅을 활용해서 작성된 수많은 코드가 존재합니다.
만약 지금 갑자기 호이스팅이 사라진다면, 기존 코드에서 수많은 오류가 발생할 것이고, 그걸 일일이 수정하는 데 막대한 비용과 시간이 소모될 것입니다.
그래서 호이스팅은 쉽게 제거할 수 없는 기능입니다.
두 번째 이유는, 호이스팅이 오히려 장점이 될 수도 있기 때문입니다.
예를 들어 함수 선언식의 경우, 선언문보다 먼저 함수를 호출해도 정상적으로 동작합니다.
이 특성을 잘 활용하면 코드의 가독성을 높일 수 있습니다.
예를 들어 유틸 함수를 만들 때, 내보내야 하는 주요 함수는 파일 상단에, 그 함수에 필요한 보조 함수들은 하단에 배치할 수 있습니다.
이렇게 하면 중요한 흐름을 먼저 보여주고, 세부 구현은 나중에 볼 수 있어서 코드를 이해하기 쉬워집니다.
어떤 방식이든 호이스팅은 발생합니다.
하지만 TDZ(일시적 사각지대)의 영향을 받느냐에 따라 선언 전에 호출할 수 있는지 여부가 달라집니다.
TDZ의 영향을 받지 않으면 선언 전에 호출이 가능하고, 영향을 받으면 선언 전에 호출할 수 없습니다.
특히 var의 경우, 선언 전에 호출하더라도 원하는 값으로 초기화되지 않고 undefined로 초기화되기 때문에, 예기치 않은 오류가 발생할 수 있다는 점에 주의해야 합니다.
이번 발표의 부제목은 ‘편리함일까, 함정일까?’였습니다.
주제에 대해 공부하고 나니, 이제는 이렇게 말할 수 있을 것 같습니다.
'호이스팅을 모르면 버그를 부르는 함정이고, 알고 나면 const
, let
, 함수 선언식
, 함수 표현식
, class
등 어떤 키워드를 선택할지에 대한 폭을 넓혀주는 유용한 수단이다'라고 말입니다.
이번 주제를 학습하면서 "왜 클래스에서는 호이스팅이 되지 않을까?"라는 질문에 대해 고민하게 되었고, 그에 대한 저의 생각을 아래 글로 정리해보았습니다.