Hoisting과 이에 대한 나의 생각

Heojoooon·2023년 2월 13일
1

잘못되었거나 수정이 필요한 내용이 있을 수 있습니다.
그런 경우, 댓글로 남겨주시면 정말 감사하겠습니다.

console.log(name);

var name = 'Heo';

내가 위와 같은 코드를 맨처음 접했을때, 당연히 오류가 난다고 생각하였다. (물론, undefined가 출력된다.)
C에서 절차지향 언어답게 코드가 윗줄부터 실행되며 아직 선언되지 않은 변수를 미리 사용하는 것은 절대 용납될 수 없기 때문이다.
하지만 JavaScript에서 이를 오류 없이 실행 가능하게 하는 것이 hoisting 이다.

Hoisting

JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다. MDN

따라서 JavaScript 문법을 공부하다 가장 먼저 눈에 띄인 용어가 hoisting(호이스팅) 이다.
영사전에서 hoist란, 계양하다, 끌어올리다라는 의미이다.
공식 문서에서 잘 설명해준 것과 같이 JavaScript 코드가 실행될 때, JavaScript 인터프리터가 변수와 함수에 대해 메모리 공간을 미리 할당하는 것을 의미한다.
이렇게 되면 마치 아래에 선언된 함수나 변수가 위로 끌어올려지는 듯한 효과를 볼 수 있게 된다.

따라서 맨 위의 코드가 실행되는 것이 이해가 된다.
선언된 변수가 호이스팅으로 인해 이미 메모리를 할당받아 메모리 공간에 존재하고, 이를 가져다 사용했기 때문이다.

그렇다면 호이스팅에 대해 조금 더 정리해보자.

JavaScript에서 변수의 생성

JavaScript에서 변수나 함수가 메모리를 할당 받아 사용되기까지의 과정은 3가지로 나누어진다.

  1. 선언 단계
  2. 초기화 단계
  3. 할당 단계

선언 단계에서는 변수나 함수의 선언실행 컨텍스트에 등록하게 된다.
특히, var로 선언한 변수의 경우 이때 undefined로의 초기화 단계까지 함께 이루어져 맨 위의 코드가 실행 가능해지는 것이다.
let과 const로 선언된 변수의 경우, 다음과 같은 에러가 발생한다.

ReferenceError: Cannot access 'a' before initialization

var와 달리 초기화 단계가 이루어지지 않아 선언이 참조할 메모리 공간이 없어 TDZ라고 하는 영역에 빠져 있기 때문에 발생하는 것이다.
하지만 그렇다고 해서 호이스팅이 발생하지 않은 것은 아니다.
이미 선언에 대한 메모리는 할당되었지만 초기화되지 않아 발생하는 에러이기 때문이다.
이후 letconst의 경우, 따로 초기화 단계를 거쳐 undefined로 초기화가 된다.
마지막으로 모든 변수는 할당 단계에서 선언에 사용자가 직접 입력한 값을 할당하게 된다.

  • TDZ : Temporal Dead Zone, 일시적 사각지대

그렇다면 함수는?

함수도 변수와 마찬가지로 호이스팅이 일어난다.

hello(); // hello!

function hello() {
	console.log("hello!");
}

hello(); // hello!

위처럼 function 키워드를 이용한 함수 선언문의 경우, 호이스팅이 일어나 함수가 정의된 코드가 작성되기 이전에 사용해도 문제없이 실행된다.

하지만 다음과 같이 함수 표현식을 사용한 경우, 아래의 에러가 발생한다.

hello();

var hello = function() {
	console.log("hello!");
}

TypeError: inner is not a function

변수와 마찬가지로 "undefined로 떠야하지 않나?"라고 생각할 수 있다.
하지만, 누가 봐도 함수로 사용되었고, JavaScript 인터프리터가 "hello의 선언이 변수구나!" 라고 인식하여 호이스팅이 일어났지만 일어나지 않은 것처럼 보이는 것 같다고 생각하였다.

그래서 코드를 var가 아닌 const로 수정하여 다시 실행해보았다.

hello();

const hello = function() {
	console.log("hello!");
}

ReferenceError: hello is not defined

즉, 선언조차 찾을 수 없다고 하는 것을 보니 호이스팅이 일어나지 않았다.

그렇다면 화살표 함수의 경우는 어떨까?

hello();

const hello = () => {
	console.log("hello!");
}

ReferenceError: hello is not defined

마찬가지로 호이스팅이 일어나지 않았다.

따라서 함수의 경우에는 함수 선언문을 사용해야지만 호이스팅이 일어나는 것 같다.

Hoisting에 대한 나의 생각

이전 회사에서 나는 Linux 환경에서 모바일 기기의 Kernel Driver를 개발하는 업무를 진행했었다.
이때 코드 중간에 변수 선언하는 것을 컴파일러 자체가 막아놨었다. (C99부턴가 가능해졌다고 한다...)

int getTotal(int arr[], int len) {
	int sum = 0; // ok
    
	for(int i = 0 ; i < len ; i++) {
    	sum += arr[i];
    }
    
    int ret = sum; // -> error
    return ret;
}

즉, 함수 초반에 변수를 정의하는 것만이 허용되었다.
이때는 컴파일러가 정책으로 막아두었기 때문에 별다른 생각없이 그렇구나하고 넘어갔었다.

하지만, 퇴사를 하고 어느날 문득 든 생각이 있다.

locality (지역성) 때문인가?

먼저, locality에 대해 소개하자면 2가지가 존재한다.

  1. Spatial locality

    어떤 메모리 공간을 참조했다면, 이후 해당 메모리의 주변 메모리를 참조할 확률이 높다.

  2. Temporal locality

    어떤 메모리 공간을 참조했다면, 이후 다시 이 메모리를 참조할 확률이 높다.

대학교때 운영체제 강의를 들으며 지역성에 대해 공부하다가 코드를 작성할 때, 변수를 최대한 한 곳에 모아 작성해야 한다라는 것을 본 적이 있다.
코드가 실행되고 메모리가 필요한 경우 한 곳에 모아 선언을 해야 비슷한 메모리 공간에 들어가게 되고, 최대한 하나의 페이지에 저장되어 spatial locality 개념을 적용해 효율적으로 관리할 수 있기 때문이다.
(물론, 메모리 할당에는 시스템 콜이 발생하기 때문에 열심히 코드를 실행하다 중간에 시스템 콜에 의해 interrupt가 발생하는 것을 최소화하기 위함도 하나의 이유지 않을까 생각하긴 한다.)

충분히 일리 있는 말이라고 생각했다.
현업에서 보던 코드는 기본이 몇백줄, 몇천줄, 심지어 만줄 단위까지도 있었고, 함수 하나가 말도 안되게 긴 경우도 있었다.
때문에 코드가 길어지면 길어질수록 사용하는 변수도 많을 것이다.
그런데 이리저리 난잡하게 변수 선언이 되어있다면?
같은 스코프 안에서 고작 변수 호출 하나 때문에 page fault가 일어나는 것은 너무나도 비효율적이라고 생각한다.
그래서 컴파일러도 이를 조금이라도 최적화하고자 중간에 변수 선언을 막는 정책을 사용하는 것인가라고 생각하였다.

이후에 JavaScript를 공부하면서 호이스팅을 접하게 되었을 때에도 같은 이유로 그런 것이 아닌가라는 생각이 들었다.

var a = 'abc';

이미지 출처

JavaScript에서는 다음과 같이 변수가 메모리에 저장된다.
선언 단계에서 선언에 대한 메모리 공간이 먼저 할당되고 나서 초기화 단계를 거쳐 할당 단계에서 실제 데이터에 대한 메모리 공간이 할당된다.
그리고 데이터에 대한 메모리 공간의 주소가 선언의 값으로 들어간다.

호이스팅은 인터프리터가 이러한 선언들에 대해 미리 실행 컨텍스트에 한번에 등록을 시킨다.
따라서 spatial locality 개념을 적용하여 최대한 비슷한 공간에 선언들의 주소를 모아둠으로써 효율적으로 변수를 저장하고 관리하기 위함이라고 생각한다.

마치며

물론, 하나의 스코프에 변수를 얼마나 많이 쓴다고 이렇게까지 할까라고 말할 수도 있다.
하지만 조금이라도 최적화를 하기 위해 이러한 사소한 행위라도 하는 것이지 않을까라는 단순한 생각에 이렇게 글을 써봤다.
비슷한 생각을 가진 사람이 있다면 이야기를 나누고 싶다는 생각에 Stack Overflow에도 처음 질문을 작성하는 경험도 했다.
하지만 다들 호이스팅은 그냥 효과일뿐이지 전혀 관련이 없다는 말만 했다.
이런 설명을 한 답글이 원래 존재했는데, 왜 내가 이렇게 생각했는지, 근거는 무엇인지를 잘 정리해서 설명 감사하다는 말과 함께 코멘트를 달았는데, 내 글에 좋아요(?) 기능과 비슷한 up 버튼을 하나 누르고 답변을 삭제해버렸다...
나도 다시 보려고 잘 정리해둔건데...
조금 아쉽지만 그래도 블로그에 글을 쓰면서 한번 더 정리를 할 수 있어 좋았다.
쓰면서 문득 '변수를 얼마나 많이 한번에 쓴다고 굳이 그럴까?' 싶기도 하였지만, 이 생각을 통해 뭔가 CS적으로 생각하는 방법을 기를 수 있었던 것 같아서 쭉 작성해나갔다.
혹시나 이 관계에 대해 알거나 다른 생각/ 같은 생각이 있다면 얼마든지 댓글 남겨주시면 감사하겠습니다 :)

profile
별 3개짜리 개발자

1개의 댓글

comment-user-thumbnail
2023년 7월 25일

호이스팅 관련해서 외우는거에 그치지 않고 배경지식과 연결하여 고민해보는 모습이 인상깊었습니다. 재밌게 읽었어요 bb

답글 달기