이번 포스팅에선,

  • 호이스팅이 무엇인가?
  • 변수와 함수에선 어떻게 쓰이는지?
  • 왜 쓰면 안되는지

에 대해서 알아보겠습니다.



1. 안녕 호이스팅(Hoisting) ? 안녕 호이스팅!

1-1. 호이스팅이 뭡니까?

호이스팅(Hoisting) = 선언을 끌어 올린다.

crain

자바스크립트에서 호이스팅은 모든 선언문들 (var, let, const, function, class) 을 스코프의 가장 위로 끌어 올립니다.

Hoisting
(명사) 끌어 올리기, 들어올려 나르기

출처 - 네이버 영어 사전

호이스팅이라는 단어의 정의에서도 보이지만, 모든 선언들을 해당 스코프의 {} 제일 위로 끌어 올립니다.

여기서 말하는 선언이란, 자바스크립트에선 변수의 선언과 초기화가 따로 이뤄지고, 이 중에서 선언 을 스코프의 최상단으로 끌어 올리는 것입니다.

이것이 자바스크립트에 변수 선언에서 말하는 호이스팅이죠.


1-2. 호이스팅 예제

이렇게 글로만 설명하니 잘 와닿지 않으신다구요? 한 번 코드로 보시죠.

/// 실제 코드
console.log(coin); // undefined
var coin = 10;
console.log(coin); // 10

첫 번째 console.log 는 언뜻 코드 상으로는 에러가 발생할 것 같지만, 에러는 발생하지 않고, undefined 가 나옵니다.

두 번째 console.log 는 결과가 10으로 제대로 나옵니다.

// 컴파일 단계에서 동작의 이해를 돕기 위한 코드
var coin; // coin 의 선언문, 스코프의 최상단으로 끌어올림 (호이스팅)
console.log(coin); // undefined
coin = 10; // coin 10으로 초기화
console.log(coin); // 10

컴파일 단계에서, var coin = 10 이라는 우리의 코드는

선언문 var coin;, 스코프 최상단 이동,

초기화문 coin = 10; 으로 나뉘어졌습니다. (물론 실제로 코드가 나뉘거나 올라가지 않습니다. 이해를 돕기 위한 설명입니다.)

그렇기 때문에, 실제 코드상에서 아직 선언되지 않았던 coin 의 객체에 접근 하는 것이 가능했고, 아직 값이 없는 변수이기 때문에 undefined 라는 값이 나왔습니다.


1-3. 어떻게 호이스팅이 이뤄지나요?

자바스크립트 컴파일 단계에서 JS Parser 가 컴파일 할 블록을 먼저 훑어봅니다.

이 과정에서 선언문의 경우, 메모리에 먼저 등록을 하게 됩니다. 메모리에 변수로써 값을 갖지 않고 먼저 등록 되어 있는거죠.

자바스크립트에서 변수 생성 과정을 좀 더 상세하게 과정을 살펴보면,

  1. 선언 단계 (Declaration phase)

    • 변수 객체 (Variable Object, VO)에 변수를 등록함.
  2. 초기화 단계 (Initialzation phase)

    • VO 에 등록된 변수를 메모리에 할당, 이 과정에서 변수는 undefined 로 초기화.
  3. 할당 단계 (Assignment phase)

    • undefined 로 초기화된 변수에 실제 값을 할당

이렇게 총 3개의 단계로 이뤄져 있습니다.

var 의 경우 선언문에서 선언 단계과 초기화 단계를 동시에 진행합니다. 선언문에서 메모리에 할당되고, 초기 값으로 undefined 까지 설정 되어 있는 것이죠.

let, const 같은 경우, 선언문에서 (1. 선언 단계) 만 진행합니다. 이 상태에서 변수에 접근하려 하면 References Error 가 발생하죠. TDZ 에 등록 되었다고 하는데, 이 내용은 다른 포스팅에서 다루도록 하겠습니다.


변수 할당에서 중요한 부분을 차지하는 호이스팅,

우린 그동안 잘 모르고 있었는데, 좀 더 실제 예를 찾아 볼까요?

var reuslt = add(4, 7); // 아직 선언되지 않은 함수 호출

function add(x, y) {
  return x + y;
}

console.log(result); // 정상적으로 11 리턴

우리가 흔히 함수가 선언되기 전에 사용하는 경우가 많은데, 꼭 순서에 맞지 않더라도 호출할 수 있습니다.

컴파일 단계에서 이 코드를 해석하자면,

var result;
function add(x, y) {
  return x + y;
}

result = add(4, 7);

console.log(result);

이렇게 우린 잘 알지 못했지만, 호이스팅을 적극적으로 사용 했습니다.


2. 변수와 함수에서 호이스팅

2-1. 변수의 경우

// 실제 코드
console.log(myName);
var myName = "kiwinam";
console.log(myName);

// 호이스팅
var myName;
console.log(myName);
myName = "kiwinam";

간단한 변수의 호이스팅입니다.

위에서 봤던 호이스팅과 비슷한 형태입니다.

// 실제 코드
function printName(name) {
  return prefix + name; // undefined+name
  var prefix = "pre";
}

// 호이스팅된 코드
function printName(name) {
  var prefix; // 메모리 등록과 undefined 로 초기화
  return prefix + name;
  prefix = "pre"; // 리턴 이후 pre 할당
}

함수 내에서도 변수에 대한 호이스팅은 이뤄지고 있습니다

다음 코드에선 좀 다르게 해보겠습니다.

// 실제 코드
console.log(name);
let name = "kiwinam";
console.log(name);

// 호이스팅
let name;
console.log(name); // Reference Error, TDZ 에 등록되어 보호 중인 변수.

이렇게 let, const 같은 경우에는 실제 값이 할당되기 전에 호출하게 된다면 Reference Error 가 발생합니다.


2-2. 함수의 경우

함수같은 경우는 두 가지 경우가 있습니다. 선언식과 표현식이 있습니다.

선언식은 아래같은 함수를 말합니다.

// 실제 코드
console.log(add(2, 3)); // 호이스팅된 함수를 이용해서 정상 결과 출력

function add(x, y) {
  // 선언식 함수
  return x + y;
}

// 호이스팅된 코드
function add(x, y) {
  // 선언식 함수
  return x + y;
}

console.log(add(2, 3)); // 호이스팅된 함수를 이용해서 정상 결과 출력

선언식 함수에선 var 변수처럼 호이스팅이 적용되는 것을 볼 수 있습니다.


표현식 함수는 아래와 같습니다.

// 실제 코드
console.log(add(2, 3)); // Error, add is not a function

var add = function (x, y) {
  // 표현식 함수
  return x + y;
};

// 호이스팅 된 코드
var add; // 변수 add, 최상단으로 끌어올려짐
add(); // Error, add is not a function
add = function (x, y) {
  return x + y;
};

이렇게 표현식 함수에선, 함수가 변수에 할당되기 때문에, 호이스팅이 안되는 것을 알 수 있습니다.


2-3. import 의 경우

// 실제 코드
externalAdd();
import { externalAdd } from "./module";

// 호이스팅된 코드
import { externalAdd } from "./module";
externalAdd();

이렇게 import 구문 같은 경우도 호이스팅 되기 때문에, 스코프의 최상단에 import 구문을 놓는 것이 좋습니다.


3. Stop using Hoisting

위 설명을 보면, 호이스팅이 개발자에게 편하고 좋은 기능인 것 같은데, 왜 사용하지 말라 할까요?

이유는 간단합니다.

Simple is best


우린 살아 숨쉬는 제품을 만드는 개발자들입니다.

문제는 이 살아 숨쉬는 제품을 만드는건 우리 팀에서 저 혼자가 아니라는 거죠.

그렇기 때문에 다양한 개발자들과 협업하면서 서로가 이해하기 쉬우면서도 깔끔한 코드를 작성해야 합니다.


그래야만 1년 뒤에 내가 짠 코드가 레거시 라는 이름의 괴물이 되어 돌아오지 않죠.

하지만 변수와 함수 호이스팅을 사용해서 개발을 진행하면, 실제 코드와 컴파일된 코드는 점점 꼬이고 알아보기 힘들게 됩니다.

이런 상황에선 갑자기 이해할 수 없는 에러들이 나오기 시작하죠…


그 때부턴 우리들은 “어라 이럴 일이 없는데, 왜 이러지?” 라는 고민을 하게 됩니다.

이런 일을 방지하기 위해 호이스팅 사용을 자제 하는 것이 좋습니다.



여기까지 자바스크립트에서 호이스팅이 변수와 함수에서 어떻게 적용 되는지 정리해봤습니다.

오기입하거나 틀린 내용이 있으면 언제든지 알려주시면 감사하겠습니다.

긴 글 읽어주셔서 감사합니다.