본문 바로가기
[ 개발 지식 ]

일반인에게 설명하는 [자바 프로그램의 실행 과정] - 1. 프로그램 실행

by 히앤님 2022. 9. 19.
반응형
SMALL
SMALL

그동안 소스코드를 작성하고 언어를 배우며 써왔지만 실제로 어떻게 동작하는지에 대해서는 등한시한 경향이 있었다. 일반인도 이해할 수 있게, 가장 많이 쓰이는 자바 프로그램이 어떻게 동작하는지에 대해 정리를 해보고자 한다.

자바 역시 프로그램이다. 그러니 가장 기초적인 질문부터 시작해보자.


[ 프로그램은 어떻게 실행될까? ]

설계 →  코딩 번역 실행


1. 프로그램 설계

어떠한 문제를 해결하기 위해, 또는 어떤 기능을 제공하는 프로그램을 만든다고 하면 내부에 논리적 설계가 들어가게 된다. 즉, 어떤 순서와 논리와 구조로 이 기능이 구현되는지를 짜는데, 그것을 알고리즘이라고 한다. 

▼ 내가 컴퓨터 구조도 잘 모르겠다면?

 

컴퓨터 구조와 메모리(스택, 힙, 메모리 계층 구조)

아아주 기초적인 지식으로 컴퓨터에 대한 이야기부터 해보자. 컴퓨터는 여러 장치를 합쳐놓은 기계다. 본체 내부에도 여러 장치를 합쳐놓았고, 모니터, 키보드, 스피커, 마우스 등의 구성요소도

heannim-world.tistory.com

2. 프로그램 코딩

어떻게 만들지에 대한 논리적 설계를 짜면, 이걸 프로그래밍 언어로 각 언어에 맞게 프로그램을 작성한다. 프로그래밍 언어는 JAVA, Python, C++ 등이 있다. 이 때 프로그래머가 각 언어의 맞는 기능을 썼는지, 설계한대로 잘 만들고 있는지, 오타는 안냈는지 확인해야 한다.  오타 같은 구문에러(syntax error) 는 프로그래밍 언어가 안돌아가면서(실행실패) 오류 메시지를 발생시키기도 하지만, 논리적인 에러(logical error) 의 경우 구문 에러는 아니라서 돌아는 가는데 원하는 설계대로 동작이 안될 수 있다. (ex.아버지 가방에 들어가신다. -> 논리적 에러는 잘못된 동작을 하게 만들수도 있다.)

3. 프로그램 번역

여기서부터는 눈에 보이지 않는 동작이다. 에러 없이 원하는 설계대로 프로그램을 작성했다면, 프로그래밍 언어의 번역 프로그램이 자동으로 동작하기 때문이다. 프로그램 번역은 쉽게 말해서 우리가 쓴 말을 컴퓨터가 알아들을 수 있게 해석해주는 것이다. 컴퓨터는 무슨 말을 쓸까?

1) 기계어(Machine Language)

저급 기계어 (low-level machine language)라고 불리는 기계어(Machine Language)컴퓨터가 알아들을 수 있는 언어로 0과 1로 구성되어 있다. 컴퓨터는 0과 1로 이루어진 언어체계로 말을 알아듣는다. 0과 1로 이루어진 언어체계를 이진코드 또는 바이너리코드(binary code)라고 부른다. 컴퓨터는(=CPU는) 바이너리 코드(binary code) 로 만들어진 기계어(Machine Language)로 쓰인 명령어를 수행한다. 사람이 아무리 컴퓨터에 명령하려고 해도 알아들을 수 있어야 시키는대로 할테니까 말이다. (ex.미국인(=컴퓨터)은 알파벳(=바이너리 코드)으로 만들어진 영어(=기계어)를 쓴다)

2) 어셈블리어(Assembly Language)

우리는 어떻게 바이너리코드(binary code) 를 쓰는 컴퓨터와 소통할 수 있을까? 우리가 외국인과 대화할 수 있는 이유는 [안녕] 이라는 한글이 [Hello]와 같은 의미라는 것을 알기 때문이다. 이와 같이 0과 1로 되어있는 기계어의 숫자를 사람이 이해할 수 있는 언어와 매치하여 만든 언어를 어셈블리어(Assembly Language)라고 한다.

//x86 계열 CPU의 기계어 명령
10110000 01100001

위는 x86 계열 CPU의 기계어 명령이고, 이것을 어셈블리어로 옮겨쓰면 다음과 같다.

//어셈블리어로 변환
mov al, 061h

기계어보다는 익숙한 언어로 변경된다.
어셈블리어(Assembly Language)는 사람이 알아들을 수 있게 만들었으니 반대로 CPU가 이해하기 위해서는 다시 기계어로 바꿔줘야 한다. 어셈블리어를 기계어로 바꿔주는 프로그램을 er을 붙여서 어셈블러(assambler)라고 한다.

// 예시) NASM x86 어셈블리어로 구현한 헬로 월드 프로그램
adosseg
.model small
.stack 100h

.data
hello_message db 'Hello, World!',0dh,0ah,'$'

.code
main proc
      mov    ax, @data
      mov    ds, ax

      mov    ah, 9
      mov    dx, offset hello_message
      int    21h

      mov    ax, 4C00h
      int    21h
main endp
end main

3) 고급 프로그래밍 언어 (high-level programing language)

컴퓨터와의 소통은 이렇게 시작했다. 현재는 어셈블리어(Assembly Language)보다 더 발전된 언어 체계로 프로그래밍을 할 수 있다. 고급 프로그래밍 언어 (high-level programing language)사람이 이해하기 쉽게 작성된 언어 체계로, 저급 프로그래밍 언어보다 가독성이 높고 다루기 간단하다는 장점이 있다.  C, C++, C#, JAVA 등을 고급 프로그래밍 언어라고 한다.

※ 여기서 저급과 고급은 좋고 나쁨이 아니라 저급일수록 기계어 문법과 유사하고 고급일수록 사람의 언어와 유사한 것이다.

//고급 프로그래밍 언어 예제(java)
int num1 = 8, num2 = 4;
result = num1 + num2;
System.out.println("+ 연산자에 의한 결과 : "+ result);

// + 연산자에 의한 결과 : 12

어셈블리어보다 이해하기 쉽다. 그 이유는 사람이 쓰는 수학적 논리 구성과 인간의 언어로 구성되어있기 때문이다.

4) 빌드(Build)

빌드 과정

고급 프로그래밍 언어와 같은 각각의 언어 문법에 맞게 명령들을 기술한 파일들을 원시파일(source File)이라고 하며, 간단하게는 소스코드라고 부른다. 소스로 작성한 명령들을 CPU가 알아들을 수 있게 기계어로 번역하는 작업컴파일(Compile)이라고 부르고, 고급 프로그래밍 언어로 작성된 명령어를 기계어로 변경하는 프로그램 컴파일러(Compiler)라고 부른다.

컴파일(Compile)은 어떻게 이루어질까? 원시파일(source File)로 작성된 내용은 기계어 코드로 바뀌면서 그 결과를 목적파일(Object File)에 저장된다. 컴파일러는 원시파일을 해석해서 목적파일로 바꾸는 프로그램이다. 하지만 목적파일만 있다고 바로 실행할 수 있는건 아니다. 운영체제가 요구하는 코드가 추가로 있어야 실행할 수 있는 것이다.

목적파일을 실행가능파일(Executable File)로 바꾸기 위해서는 운영체제 요건에 맞게 형태가 바뀌고 StartUp이라는 실행코드가 있어야 한다. 이러한 처리를 링크(Link)라고 하며, 목적파일을 링크 처리해서 실행파일로 바꿔주는 프로그램을 링커(Linker)라고 한다.

우리가 만든 소스코드를 컴퓨터가 실행할 수 있도록 번역해주는 과정, 이 전 과정을 빌드(Build)라고 부른다.

최근 개발툴들은 그 자체에 소스 편집기, 컴파일러, 링커를 모두 내장하고 있어서 한번에 소스를 실행파일로 바꿀 수 있다. (하지만 아직 일부 리눅스, 유닉스 환경에서는 이 과정을 프로그래머가 거쳐야 실행파일로 만들 수 있다.) 여기에 디버거, 프로파일러, 개발편의 기능까지 제공하기 때문에 우리는 이러한 개발 툴들을 통합 개발 환경(IDE)라고 부른다. 컴파일러는 소스파일을 목적파일로 변환하는 프로그램을 의미하지만 요즘엔 개발 환경 자체를 컴파일러라고 부르기도 한다.

빌드(Build)는 소스를 실행파일로 바꾸는 과정이다. 그렇다면 빌드는 어떤 방식으로 진행될까?

[ 빌드 방식 종류 ]

Type Process
컴파일(Compile) 소스코드 전체를 기계어로 번역
인터프리터(InterPreted) 소스코드를 한 줄씩 번역하면서 실행
하이브리드(Hybrid) 소스코드 전체를 중간코드(바이트코드)로 번역한 뒤 가상머신(VM)에서 한줄씩 실행

참고 : https://st-lab.tistory.com/176 (글이 너무 좋아서 직접 들어가서 읽어보는 것을 추천. 프로그래밍 빌드 과정 설명중에 가장 좋은 포스팅인듯.)

1. Compile Type : 통번역

컴파일 타입(Compile Type)소스코드 전체를 기계어로 한번에 번역해서 실행파일로 만들어준다. 대표적으로 C, C++, Go 언어가 있다.

컴파일러를 이용한 빌드 과정

언어별로 약간의 과정이 추가 또는 생략될 수 있지만 대부분 위와 같은 과정을 거치며 실행파일로 만든다.

전처리(preprocessing)main 소스가 시작되기 전에 소스코드에 포함된 매크로나 지시자 같은 것을 포함시켜준다. 예를 들어 C언어나 C++ 언어에서 #으로 시작하는 구문들(#include, #define 등)과 같은 것들을 처리해준다. 책을 번역하기 전에 저자 이름과 인용 등을 먼저 처리해서 나중에 일일히 찾아보지 않게끔 먼저 정리해준다고 생각하면 좋다.

컴파일(Compilation)은 원시파일(source File)로 작성된 내용을 기계어 코드로 바뀌면서 그 결과를 목적파일(Object File)에 저장하는 것으로 위에서 말했는데, 사실 원시파일 -> 어셈블리어(저수준언어) -> 목적파일로 가기도 한다. 대표적으로 C, C++ 언어의 경우 컴파일 과정에서 소스코드(=원시파일)을 저수준 언어로 번역하니 상황마다 어셈블(assemble)과정을 거치기도, 안거치기도 한다. (gcc, g++, Visual C++ 과 같은 컴파일러들은 저수준언어로 번역해준다) 

어셈블(assemble)저수준언어(어셈블리어)를 기계어로 번역하여 목적파일(Object File)로 번역해준다. 여기서는 목적이라고 하지만 '객체'로 생각하면 이해가 편하다. 컴퓨터도 내부에서 각 책의 챕터별로 번역하기 때문에 각각의 목적파일들이 생기는 것이라고 볼 수 있다.

링크(linking)는 컴파일과 어셈블을 통해 생긴 각각의 목적파일들을 하나로 연결해준다. 각 챕터를 엮어서 하나의 책으로 엮는 것.(위에서 말한 것처럼 운영체제 요건에 맞게 형태가 바뀌고 StartUp이라는 실행코드가 있어야 한다.)

이 과정을 한번에 단계를 밟아 처리하는게 컴파일 방식이다.

컴파일 타입(Compile Type) : 통번역
장점 빌드 완료한 실행가능한 파일은 실행속도가 빠르다.(다 끝냈으니까)
매번 번역할 필요없이 실행파일만 실행하면 된다.
단점 만약 프로그램을 수정하면 처음부터 다시 통번역해야한다.
플랫폼에 매우 의존적이다.
(이 말은 Mac과 Window와 64비트, 32비트 별로 설치파일이 다른 이유이기도 한데, CPU하나에 1:1로 매칭되는 어셈블리어이기 때문에 해당 플랫폼 전용 번역본이라 수정되거나 OS를 변경할 경우 다시 조건에 맞게 형태를 바꾸고 처음부터 다시 빌드해줘야 한다.)

 

2. Interpreted Type : 통역

인터프리트 타입(Interpreted Type) 소스코드를 한 줄씩 번역하면서 실행해준다. 대표적으로 자바스크립트, Ruby 언어가 있다.

목적파일을 만드는게 아니라 곧바로 번역해서 그때그때 실행한다

소스코드의 한 명령 세트마다 기계어로 번역하면서 바로바로 실행해주는 프로그램(또는 환경)인터프리터(Interpreter)라고 한다. 실행파일을 안만든다기보다는 소스코드 자체가 실행가능한 파일로 바뀌는 것. 얘는 각 OS에 맞는 번역가만 세우면 다 번역해주기 때문에 플랫폼에서 독립적이다.

인터프리트 타입(Interpreted Type) : 통역
장점 컴파일 과정 없이 바로 실행하므로 수정, 디버깅에 유리하다. 즉, 개발 속도가 빠르다.
(바꿔도 또 그 부분만 번역해서 실행하면 되기 때문.)
각 플랫폼에 지원하는 인터프리터만 있으면 실행이 가능하기 떄문에 플랫폼에 독립적이다.
단점 빌드가 되어있는 컴파일 언어 프로그램보다는 실행시간이 느리다. 컴파일 언어 프로그램은 이미 다 되어있는거 실행만 하면 되지만 인터프리트 프로그램은 번역 후 실행을 반복하기 때문.
(하지만 요즘이야 하드웨어 스펙이 높아져서 사람들이 체감할만큼 차이가 있지도 않다)
코드를 열면 내부가 다 보이기 때문에 보안에 좋지 않다.

 

3. Hybrid Type : 영어(Byte Code)로 1차 번역 후 통역

하이브리드 타입(Hybrid Type) 소스코드 전체를 중간코드(바이트코드)로 번역한 뒤 가상머신(VM)에서 한줄씩 실행해준다. 대표적으로 JAVA 언어가 있다.

바이트코드로 컴파일 후 가상머신에서 한 명령여씩 번역해서 그때그때 실행한다.

프랑스어, 힌두어를 할 줄 아는 사람을 데려와서 그때그때 통역하는 것도 방법이지만 영어(=바이트코드)는 어디서든 대부분 쓰기 때문에 조금 번거롭더라도 중간 번역과정을 거쳐두면 그 다음부터 재사용하기 쉽고 다른 언어로도 번역하기 쉽다.

바이트 코드(Byte Code)일종의 중간 언어이다. VM(Vertual Machine : 가상머신)이 중요한데, VM이라는 프로그램이 바이트코드를 기계어로 바꿔준다. 이 VM만 각각의 OS에 맞게 깔려있다면 가상머신 안에 있는 인터프리터(해석기)가 바이트코드를 해석해서 실행한다.

가장 유명한 VM은 JVM(Java Virtual Machine)이다. VM이 해당 운영체제에 맞게 지원만 해준다면 플랫폼에 독립적으로 실행할 수 있다는 장점이 있다. 인터프리터 방식과 같은 원리인 것이다. (물론 VM이 인터프리트(=해석)만 하는 것이 아니라 최근에는 컴파일 방식과 혼용하여 구현되어 있다. 대표적으로 JIT(Just-in-Time) 이 있음.)

하이브리드 타입(Hybrid Type) : 1차 번역 후 통역
장점 각 플랫폼에 지원하는 가상머신(VM)이 있다면 실행가능하기 때문에 플랫폼에 독립적이다.
단점 컴파일 언어처럼 하드웨어를 직접 제어하는 작업은 불가능하다.

 뭐가 인터프리터 언어이고, 뭐가 하이브리드 언어인지는 중요하지 않다. 이런 방식들이 있다는 것이 중요하다. 각 언어는 서로의 것을 기반으로 섞이는 경우가 많기 때문에 이를 나눌 필요는 없다.

4. 프로그램 실행

번역을 마치고 실행가능한 파일을 컴퓨터가 실행시킨다. 처음 설계했던 도면대로, 그리고 그 내용으로 코딩한 내용대로, 컴퓨터가 차례로 실행한다.

▼ 2탄 보러가기

 

일반인에게 설명하는 [자바 프로그램의 실행 과정] - 2. 자바 프로그램 실행

그동안 소스코드를 작성하고 언어를 배우며 써왔지만 실제로 어떻게 동작하는지에 대해서는 등한시한 경향이 있었다. 일반인도 이해할 수 있게, 가장 많이 쓰이는 자바 프로그램이 어떻게 동작

heannim-world.tistory.com

 

반응형
LIST

댓글