IT 지식

고수준 언어에서 프로그램 실행까지

OIIUOI 2022. 8. 20. 05:28

1950년대 말, 1960년대 초에 컴퓨터가 프로그래머를 대신해서 더 많은 일을 수행하게 되는 또 다른 움직임이 일어났는데, 아마도 프로그래밍의 역사에서 가장 중요한 발걸음이었을 것이다. 바로 특정 프로세서 아키텍처에 독립적인 고수준 프로그래밍 언어의 개발이다. 고수준 언어를 쓰면 사람이 표현하는 방식에 가까운 용어로 계산 과정을 작성할 수 있다.

 고수준 언어로 작성된 코드는 번역기 프로그램을 통해 대상 프로세서의 어셈블리 언어로 된 명령어로 변환된 다음, 어셈블러에 의해 비트로 변환되어 메모리에 로드되고 실행된다. 여기서 번역기 프로그램은 보통 컴파일러라고 불리는데, 그다지 통찰력이나 직관이 느껴지지 않는 역사적 용어다. 

 일반적인 고수준 언어세너는 두 수 X와 Y를 더하고 결과를 Z에 저장하는 계산이 다음과 같이 표현된다.

 Z = X + Y

이는 'X와 Y라는 메모리 위치에서 값을 가져와서, 두 값을 더하고, Z라는 메모리 위치에 결과를 저장하라'라는 뜻이다. 연산자 '='는 '같다'가 아니라 '대체하다' 또는 '저장하다'라는 뜻으로 쓰인다.

 모형 컴퓨터용 컴파일러는 이 코드를 세 개의 명령어로 변환하겠지만, 또 다른 컴퓨터용 컴파일러는 두 개의 명령어로 변환할 수도 있을 것이다. 다음으로 각 어셈블러는 각자의 어셈블리 언어 명령어를 실제 명령어의 비트 패턴으로 변환하는 일뿐만 아니라 변수 X, Y, Z를 저장할 메모리 위치를 확보하는 일을 담당한다. 비트 패턴은 각각의 컴퓨터에 대해 거의 완전히 다르게 변환된다.

 실제로는 컴파일러가 내부적으로 하나의 '프런트엔더(전단부)'와 몇 개의 '백엔드(후단부)'로 나뉠 가능성이 있다. 프런트엔드는 고수준 프로그래밍 언어로 작성된 프로그램을 처리해서 어떤 중간 형태로 만들고, 각 백엔드는 공통된 중간 표현을 특정 아키텍처에 맞는 어셈블리 언어로 변환한다. 이 구조는 완전히 독립적인 여러 개의 컴파일러를 이용하는 것보다 단순하다.

 고수준 언어는 어셈블리 언어에 비해 큰 이점을 갖는다. 사람들이 생각하는 방식에 더 가까워 배우고 사용하기 더 쉽다. 고수준 언어에서는 프로그램을 효율적으로 짜기 위해 특정 프로세서의 명령어 레퍼토리를 알아야 할 필요가 있다. 따라서 더 많은 이들이 더 빨리 프로그래밍을 할 수 있게 해 준다.

 또한 고수준 언어로 작성된 프로그램은 특정 아키텍처에 종속되지 않는다. 그래서 같은 프로그램이 여러 아키텍처상에서 실행될 수 있다. 보통은 코드를 변경할 필요도 없으며, 다른 컴파일러로 컴파일하기만 하면 된다. 프로그램은 한 번만 작성하면 다양한 컴퓨터에서 실행할 수 있다. 이렇게 하면 프로그램 개발 비용을 여러 종류의 컴퓨터(심지어 아직 출시되지 않은 기종까지 포함하여)에서 발생하는 수익으로 나누어 메울 수 있다.

 또한 컴파일 단계는 몇 가지 명백한 에러를 미리 점검하게 해 준다. 여기에는 철자 오류, 괄호 불일치 같은 구문 오류, 정의되지 않은 변수에 대한 연산 같은 것들이 포함되며, 실행 프로그램이 만들어지려면 프로그래머가 이러한 에러를 반드시 고쳐야만 한다. 반면 어셈블리 언어 프로그램에서는 구문 오류 이외의 에러는 검출하기 어렵다. 어셈블러는 명령어의 실행 순서까지는 상관하지 않아서 논리적인 흐름이나 전후 관계를 파악하지 못하기 때문이다(물론 고수준 언어에서도 구문상 올바르다고 판단되는 프로그램이 여전히 컴파일러가 검출할 수 없는 에러로 가득할 수도 있다. 가령 프로그래머가 곱하기 대신 나누기 연산자를 사용한 것과 같은 의미 체계 오류가 남아 있는 것처럼 말이다.). 이와 같이 고수준 언어의 중요성은 아무리 강조해도 지나치지 않다.

 프로그래밍 언어 간의 유사점과 차이점을 살펴볼 수 있도록 가장 중요한 고수준 프로그래밍 언어 여섯 가지(포트란, C, C++, 자바, 자바 스크립트, 파이썬)로 작성된 동일 기능의 프로그램을 보여 주려고 한다. 각 프로그램은 앞서 만든 모형 컴퓨터 프로그램과 동일한 계산을 수행한다. 일련의 정수를 합산하다가 0 값이 입력되면 합계를 출력하고 작동을 멈춘다. 프로그램들은 모두 같은 구조로 되어있다. 프로그램이 사용하는 변수에 이름을 붙이고, 누적 합계를 0으로 초기화한 다음, 수를 읽으면서 0을 만나기 전까지는 누적 합계에 더하고, 마지막으로 합계를 출력한다. 구문 세부 사항은 신경 쓰지 않아도 된다. 언어가 어떻게 생겼는지 경험하는 것이 중요하다. 

 초기 고수준 언어들은 특정 응용 분야에 집중된 형태였다. 포트란은 초창기 언어 중 하나인데 수식 변환에서 이름이 유래했고 오늘날에는 포트란으로 표기한다. 포트란은 존 배커스가 이끈 IBM 팀에서 개발했고, 과학과 공학 분야에서 계산을 표현하는 데 매우 성공적으로 사용됐다. 많은 과학자와 공학자들은 첫 프로그래밍 언어로 포트란을 배웠다. 포트란은 오늘날에도 건재하다. 1958년 이래로 몇 번의 진화 단계를 거쳤지만, 같은 언어라고 쉽게 알아볼 수 있을 정도다. 배커스는 포트란 개발 공로를 인정받아 1977년 튜링상을 받았다.

 두 번째로 소개할 주요 고수준 언어는 1950년대 후반에 등장한 코볼이다. 코볼은 어셈블리 언어를 대체할 고수준 언어를 만들기 위한 그레이스 호퍼의 작업 결과물에 큰 영향을 받았다. 그는 고수준 언어와 컴파일러의 잠재력을 처음으로 알아본 사람 중 한 명이었다. 코볼은 특히 사무 데이터 처리 목적으로 사용되었고, 재고 관리, 송장 작성, 급여 계산 등에 사용되는 자료 구조와 계산을 쉽게 표현할 수 있는 언어적 특징이 있었다. 코볼도 아직까지 사용되는 언어인데, 모습이 많이 바뀌었지만 아직 알아볼 만하다. 오래전에 개발되어 계속 사용되는 코볼 프로그램은 많지만, 코볼 프로그래머는 많지 않다. 2020년에 뉴저지주에서 실업수당 청구를 처리하는 낡은 코볼 프로그램이 COVID-19 때문에 늘어난 작업량에 대응하지 못하는 문제가 발생했는데, 코볼 프로그램을 업그레이드할 숙련된 프로그래머를 필요한 만큼 구하지 못한 일도 있었다.

 당시의 또 다른 언어인 베이직은 1964년에 다트머스 대학에서 일하던 존 케메니와 톰커츠가 개발했다. 베이직은 프로그래밍 교육을 위한 쉬운 언어로 만들어졌다. 베이직은 특히 간단하면서 컴퓨팅 자원을 적게 필요로 해서 개인용 컴퓨터에서 사용할 수 있는 첫 번째 고수준 언어였다. 마이크로소프트 창업자인 빌 게이츠와 폴 앨런은 1975년에 알테어 마이크로컴퓨터용 베이직 컴파일러를 만들면서 사업을 시작했는데, 이게 마이크로소프트의 첫 번째 제품이었다. 베이직의 주요한 변종인 마이크로소프트 비주얼 베이직이 오늘날에도 활발하게 지원되고 있다.

 초창기에는 컴퓨터가 비싸고, 느리고, 성능에도 한계가 있어, 고수준 언어로 작성된 프로그램이 매우 비효율적일 것이라는 우려가 있었다. 컴파일러가 숙련된 어셈블리 언어 프로그래머가 작성한 것만큼 작고 효율적인 어셈블리 코드를 잘 만들지 못했기 때문이다. 컴파일러 개발자들은 손으로 작성한 것만큼 좋은 코드를 생성하고자 열심히 노력했으며, 이는 프로그래밍 언어를 확립하는 데 도움이 됐다. 지금은 컴퓨터가 수백만 배 빠르고 메모리가 풍족해졌기에 프로그래머가 개별 명령어 수준의 효율성까지 걱정하는 경우는 드물다. 그래도 컴파일러와 컴파일러 개발자들은 여전히 여기에 신경 쓰고 있음이 분명하다.