[우아한테크코스 7기 FE 테코톡] 호이스팅이란?

유소정·2025년 3월 10일
0
post-thumbnail

3월 27일 테코톡을 앞두고, 발표 내용을 정리해봤습니다.

주제: 호이스팅이란?
부제: 편리함일까, 함정일까?

🪵 서문

  • var
  • const
  • let
  • 함수 선언식
  • 함수 표현식

여러분들은 위 키워드들을 한 번쯤은 사용해보셨을 거예요.

이 키워드들마다 호이스팅이 다르게 동작하는데요,

호이스팅이란 "코드를 실행하기 전에, 선언문이 해당 범위의 맨 위로 끌어올려지는 것처럼 보이는 현상"을 말합니다.

🌳 var 호이스팅은?

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와 함수 선언식 호이스팅 차이는?

정리해보면,
선언 전에 접근할 수 있다는 공통점이 있지만,

  • 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 폐해, 그리고 TDZ의 등장!

지금까지 봤듯이, undefined는 예상치 못한 버그를 발생시킬 수 있습니다.
개발자가 변수에 실제 값이 할당되었다고 착각할 수 있기 때문이죠.

그렇다면, undefined로 초기화되지 않게 하는 방법은 없을까요?

그래서 등장한 개념이 바로 TDZ (Temporal Dead Zone) 입니다.

TDZ는 일시적 사각지대로,
선언되기 전에 변수에 접근하는 것을 금지합니다.

TDZ가 적용되는 키워드는 다음과 같습니다:

  • const
  • let
  • class
  • (일부) 매개변수

🌳 함수 표현식 호이스팅은?

이번에는 함수 표현식에 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(일시적 사각지대)라는 개념까지 등장했습니다.

그렇다면, 차라리 호이스팅 자체를 없애버리면 되지 않을까요?

그런데도 자바스크립트는 여전히 호이스팅을 유지하고 있습니다. 왜일까요?

그 이유는 두 가지입니다.

1️⃣ 레거시 코드와 하위 호환성 유지

첫 번째 이유는, 레거시 코드와 하위 호환성 유지 때문입니다.

이미 세상에는 호이스팅을 활용해서 작성된 수많은 코드가 존재합니다.

만약 지금 갑자기 호이스팅이 사라진다면, 기존 코드에서 수많은 오류가 발생할 것이고, 그걸 일일이 수정하는 데 막대한 비용과 시간이 소모될 것입니다.

그래서 호이스팅은 쉽게 제거할 수 없는 기능입니다.

2️⃣ 함수 선언식의 유연한 호출과 가독성 향상

두 번째 이유는, 호이스팅이 오히려 장점이 될 수도 있기 때문입니다.

예를 들어 함수 선언식의 경우, 선언문보다 먼저 함수를 호출해도 정상적으로 동작합니다.

이 특성을 잘 활용하면 코드의 가독성을 높일 수 있습니다.

예를 들어 유틸 함수를 만들 때, 내보내야 하는 주요 함수는 파일 상단에, 그 함수에 필요한 보조 함수들은 하단에 배치할 수 있습니다.

이렇게 하면 중요한 흐름을 먼저 보여주고, 세부 구현은 나중에 볼 수 있어서 코드를 이해하기 쉬워집니다.

🪵 결론

어떤 방식이든 호이스팅은 발생합니다.

하지만 TDZ(일시적 사각지대)의 영향을 받느냐에 따라 선언 전에 호출할 수 있는지 여부가 달라집니다.

TDZ의 영향을 받지 않으면 선언 전에 호출이 가능하고, 영향을 받으면 선언 전에 호출할 수 없습니다.

특히 var의 경우, 선언 전에 호출하더라도 원하는 값으로 초기화되지 않고 undefined로 초기화되기 때문에, 예기치 않은 오류가 발생할 수 있다는 점에 주의해야 합니다.

🪵 마무리

이번 발표의 부제목은 ‘편리함일까, 함정일까?’였습니다.

주제에 대해 공부하고 나니, 이제는 이렇게 말할 수 있을 것 같습니다.

'호이스팅을 모르면 버그를 부르는 함정이고, 알고 나면 const, let, 함수 선언식, 함수 표현식, class 등 어떤 키워드를 선택할지에 대한 폭을 넓혀주는 유용한 수단이다'라고 말입니다.

🍵 발표 자료

🍵 번외

이번 주제를 학습하면서 "왜 클래스에서는 호이스팅이 되지 않을까?"라는 질문에 대해 고민하게 되었고, 그에 대한 저의 생각을 아래 글로 정리해보았습니다.

profile
기술을 위한 기술이 되지 않도록!

0개의 댓글