brunch

You can make anything
by writing

C.S.Lewis

by zwoo Jan 02. 2022

컴파일러와 인터프리터

자바스크립트는 인터프리터 언어이지만 컴파일 과정을 거친다 


소스코드는 기계어로 변환된다


현대의 소프트웨어 프로그래머들은 사람의 언어로 코드를 작성하여 컴퓨터에 명령을 내린다. 자바스크립트, 자바, C++, C# 같은 언어들은 영어문법과 단어를 차용해 컴퓨터가 알아들을 수 있는 기계어에 대응하는 규칙을 갖도록 만들어진 인간의 언어이다. 이 문법규칙에 맞게 소스코드를 작성하고, 컴파일러 프로그램을 돌려서 기계어로 된 실행파일을 산출하면 컴퓨터가 이해할 수 있는 네이티브 코드로 변환되고, 컴퓨터는 이 파일에 적힌대로 명령을 수행할 수 있다.


고수준 언어 (소스코드)가 기계가 알아들을 수 있는 저수준 프로그래밍 언어로 변환되는 과정은 일반적으로 세단계로 이루어진다. 'Source -> ByteCode -> Machine Code'. 예시 이미지는 자바소스코드가 기계어로 변환되는 과정이다. 전통적인 변환과정은 소스코드가 곧바로 기계어로 변환되는 두단계로 이루어졌으나, 현대로 오면서 중간단계가 추가된 컴파일러가 보편적이 되었다. 그 이유는 이어서 적어보겠다.  


소스코드가 컴파일되는 과정

(출처 : ResearchGate)



컴파일러


컴퓨터에서 명령을 수행하는 주체인 CPU 는 0과 1 두개의 숫자조합으로 명령을 인식하고 실행할 수 있다. 저수준 프로그래밍 언어는 일반적으로 이 이진숫자 조합 자체를 의미하지는 않고, 이진숫자 조합에 일대일로 대응되도록 만들어진 어셈블리어를 의미한다. 어셈블리어는 직접적으로 0과 1을 가지고 프로그래밍하지 않아도 되도록 만들어진, 사람이 읽고 쓸 수 있는 정도의 단어와 문법규칙이 있는 저수준 프로그래밍 언어이다. (이진 바이너리 코드에 일대일 대응하므로, 고수준 프로그래밍 언어에 비하면 활용할 수 있는 문법규칙과 단어들이 제한적이다) 고수준 언어로 작성된 소스코드를 컴파일러에 넣고 돌리면 기계어로 된 실행파일이 산출된다. 


운영체제가 윈도우, 맥, 리눅스 등 여러개가 있듯이 컴파일러의 종류도 다양하다. 컴파일 과정에서 중요한 건 최적화이다. CPU 의 종류에 따라, 그리고 CPU 를 운영하는 운영체제의 종류에 따라 최적의 방식이 있고, 좋은 컴파일러는 각각의 운영체제에 맞는 최적화 방식을 가지고 있다. 또한 특정 운영체제만 지원하는 컴파일러도 있다. 


개발에 필요한 과정들을 Tool 로 제공하는 통합개발환경(IDE - 비주얼 스튜디오, 웹스톰 등)에서는 자체 컴파일러를 내장해두었고, 소스코드 빌드과정에 컴파일과정이 포함되어있다. 컴파일러와 IDE 를 헷갈려하는 경우가 생기는 것은 이때문이다. 컴파일을 하는 장점은 말한 코드 최적화와 함께, 실제로 실행되는 런타임 이전에 빌드 과정에서 이미 컴파일이 되었으므로, 실행속도에서 이점을 얻을 수 있다는 점이다.



JIT 컴파일러와 바이트코드


바이트코드 자체도 이진표현법이기 때문에, 사람이 읽을 수 있는 소스코드와는 전혀 다르게 생겼다. 


.java


로 작성된 자바 소스코드는 자바 컴파일러를 거치면 


.class

라는 바이트코드를 생성하는데, 메모장으로 .class 파일을 열어보면 이렇게 생겼다고 한다.

Why is Bytecode not human-readable?

(출처 : 스택오버플로우)


전통적인 정적 컴파일러가 하드웨어에 의존적이었기 때문에, 자바 언어를 만든 프로그래머 제임스 고슬링은 CPU 나 운영체제 종류에 따라 컴파일 과정을 통으로 분리하지 않아도 되도록 가상머신을 설계했다. 그리고 JIT 컴파일 방법을 도입했다.


자바 컴파일러는 소스코드를 곧바로 기계어로 변환하는 것이 아니라 바이트코드로 변환한다. 바이트코드는 CPU가 아니라 가상머신이라는 소프트웨어에서 처리되기 때문에 기계어보다는 추상적이지만, 소스코드보다는 훨씬 기계친화적이다. 바이트코드는 아직 기계어가 아니기 때문에 최종적으로는 기계어로 반드시 변환이 되어야 하는데, 이 최종 변환과정을 자바 가상머신의 JIT 컴파일러가 수행한다. JIT 컴파일은 빌드시점이 아니라 실행시점에 수행된다. JIT 컴파일러는 변환한 기계어를 캐싱하여, 실행될 때마다 불필요하게 기계어코드를 새로 생성하는 비효율을 방지한다. 



JVM ( 자바 가상 머신 )

(출처 : https://www.javatpoint.com/jvm-java-virtual-machine )



인터프리터


인터프리터는 컴파일러와 달리 기계어로 된 '실행파일'이 없다. 응용프로그램의 실행환경(런타임)에서 동작하는 스크립트 언어들은 대부분 인터프리터 언어인데, 이들은 소프트웨어와 상호작용할 목적으로 만들어졌고, 별도의 실행파일을 생성하지 않고 즉각 즉각 소스코드를 실행한다. 


인터프리터는 명령어 라인들을 한번에 한줄씩 실행한다. 미리 모아서 컴파일을 하지않기 때문에 코드 최적화의 이점을 얻을 수 없고, 따라서 컴파일해서 만들어진 실행파일에 비해 실행속도가 느리다. 


그러나 인터프리터 언어로 분류되는 자바스크립트의 경우 컴파일과정이 전혀 없는 것은 아니다. 자바스크립트 소스코드는 자바스크립트 엔진에 의해 중간언어 (IR / 바이트코드)로 파싱되고, 상황에 맞게 바이트코드가 곧바로 실행되거나 native code 로 JIT 컴파일되어 실행되거나 둘중 하나의 방식으로 명령이 수행된다.



자바스크립트 엔진의 최적화 기법

(출처: https://meetup.toast.com/posts/77)




트랜스파일러


한편 트랜스파일이라는 용어도 있는데, 컴파일의 부분집합이라고 볼 수 있다. 고급에서 저급 언어로의 변환이 아닌, 비슷한 수준의 추상성을 가진 언어로 변환되는 것을 트랜스파일이라고 한다. 자바스크립트 최신문법을 es5로 변환해주는 바벨은 대표적인 트랜스파일러이다. 하지만 컴파일러라고 불러도 맞는 말이며, 두 언어를 명확히 구분해서 사용해야 하는 것은 아니라고 한다. 














Photo by Lindsay  Henwood on Unsplash


참고링크


- What does byte code look like? How does it differ from assembly language? 

- Source code -> Byte code -> Native code

How does the CPU translate assembly to binary?

https://www.javatpoint.com/jvm-java-virtual-machine

- Visual Studio 컴파일러 질문

- [Java] JVM 메모리 구조

- 자바스크립트 엔진의 최적화 기법 (1) - JITC, Adaptive Compilation

- 포큐 아카데미 실무 프로그래밍 입문 (C#)





        

매거진의 이전글 [React] DOM 과 Virtual DOM
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari