각 프로그래밍 언어의 특징 및 패러다임에 대해서 분석하고 컴파일러와 인터프리터의 작동 원리 및 차이점을 아직 제대로 모르는 것 같아서 다시 복습하고자 한다. 내용들은 대부분 프로그래밍 언어 개념(원유헌), 클린 아키텍처(로버트 C. 마틴)에서 참고하였다.


프로그래밍 언어

먼저 프로그래밍이란 무엇이고 프로그래밍 언어란 무엇일까? 필자가 생각하는 프로그래밍이란 어떤 목적을 달성하기 위한 동작을 모아놓은 프로그램을 만드는 것이라 생각한다. 너무 거창한가? 게임도 계산기도 웹서비스도 모두 목적을 달성하기 위한 동작이 구현된 프로그램이고 이를 만드는 것이 프로그래밍이라 생각했다.

그렇다면 컴퓨터에서 어떻게 이러한 동작들을 표현할 것인가? 이전에는 컴퓨터의 전선을 바꾸며 동작을 바꿨으나 이를 보다 못한 폰노이만 선생님이 중앙처리 장치에게 명령을 내리는 방식을 제안한다 그 명령을 구현하는 것은 저수준과 고수준으로 나뉘지만 여하지간 궁극적으로 프로그래밍 언어다. 프로그래밍 언어론에서 정의하는 프로그래밍 언어란 아래와 같다.

프로그래밍 언어란 기계가 읽을 수 있고 사람이 읽을 수 있는 형식으로 계산을 기술하는 표현 체계다.

위와같은 특징을 명확하게 표현하면 기계에서 효율적인 번역이 가능한 구조를 가지도록 단순하게 만들어야 한다는 것과 기계적 특징에 무지한 사람도 충분히 이해하기 쉽도록 추상화 해야 한다는 목적을 가지고 있어야 한다. 또한 프로그래밍 언어가 필연적으로 가져야 하는 특성은 아래와 같은데

  • 일반성 : 상식적이고 일관적인 디자인
  • 직교성 : 구성자가 문맥이 다르다고 자신의 성질 잃으면 안됨
  • 획일성 : 유사한 것들은 유사하게 유사하지 않은 것들은 유사하지 않게

모든 프로그래밍 언어가 위와같은 속성을 모두 만족하는 것은 아니다. 어떠한 언어는 일반성이 부족하지만 직교성이 뛰어나고 어떠한 언어는 획일성은 떨어지지만 일반성이 뛰어난 언어도 있다. 프로그래밍의 관점이 변화함에 따라서 언어의 설계도 변화하고 있지만 근본적으로 기계가 읽을 수 있고 사람이 읽을 수 있으며 일반성, 직교성, 획일성을 갖춘 언어를 만드는 것이 컴퓨터 과학자들의 목표다.


변수란 무엇인가?

변수란 이름(식별자)을 가진 가장 기본적인 추상화 메커니즘이며 속성의 집합, 하나 이상의 주소, 값을 가지고 있다. 변수의 위치는 메모리 상의 위치지만 추상적인 의미로 생각되기도 한다. 이름의 의미는 이름에 연결된 속성들에 의해서 결정된다. 이름에 속성을 연결하는 것을 바인딩이라고 하는데 어떤 언어에서는 이름에 값을 연결 시키는 생성을 선언보다는 바인딩이라 부르기도 한다. 대부분의 바인딩은 프로그램 실행 시간에 이루어지는 동적 바인딩을 한다.


언어 구현 기법

번역 기법

고급 언어로 쓰여진 프로그램을 특정한 컴퓨터에서 직접 실행이 가능한 형태의 프로그램으로 번역해 주는 기법이다.

컴파일러는 고급 언어로 쓰여진 프로그램을 어떤 특정한 컴퓨터에서 직접 실행 가능한 형태의 프로그램으로 번역해 주는 컴퓨터 프로그램이다. 컴파일러의 구조는 크게 전단부와 후단부로 나눌 수 있다. 전단부는 소스 언어에 관계되는 부분으로 소스 프로그램을 분석하고 중간 코드를 생성하는 부분이다. 이에 비해 후단부는 소스 언어보다는 목적 기계에 의존적이며 전단부에서 생성한 중간 코드를 특정 기계를 위한 목적 코드로 번역하는 부분이다.

  • 컴파일러 : 원시 언어가 고급 언어이고 목적 언어가 저급 언어(어셈블리 언어가 이에 속함)인 번역기
  • 어셈블러 : 원시 언어가 어셈블리 언어이고 목적 언어가 준기계어 형태인 번역기
  • 링커 : 기계어로 된 여러 개의 프로그램을 묶어서 로드 모듈 이라는 어느정도 실행 가능한 나의 기계어로 번역해주는 번역기
  • 로더 : 로드 모듈로 된 프로그램을 실행시가능한 기계어로 번역해서 주기억 장치에 적재
  • 프리프로세서 : 원시 언어와 목적 언어가 모두 고금 언어인 번역기 주로 확장된 언어(C → C++)에서 사용하며 번역을 한던 더 한다는 단점을 가짐

그림으로 정리


인터프리터 기법

‘고급 언어를 기계어로 취급하는 컴퓨터’를 주어진 컴퓨터에서 시뮬레이션하여 실행시키는 방법이므로 구동하는 컴퓨터가 바뀌어도 상관없으므로 접근성이 매우 높다.


두 기법의 장단점

구분 장점 단점
번역 기법 한번 디코딩하는 정도의 시간으로 번역을 하고나면 다음부터 빠르게 실행이 가능하므로 전체 실행 시간에 효율적 입출력을 위한 많은 버퍼와 기계 상태 파악이 필요할 수 있음
인터프리터 기법 프로그램이 실행할때까지 원시 언어와 유사하기 때문에 추가 기억장소가 필요없음 매 실행마다 형 검사와 같은 많은 작업을 수행하며 시뮬레이션하기 때문에 실행시간이 길어짐


두 기법의 차이

두 기법은 어떤 차이점을 가지고 있을까? 번역 기법은 입력 프로그램과 동일한 목적 언어로 프로그램을 출력만 하는데 인터프리터는 직접 입력 프로그램을 실행시킨다. 두 기법 모두 위 장단점을 충분히 인지하고 있기 때문에 많은 경우 컴파일러 언어에서도 인터프리터 기법을 응용하고 인터프리터 언어도 번역 기법을 응용한다. 가령 인터프리터 언어의 경우 컴퓨터에서 실행시키기 편리한 형태의 중간 코드로 번역하고 번역한 프로그램을 디코드하여 시뮬레이션한다. 주로 인터프리터 기법에 속하지만 하이브리드 구현 기법이라고도 한다.

규모가 큰 상용 소프트웨어의 경우 C++와 같은 컴파일러 언어를 사용했으며 HTML안에 삽입되는 JavaScript와 같은 성능이 크게 중요하지 않은 경우 인터프리터 기법을 사용되었다. 이후 JavaPerl에서 하이브리드 기법을 사용하며 폭넓게 사용되었다. 예전에는 하이브리드 언어가 컴파일러 언어보다 훨씬 느렸지만 JIT 컴파일러의 사용으로 실행시간 효율이 좋아졌다.

컴파일러 언어와 인터프리터 언어의 구별은 언어 자체에 있는 것이 아니라 구현시키는 방법에 따라서 구분된다. C도 인터프리터로 Python도 번역 기법으로 구현할 수 있다. 궁극적으로 두 기법의 차이는 컴퓨터를 시뮬레이션하여 실행시키는가 아닌가이다.


프로그래밍 패러다임

절차적 프로그래밍 패러다임

프로그래밍 언어는 컴퓨터의 연산을 모방하고 추상화하는 데서 비롯되었다. 따라서 컴퓨터의 구조가 언어 설계의 영향을 미친 것은 당연하다. 즉 프로그래밍 언어는 명령의 순차적 실행, 기억장소 위치를 표현하는 변수의 사용, 변수의 값을 변경하기 위한 배정문의 사용으로 특정지을 수 있다. 이러한 언어를 명령형 언어 또는 절차 언어라고 한다.

오늘날 대부분의 언어는 명령형 언어이지만 계산을 반드시 순차적으로 기술할 필요는 없다. 실제로 순차적 명령에 의해서 실행되는 것은 폰노이만 병목 현상을 불러일으킨다.

폰노이만 병목 현상이란? 일반적으로 자료경로의 병목현상 또는 기억장소의 지연 현상을 이르는데, 이는 나열된 명령을 순차적으로 수행하고, 그 명령은 일정한 기억장소의 값을 변경하는 작업으로 구성되는 폰 노이만 구조에서 기인한다.


논리적 프로그래밍 패러다임

논리형 언어는 기호 논리학에 근거를 두고 있다. 논리형 언어에서는 계산을 실행하기 위한 순서를 기술하는 대신에 무엇을 하려고 하는가를 기술함으로써 프로그램 하게 된다. 순수한 논리형 프로그램은 반복이나 선택문을 필요로 하지 않는다. 논리형 언어에서는 계산의 내용만을 선언하듯이 기술한다. 따라서 논리형 언어는 선언적 언어라고도 부른다.

1
2
(define (gcd u v)
	(if(=v 0) u(gcd v (remainder u v))))

그럼 LISP과 같은 논리형 언어에서는 폰노이만 병목 현상이 발생하지 않나? 그렇다. LISP은 폰노이만 구조와는 다른 연산 방식을 사용하므로 기존 컴퓨터에서 효율적으로 실행시키기 어려워 새로운 구조가 고안되기도 하였다.


객체지향 프로그래밍 패러다임

객체는 기억장소와 이 기억장소의 값을 변경할 수 있는 연산의 집합 클래스가 정의되면 객체를 선언할 수 있다. 많은 객체 지향 언어에서 객체는 클래스로 그룹화 된다. 생성된 클래스로 객체의 특정 예를 생성하는데 이를 인스턴스라고 부른다. 가장 최초로 객체 지향을 소개한 언어는 Simula 67이다.

1
2
3
class Person:
	def Person(name):
		this.name = name

위와같은 클래스가 존재할 때

1
Person m_person;

위와같이 객체를 선언할 수 있으나 위 경우에는 자바(Java)를 예로들어 참조형으로 선언된 것으로 실질적으로 객체가 생성된 것이 아니다. 객체를 생성하기 위해선 new와 함께 생성자(constructor)를 명시하는 형태로 기술한다.

클린 아키텍쳐라는 책에서는 객체지향을 아래와 같이 설명하고 있다.

절차지향은 오로지 컴퓨터 관점에서의 프로그래밍 패러다임이라면 객체지향은 인간이 구분할 수 있는 요소를 객체로 표현한 인간 중심적 프로그래밍 패러다임이다.

WRITTEN BY

배진오

소비적인 일보단 생산적인 일을 추구하며, 좋아하는 일을 잘하고 싶어합니다 :D
im@baejino.com