이 페이지의 목차
번역 방식으로 나눠 보는 언어 — 컴파일러와 인터프리터
코드 예제: Python · C · 예상 50분
학습 목표
- 컴파일러와 인터프리터의 번역 방식 차이를 설명한다.
- 같은 문제를 두 방식의 언어로 실행할 때의 차이를 비교한다.
관련 성취기준
- 프그01-01
각종 프로그래밍 언어의 종류와 특징을 이해하고, 개발 분야와 용도에 따라 적절한 프로그래밍 언어를 선택할 수 있다.
생각 열기 — 번역가와 통역사
외국어로 된 책을 읽어야 한다. 방법은 두 가지다.
하나는 번역가에게 책 전체를 맡기는 것이다. 시간이 걸리지만 한 번 끝내 두면 한국어 책이 남는다. 그 뒤로는 번역가 없이 몇 번이든 읽을 수 있고, 읽는 속도도 빠르다.
다른 하나는 통역사를 옆에 두는 것이다. 한 문장 읽을 때마다 그 자리에서 옮겨 준다. 기다릴 필요 없이 바로 시작할 수 있다. 그러나 읽는 동안 통역사가 계속 옆에 있어야 하고, 같은 책을 다시 읽을 때도 처음부터 다시 옮겨야 한다.
고급 언어는 반드시 번역된다
1차시에서 확인한 대로 중앙처리장치가 실행할 수 있는 것은 기계어뿐이다. Python이든 C든 고급 언어로 쓴 코드는 그대로 실행되지 않는다. 기계어로 옮기는 단계를 반드시 거친다.
실행하기 전에 소스 코드 전체를 미리 기계어로 옮겨 두는 방식을 1이라 하고, 실행하면서 한 줄씩 그때그때 옮기는 방식을 2이라 한다.
정답 보기
- 컴파일
- 해석
옮기는 방식은 앞의 번역가와 통역사처럼 두 갈래로 나뉜다. 실행하기 전에 전체를 미리 옮겨 두는 방식이 컴파일이고, 실행하면서 필요한 만큼 그때그때 옮기는 방식이 해석이다.
컴파일러 — 미리 전부 번역한다
컴파일러(compiler) — 소스 코드 전체를 실행 전에 한 번에 번역하는 프로그램는 소스 코드 전체를 훑어 기계어로 옮긴다. 그 결과로 나오는 것이 목적 코드(object code) — 기계어로 번역됐지만 아직 혼자 실행되지는 못하는 중간 결과물다.
목적 코드는 아직 혼자 실행되지 못한다. 프로그램이 쓰는 화면 출력 기능처럼 미리 만들어져 있는 부분과 이어 붙여야 하기 때문이다. 여러 목적 코드와 라이브러리를 하나로 묶어 실행 파일을 만드는 일을 링커(linker) — 목적 코드와 라이브러리를 묶어 실행 파일로 만드는 프로그램가 한다.
이 과정이 끝나면 실행 파일 하나가 남는다. 소스 코드도 컴파일러도 필요 없다. 우리가 내려받아 쓰는 프로그램 대부분이 이렇게 만들어진 실행 파일이다. C가 대표적인 컴파일 방식 언어다.
인터프리터 — 한 줄씩 그때그때 번역한다
인터프리터(interpreter) — 소스 코드를 실행하면서 한 줄씩 번역해 곧바로 수행하는 프로그램는 실행 파일을 만들지 않는다. 소스 코드를 위에서부터 읽어 한 줄을 번역하고 곧바로 실행한 뒤 다음 줄로 넘어간다.
그래서 코드를 고치고 바로 실행해 결과를 볼 수 있다. 대신 프로그램을 실행하려면 소스 코드와 인터프리터가 늘 함께 있어야 한다. 번역과 실행을 같이 하므로 같은 계산이라면 실행 파일보다 느리다. Python이 대표적인 인터프리터 방식 언어다.
- 1소스 코드
- 2컴파일러가 번역
- 3목적 코드
- 4링커가 묶음
- 5실행 파일
목적 코드는 아직 혼자 실행되지 못한다. 라이브러리와 이어 붙이는 링크 단계를 거쳐야 실행 파일이 된다.
언제 오류를 만나는가
두 방식의 차이가 가장 뚜렷하게 드러나는 곳이 오류를 만나는 시점이다. 같은 실수를 두 언어에 심어 보자. 세 줄을 출력하는 프로그램인데 마지막 줄에서 print를 prnit로 잘못 적었다.
세 줄을 출력하는 코드인데 셋째 줄에 오타가 있다. Python으로 실행하면 화면에 어디까지 찍힐까?
힌트 — 인터프리터는 위에서부터 한 줄씩 번역하며 실행한다
첫째 줄 둘째 줄
결과 바로 보기
첫째 줄 둘째 줄
print("첫째 줄")
print("둘째 줄")
prnit("셋째 줄")첫째 줄 둘째 줄
브라우저에서 실행되지 않는 예제다. 일부러 오류를 내는 예제다. 표준 출력만 적었고, 그 뒤에 오류 메시지가 따라 나온다. 전산실 컴파일러에 붙여 넣어 확인해 보자.
앞의 두 줄은 출력됐다. 그리고 세 번째 줄에서 멈추며 NameError: name 'prnit' is not defined 라는 오류가 나온다. 인터프리터는 첫 줄부터 차례로 번역해 실행하므로, 세 번째 줄에 닿기 전까지는 오타가 있다는 사실 자체를 모른다.
#include <stdio.h>
int main(void)
{
printf("첫째 줄\n");
printf("둘째 줄\n");
prntf("셋째 줄\n");
return 0;
}(화면에 출력되는 내용 없음)
브라우저에서 실행되지 않는 예제다. 일부러 컴파일이 실패하는 예제다. 실행 파일이 만들어지지 않으므로 출력이 없다. 전산실 컴파일러에 붙여 넣어 확인해 보자.
C는 아무것도 출력하지 않는다. 컴파일러가 소스 코드 전체를 먼저 훑어 일곱째 줄의 오타를 찾아내고, call to undeclared function 'prntf' 라는 메시지를 낸 뒤 실행 파일을 만들지 않는다. 앞의 두 줄은 옳게 적혀 있지만 실행될 기회가 없다.
같은 계산을 두 언어로
1부터 10까지 더하는 프로그램을 두 언어로 적었다. 상단의 언어 단추로 바꿔 볼 수 있다.
total = 0
for i in range(1, 11):
total += i
print("1부터 10까지의 합:", total)1부터 10까지의 합: 55
#include <stdio.h>
int main(void)
{
int total = 0;
for (int i = 1; i <= 10; i++) {
total += i;
}
printf("1부터 10까지의 합: %d\n", total);
return 0;
}1부터 10까지의 합: 55
결과는 같은 55다. 다른 것은 그 결과에 이르는 길이다. Python 코드는 저장하고 곧바로 실행했다. C 코드는 컴파일해 실행 파일을 만든 뒤 그 파일을 실행했다. 문법의 차이는 2단원에서 하나씩 다룬다.
두 방식을 섞은 하이브리드
컴파일과 해석은 서로 배타적이지 않다. 둘을 섞은 하이브리드 방식(hybrid) — 소스 코드를 중간 형태로 컴파일한 뒤 그것을 해석해 실행하는 방식도 널리 쓰인다.
Java가 대표적이다. Java 컴파일러는 소스 코드를 특정 중앙처리장치의 기계어가 아니라 바이트 코드(byte code) — 특정 중앙처리장치가 아니라 가상 머신이 해석하도록 만든 중간 코드로 번역한다. 그 바이트 코드를 가상 머신(virtual machine) — 바이트 코드를 해석해 실제 기계에서 실행해 주는 프로그램이 해석해 실행한다. 한 번 컴파일한 바이트 코드를 가상 머신이 있는 어느 기종에서나 쓸 수 있다는 것이 이 방식의 이점이다.
두 방식을 갈라 보기
카드를 누른 뒤, 넣을 통을 누른다. 통 안의 카드를 누르면 다시 나온다.
컴파일 방식
인터프리터 방식
- 실행 파일이 남는다 — 번역 결과를 파일로 만들어 두므로 소스 코드 없이도 실행된다
- 문법 오류를 실행 전에 알려 준다 — 전체를 미리 훑으므로 한 곳만 틀려도 실행 파일이 만들어지지 않는다
- 오류가 있는 줄에 닿기 전까지는 실행된다 — 위에서부터 한 줄씩 번역하며 실행하기 때문이다
정리
| 기준 | 컴파일 방식 | 인터프리터 방식 |
|---|---|---|
| 번역 시점 | 실행 전에 전체를 한 번에 | 실행하면서 한 줄씩 |
| 남는 산출물 | 목적 코드와 실행 파일 | 없다 |
| 실행 속도 | 빠르다 | 상대적으로 느리다 |
| 문법 오류를 만나는 때 | 실행 전 컴파일 단계 | 그 줄에 닿았을 때 |
| 코드를 고친 뒤 | 다시 컴파일해야 한다 | 바로 실행한다 |
| 실행에 필요한 것 | 실행 파일만 | 소스 코드와 인터프리터 |
| 다른 기종으로 옮기기 | 기종마다 다시 컴파일한다 | 인터프리터만 있으면 된다 |
| 대표 언어 | C, C++ | Python, JavaScript |
활동
같은 코드를 두 방식으로 실행해 차이 관찰하기
약 15분- 위의 합 구하기 Python 코드를 실행해 결과를 확인한다.
- 같은 일을 하는 C 코드를 전산실 컴파일러로 컴파일해 실행한다. 컴파일 명령과 실행 명령이 따로 있다는 점을 확인한다.
- 두 코드에서 각각 한 글자만 일부러 지운다. Python은
range의 괄호 하나, C는 문장 끝 세미콜론 하나를 지워 보자. - 오류 메시지가 언제 나왔는지 기록한다. 실행하기 전인가, 실행 중인가.
- 표로 정리한다 — 지운 것 / 오류가 난 시점 / 오류 메시지 / 앞부분이 실행됐는가.
3번에서 지운 글자는 반드시 되돌려 놓는다.
개념 확인 문제
보기를 누르면 바로 채점된다. 틀려도 해설을 읽으면 된다.
컴파일러와 인터프리터의 가장 큰 차이는 무엇인가?프그01-01
본문의 오타 예제에서 C 코드가 첫째 줄조차 출력하지 못한 이유는?프그01-01
[OX] C로 컴파일해 만든 실행 파일은 종류가 다른 중앙처리장치에서도 그대로 실행된다.프그01-01
Java의 실행 방식을 바르게 설명한 것은?프그01-01
더 알아보기
심화느린 인터프리터를 빠르게 — JIT 컴파일
인터프리터 방식의 약점은 속도다. 같은 반복문을 수만 번 돌 때, 그 안의 코드를 매번 다시 번역하는 것은 낭비다. 그래서 자주 실행되는 부분만 실행 중에 기계어로 컴파일해 두고 다음부터는 그것을 쓰는 방법이 나왔다. 실행하는 그때 컴파일하므로 적시 컴파일(JIT, Just-In-Time compilation)이라 한다. 오늘날 웹 브라우저의 JavaScript 실행기와 Java 가상 머신이 이 방법을 쓴다.
인물최초의 컴파일러를 만든 그레이스 호퍼
1952년, 미국의 수학자이자 해군 장교였던 그레이스 호퍼는 사람이 읽을 수 있는 기호로 적은 코드를 기계어로 바꿔 주는 프로그램 A-0를 만들었다. 최초의 컴파일러로 평가받는다. 당시에는 “컴퓨터에게 영어에 가까운 말로 지시한다”는 생각 자체가 받아들여지지 않았다고 한다. 그가 참여한 COBOL은 이후 수십 년간 금융·행정 업무 프로그램의 표준이 되었다.
실무컴파일 오류와 실행 오류는 다른 것이다
컴파일 단계에서 걸리는 것은 문법이 어긋난 코드다. 괄호가 안 닫혔거나 없는 이름을 불렀거나 자료형이 맞지 않는 경우다. 그런데 문법이 완벽해도 계산이 틀리거나 프로그램이 중간에 멈추는 일은 얼마든지 있다. 0으로 나누기, 없는 파일 열기, 조건을 거꾸로 쓴 경우가 그렇다. 이런 것은 컴파일러가 잡아 주지 못하고 실행해 봐야 드러난다. 그래서 컴파일 성공은 출발점일 뿐이며, 5차시에서 다루는 테스트가 필요하다.
이어지는 학습
이 차시를 이해하려면 다음을 먼저 알아야 한다.
- 1차시의 저급 언어와 고급 언어 구분 — 프로그래밍 프그01-01
교과서에서 찾기
| 교과서 | 해당 위치 | 쪽수 |
|---|---|---|
| 성림출판(Python) | 프로그래밍의 개요 › 01 프로그래밍 언어의 종류와 특징 | 12쪽 |
| 씨마스커뮤니케이션(Python) | 프로그래밍 개요 › 1 프로그래밍 언어의 종류와 특징 | 14쪽 |
| 삼양미디어(Python) | 프로그래밍의 개요 › 01 프로그래밍 언어 | 12쪽 |
| 삼양미디어(C) | 프로그래밍의 개요 › 01 프로그래밍 언어 | 12쪽 |
| 씨마스(C) | 프로그래밍의 개요 › 1 프로그래밍 언어의 종류와 특징 | 12쪽 |
| 웅보출판사(C) | 프로그래밍의 개요 › Chapter 01 프로그래밍 언어의 종류와 특징 | 11쪽 |