반응형

자바스크립트(JavaScript) 실행 구조와 프로그램 평가, 실행과정

반응형

현재 자그마한 쇼핑몰 솔루션을 사이트 프로젝트로 시작하는 중이다. 원래는 자바 기반 백엔드 쪽을 담당하고 있지만 결국에 클라이언트에게 제공해야할 화면을 만들어야 되기 때문에 자바스크립트를 공부하고 있는 중이다.


자바를 주로 하다가 자바스크립트를 공부하면서 그 유연성과 복잡스러움(?)을 느끼고 있고 동적 타입 언어가 제공하는 특징때문에 여러가지로 학습하는 데 애로사항이 피고 있다. 자바스크립트의 자유분방함은 아직 어색하기만 하다. 


특히나 자바스크립트에서 프로그램이 어떻게 인터프리터에 의해 해석되고 실행되는 지 알 수 없는 상태에서 코드를 짜려고 보니 자바와 같은 컴파일 언어에서는 이해할 수 없는 동작방식으로 돌아갈 때 심히 당황하게된다. 이 기회에 헷갈리는 것들을 아예 정리하려고 이 글을 포스팅했다.


| 자바스크립트 실행 구조


자바스크립트는 기본적으로 싱글스레드 기반이다! 그리고 원래 웹브라우져에서 돌아가는 동적인 기능을 구현하려고 만든 언어이기 때문에 콜백큐를 기반으로 한 이벤트 기반 방식으로 돌아간다. 이러한 자바스크립트 실행 구조는 후에 node.js의 싱글 이벤트 루프에서 실행되는 고성능 런타임을 제공하는 계기가 되었으리라 생각한다.


자바스크립트 엔진의 대표격인 Google V8은 다음과 같이 Memory Heap과 Call Stack을 기반으로 자바스크립트를 실행하게 된다.



이런 방식은 대부분의 언어가 채택하고 있는 실행구조로서 프로그래머들에게는 상당히 익숙한 실행 구조일 것이다. 함수 호출 시 스택에 쌓아놓고 처리하게 되며 객체 생성시 힙 메모리에 동적할당 되며 관리하게 된다.


 하지만 자바스크립트는 여기에 더해 웹브라우져 상에서 실행했을 경우 아래와 같이 브라우져 자체내에서 제공하는 WebAPI와 웹브라우져상에서 발생하는 이벤트를 처리하는 Event Loop가 존재한다. 이 Event Loop는 Event Queue에 push되는 이벤트들을 실행한다. 



그렇다면 궁금한 것이 있다 하나의 스레드로만 동작했을 때 오랜 시간이 걸리는 I/O 작업이나 네트워크 작업은 어떻게 처리할 수 있을까? 자바같은 언어처럼 멀티스레드로 I/O작업만을 따로 다른 스레드에 넘기게 처리해 줘서 전체 프로그램의 효율성을 높일 수는 없을까? 


자바스크립트는 함수 단위에서 비동기적으로 프로그래밍하기 쉽게 설계되었기 때문에 I/O작업과 같은 오랜 시간이 걸리는 작업은 그 작업이 끝났을 때 콜백함수를 등록하여 처리하는 것으로 위 문제를 해결한다. 이러한 방식을 이벤트 기반 프로그래밍이라 한다. 


다음의 코드에서 볼 수 있 듯이 함수 차원에서 비동기 콜백을 통해 작동하고 있음을 알 수 있다.

function setFirstCall() {
setTimeout(() => {
console.log("First Task is Done")
}, 1000)
}

function setSecondCall() {
setTimeout(() => {
console.log("Second Task id Done")
}, 500)
}

setFirstCall()
setSecondCall()
The Second function has been called.
The First function has been called.


아래는 자바스크리트의 비동기적인 실행과정을 잘 정리해 놓아서 참조해놓는다.


https://hudi.kr/%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-javascript-%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9C-%EA%B8%B0%EB%B0%98-js%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95/


| 프로그램 평가 및 실행 과정


자바스크립트 엔진은 실행 가능한 자바스크립트 코드(Executable Code)를 만나게 될 시에 그 코드를 평가해서 실행 문맥(Execution Context)으로 만든다. 이 실행 가능한 코드의 유형은 크게 3가지 전역코드 , 함수코드, eval코드다.


전역 코드는 전역 객체 Window 아래에 정의된 함수를 말하고, 함수 코드는 function 키워드 같은 방식으로 만들어진 함수의 몸체 부분을 뜻한다. eval 코드는 eval 함수를 말하며 이 eval함수는 문자열을 인수로 받아 이 인수를 실행 가능한 자바스크립트 코드로 해석하여 실행하게 한다.


이 실행 문맥과 위의 자바스크립트 작동방식을 알아야 나중에 자바와 같은 언어에서는 이해하기 힘든 작동 방식을 알 수 있다.


| 실행 문맥


자바스크립트의 실행 문맥은 3가지의 컴포넌트로 나뉘며 이 3가지 컴포넌트는 렉시컬 환경 컴포넌트(LexicalEnvironment), 변수 환경 컴포넌트(VariableEnvironment), 디스 바인딩 컴포넌트(This Binding)다. 여기서 렉시컬 환경 컴포넌트와 변수 환경 컴포넌트는 타입이 같고 with 문을 사용할 때를 제외하면 내부 값이 똑같다고 알려져있다. 따라서 렉시컬 환경 컴포넌트만 정리해도 되기에 앞으로 렉시컬 환경 컴포넌트와 디스 바인딩 컴포넌트에 대해서만 언급하려고 한다.


렉시컬 환경 컴포넌트는 자바스크립트 실행 엔진이 자바스크립트 코드를 실행하기 위한 자원을 모아놓은 곳이다. 해당 자바스크립트의 유효범위에 있는 변수 및 함수, 객체 레퍼런스에 대한 정보를 모아놓은 곳이라 생각하면 된다. 렉시컬 환경 컴포넌트는 이 정보들을 key-value 형식으로 기록하며 관리한다.


렉시컬 환경 컴포넌트는 또다시 환경 레코드외부 렉시컬 환경 참조 컴포넌트로 구성되어 있다. 이 환경 레코드는 유효 범위 안의 식별자를 기록하고 실행하는 영역이다. 변수와 결괏값이 이 영역에서 관리된다.


재미있는 부분은 외부 렉시컬 환경 참조 컴포넌트다. 외부 렉시컬 환경 참조 컴포넌트는 함수를 감싸고 있는 외부 코드들에 대한 정보가 담겨있다. 이 컴포넌트를 이용하면 전역변수가 아닌 외부 함수의 변수 및 함수에 접근할 수 있는 것이다! 이것을 통해 자바스크립트는 클로져(내부함수가 외부함수의 데이터에 접근하는 것)라는 기능을 제공할 수 있다. 


환경 레코드는 또다시 선언적 환경 레코드객체 환경 레코드로 분리된다. 선언적 환경 레코드는 실제로 함수와 변수같은 식별자와 실행 결과가 저장되며 객체 환경 레코드는 객체의 레퍼런스를 관리한다.


아직 디스 바인딩 컴포넌트를 정리하지 않았음에도 불구하고 위에서 실행 문맥에 관해 많은 설명이 필요함을 알 수 있다. 그만큼 자바스크립트가 제공하는 유연성을 받쳐주기 위해 이런 복잡하지만 체계화된 구조를 도입한 것이 아닌가 싶다.


다시 돌아가서 디스 바인딩 컴포넌트는 키워드 this를 관리하는 컴포넌트다. this는 자바와 같은 언어에서는 그냥 더도 말고 현재 인스턴스를 지칭한다. 하지만 자바스크립트에서는 다르다! 자바스크립트에서는 '이 함수가 실행되었을 때 그 함수가 속해 있던 객체의 참조'다. 머리가 아파오지 않은가? 이런 것들을 고려했을 때 '문맥' 이라고 네이밍한 자바스크립트 언어 개발자의 네이밍 센스에 찬탄을 금치 못한다. 


뭐 어쨋든 이 this라는 녀석은 '문맥'에 따라서 가르키는 것이 다르다. 이 부분은 아래 링크에 더 자세히 나와 있으니 더 이상의 자세한 설명은 생략한다.


http://webframeworks.kr/tutorials/translate/explanation-of-this-in-javascript-1/


| 마치며 


아직 자바스크립트를 학습하기 시작한 지 얼마 안 됬기 때문에 부족한 내용이 많을 것이다. 계속 학습을 진행하면서 모자랐던 부분이나 부족했던 부분은 계속 업데이트해 나갈 것이다.


이상 서버 쪽에서 놀고 있는 개발자의 자바스크립트에 대한 단상 및 푸념이 섞인 포스팅이었다.


참고자료 : https://vaibhavgupta.me/2018/01/20/understanding-event-loop/


https://engineering.huiseoul.com/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94%EA%B0%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EB%B6%80%EC%83%81-async-await%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%BD%94%EB%94%A9-%ED%8C%81-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80-df65ffb4e7e

반응형

이 글을 공유하기

댓글

Designed by JB FACTORY