프로그래밍 언어는 뭘까?
프로그래밍 언어는 무엇일까? 프로그래밍을 하고 코딩을 한다는 소리를 들었지만 프로그래밍 언어에 대해서 자세히 모르는 경우가 많다. 일단 프로그래밍 언어를 정확히 알기 위해서는 프로그래밍 언어가 동작하기 위해 존재하는 컴파일러, 운영체제, 하드웨어를 같이 알아야 한다. 아래에서 해당 내용에 대해 예를 들면서 설명할 예정이므로 모르는 용어라고 겁을 먹지 말고 차근차근 확인해보자. 영어를 배우기 전에 미국에 대해 공부한다고 생각하자.
하드웨어는 컴퓨터에서 실제로 보이는 기계장치라고 생각하면 된다. 컴퓨터를 조립해본 경험이나 또는 핸드폰을 구매해 본 경험이 있다면 CPU, 메모리, 디스크 등의 스펙을 보고 고른 경험이 있을 것이다. 그 내용이 하드웨어이다. 운동을 배우는 것에 비유하면 배운 운동을 실제로 적용하여 움직일 몸이 하드웨어라고 할 수 있다. 그리고 그 몸에게 어떻게 움직여야 하는지에 대해서 알려주는 부분이 프로그램이다. 프로그램이 하드웨어에 요청하는 작업을 크게 보면 두 가지 동작으로 나눠 볼 수 있다. 계산을 하는 동작과 정보를 저장하는 동작이다. 예를 들어, 단순한 계산기 프로그램만 생각해보아도 우리가 입력한 숫자를 저장한 후에 계산을 하고 계산의 결과를 다시 저장하여 보여준다. 따라서, 하드웨어를 크게 정보를 저장하는 기억 장치(메모리, 디스크)와 계산을 하는 연산 장치(CPU : Central Processing Unit)로 나눠서 설명할 수 있다.
기억 장치는 말 그대로 저장을 하는 장치이다. 따라서 프로그래밍 언어를 사용하면서 다루어지는 데이터들은 기억 장치인 메모리 또는 디스크에 저장되게 된다. 그리고 해당 기억 장치에 저장되어 있는 데이터를 이용하여 덧셈 또는 곱셈 등의 별도의 계산이 필요한 부분을 CPU인 연산 장치가 해준다. CPU, 메모리, 디스크의 관계는 음식점에 비유해서 설명하면 더 쉽게 이해할 수 있다. 음식점에서 실제로 요리가 직접적으로 진행되는 장소(냄비, 도마)와 주방장을 합쳐서 CPU라고 할 수 있고 요리를 만드는 행위를 CPU의 동작으로 볼 수 있다. 이때 요리를 만들기 위해서 중간에 만들어지는 재료들(육수, 잘라 놓은 야채 등)과 재료들로 만들어진 최종 요리는 CPU가 계산해서 알려주는 결괏값들이 된다. 요리에 사용되는 식재료들과 요리가 진행되면서 중간에 만들어지는 재료들이 저장되는 주방의 공간이 메모리이다. 장기간 보관될 식재료들이 존재하는 냉장고와 창고는 디스크이다. 요리에 바로 쓰이는 식재료와 요리를 진행하면서 만들어진 재료들은 금방 사용할 수 있도록 주방의 공간에 저장되고 자주 쓰지 않는 재료는 꺼내는데 시간이 걸리는 냉장고와 창고에 있게 된다. 따라서 비유를 보고 생각해보면 CPU는 요리를 만드는 작업을 하지만 넉넉한 공간이 없기 때문에 필요한 식재료나 중간에 만들어지는 재료들을 메모리에 저장해놓는다. CPU가 만들어 내는 중간 결과들이 계속 메모리에 저장되게 되고 CPU는 다시 메모리에서 중간 결과들을 가져와서 계산을 하게 된다. 이때 메모리에도 올릴 수 없을 정도로 큰 데이터나 자주 쓰지 않는 데이터는 냉장고와 창고의 역할을 하고 있는 디스크에 저장된다. 요약하면 자주 쓰는 데이터는 성능이 빠르지만 비싸서 공간이 적은 메모리에 넣어놓고 자주 쓰지 않는 데이터나 너무 커서 메모리에 보관할 수 없는 데이터는 디스크에 보관하고 조금씩 메모리로 데이터를 가져와서 처리하게 된다. 프로그래밍 언어 설명에서 이런 하드웨어 구조에 대해서 설명을 한 이유는 프로그래밍 언어가 이런 하드웨어의 특성을 이용하여 구현되어 있기 때문이다.
요리하는 사람 + 냄비(CPU) - 주방 공간(메모리) - 냉장고, 창고(디스크)
너무 비유만해서 직관적으로 이해가 안될 수 있으므로 프로그래밍 언어에서 1+1을 계산할 때 어떤 일이 일어나는지 설명을 하겠다. 1+1을 계산하라고 하면 1이라는 데이터를 저장할 공간이 메모리에 할당(공간 확보)된다. 공간이 확보되면 1이라는 값을 저장한다. CPU는 메모리에 있는 1이라는 값을 이용하여 1+1이라는 연산을 하여 2라는 값을 계산한다. 계산된 2라는 값은 다시 메모리에 공간을 확보하여 저장된다. 최종적으로 우리는 메모리에 접근하여 2라는 값을 확인할 수 있다.
그러면 다시 프로그래밍 언어는 무엇일까라는 질문에 대답을 하면 프로그래밍 언어는 CPU, 메모리, 디스크 등의 컴퓨터 자원을 사용할 수 있도록 명령을 내려주는 언어라고 할 수 있다. 그런데 더 명확히 들어가 보면 실제로 프로그래밍 언어는 운영체제에게 요청을 하는 언어이다. CPU, 메모리, 디스크 등의 하드웨어 자원을 사용하는 작업은 굉장히 복잡하기 때문에 하드웨어 자원을 사용해주는 프로그램(소프트웨어)이 따로 존재하는데 해당 프로그램이 운영체제이다. 우리가 익히 알고 있는 윈도우, 리눅스, 유닉스, 매킨토시 등이 해당한다. 이런 운영체제가 하드웨어를 다 관리해주기 때문에 우리는 편하게 운영체제한테 요청만 하면 하드웨어를 동작시키기 위한 명령들에 대해서 알 필요 없이 쉽게 하드웨어를 다룰 수 있다. 따라서, 우리는 실제 프로그래밍을 하면 운영체제한테 하는 요청과 다른 사람이 운영체제에게 요청을 하기 위해 미리 작성해 놓은 코드(프로그래밍 언어로 작성한 내용)를 가져다 쓰게 된다. 운영체제 위에 한 겹 더 있는 경우도 있다. 예를 들어 웹 프로그래밍은 우리가 사용하는 웹브라우저(익스플로러, 크롬, 파이어폭스, 사파리 등)에게 요청을 한다. 그런데 웹브라우저를 자세히 들어가 보면 웹브라우저가 결국 운영체제에게 요청하게 된다.
여기까지 이해했다면 컴파일러와 인터프리터에 대해서 간략히 설명하려 한다. 우리의 프로그래밍 언어가 운영체제에게 요청을 한다고 했지만 사람을 위해서 개발된 프로그래밍 언어는 운영체제 입장에서 도저히 이해할 수 없는 언어이다. 운영체제가 이해할 수 있도록 프로그래밍 언어를 번역해주는 역할을 하는 존재가 컴파일러와 인터프리터이다. 컴파일러는 써놓은 모든 코드를 참조해서 번역을 해주고 인터프리터는 보이는 일부 코드만 보고 실시간으로 번역을 시도한다. 번역이라고 했지만 컴파일러와 인터프리터는 천재적인 번역가이다. 컴파일러와 인터프리터는 단순한 번역 외에 운영체제를 더 효율적으로 동작시킬 수 있도록 최적화를 해주는 역할도 해준다. 초월 번역이라고 생각하면 된다. 비효율적으로 10개의 문장을 써놨다면 컴파일러와 인터프리터는 해당 문장을 1개의 문장으로 바꿔주는 역할을 한다. 그런데 이런 최적화를 할 때 컴파일러는 전체를 참조해서 가장 적절한 말을 찾고 인터프리터는 실시간으로 현재까지 본 내용에 맞춰서 가장 적절한 말을 찾게 된다. 따라서 번역을 하는데 소모되는 시간과 초월 번역이 얼마나 잘 되었는지가 달라지게 된다. 그래서 컴파일러와 인터프리터는 둘 다 훌륭한 번역가이지만 번역된 프로그래밍 언어는 다소 차이가 있고 서로 다른 특징을 가지고 있다. 둘 다 상황에 따라 다른 용도로 필요한 훌륭한 번역가이므로 편견을 가지고 보지는 말자.
이렇게 복잡한 프로그래밍 언어가 돌아가는 환경에 대해서 파악했다면 프로그래밍을 할 때 중요시되는 논리(규칙)에 대해서 생각해보자. 프로그래밍 언어를 배우면 논리적 사고가 중요하다거나 논리적으로 무엇을 해야 한다라는 말이 나온다. 그러면 왜 그런 말들이 나오게 되는 걸까? 그 이유는 앞에서도 설명을 들었지만 컴퓨터 및 프로그래밍 언어의 탄생 자체가 수많은 논리의 산으로 구성되어 있기 때문이다. 위에 설명한 내용만 봐도 하드웨어의 논리, 운영체제의 논리, 컴파일러의 논리, 프로그래밍 언어의 논리들이 쌓여 있고 운영체제는 하드웨어의 논리를 사용하고 컴파일러는 운영체제의 논리를 사용하고 프로그래밍 언어는 컴파일러의 논리를 사용하고 있다. 논리를 사용하는 입장에서는 사용되는 곳에서 어떤 복잡한 구조로 돌아가는지 확인할 필요 없이 이용할 수 있는 논리만을 사용하면 된다.
지금까지 설명한 프로그래밍 언어를 되돌아보면 프로그래밍 언어는 컴파일러를 통해서 운영체제가 이해할 수 있는 언어로 번역되고 운영체제가 이해할 수 있는 부분은 1과 0 밖에 모르는 하드웨어가 이해할 수 있도록 전달된다. 현재는 앞에서 설명한 큰 개념들로 논리를 사용한다고 말했지만 실제로는 수많은 논리의 층이 사이에 존재하고 해당 논리들을 사용하게 된다. 우리가 궁금했던 프로그래밍 언어는 이런 논리를 쌓는 과정이다. 수많은 논리를 쌓아서 실제 세계에 존재하는 것을 프로그래밍 언어로 표현하여 하드웨어의 세계와 연결해주는 것이 프로그래밍 언어를 배우는 우리가 할 일이다. 우리가 쌓아놓은 논리는 또 누군가에게 사용될 것이고 더 견고한 논리의 산이 더 복잡한 문제를 해결해줄 것이다.
논리를 쌓는 프로그래밍 언어를 지식을 쌓는 글을 쓰는 것에 비유하는 프로그래머들이 많다. 실제로 작가들이 글을 적는 행위도 실제 세계를 글이라는 도구로 표현하여 종이와 잉크의 세계에 연결시킨다. 작성된 책은 많은 사람들에게 공유되어 지식을 공유하게 된다. 프로그래밍 언어도 서로가 작성한 내용을 공유하고 같이 사용하기 때문에 굉장히 유사한 특성을 가지고 있다. 차이라면 프로그래밍 언어는 굉장히 까다로운 논리를 지키면서 작성을 해야 하고, 글은 논리적이지 않게 작성해도 된다는 부분이 차이라고 할 수 있다. (프로그래밍 언어는 논리를 안 지키면 동작을 안 한다.) 현재는 굉장히 논리적으로 발달한 프로그래밍 언어들이 많아서 프로그래밍 언어의 동작에 대해서는 자세히 이해하지 않아도 쉽게 프로그램을 프로그래밍 언어로 작성할 수 있다. 그러나 실력 있는 프로그래머가 되고 싶다면 (혹은 너무 궁금하다면) 현재 적고 있는 프로그래밍 언어가 다른 영역에 실제로 어떤 동작을 하고 어떤 영향을 미치는지에 대해서 이해를 해야 한다. 실제로 동작에 대한 이해를 하다 보면 프로그래밍 언어가 돌아가게 되는 영역에 대한 논리에 대해서 공부하게 되면서 다양한 문제를 사전에 예방할 수 있고 더 성능이 좋은 프로그램을 만들 수 있게 된다. 또한 배운 우수한 논리를 다시 프로그래밍 언어에서 적용하여 쉽게 우수한 프로그램을 만들 수도 있다.
논리들이 쌓였다는 부분이 이해가 힘든 사람도 있을 수 있어서 우리가 직관적으로 느낄 수 있는 예제를 들어 설명을 보충하도록 하겠다. 프로그래밍 언어가 실제로 실세계를 반영한 내용이어서 주변에서 예제들을 찾기 쉬우므로 다른 예제도 찾아보기 바란다. 여기서는 집 꾸미는 것을 예로 들도록 하겠다. 우리는 집을 꾸밀 때 의자, 책상, 티브이, 에어컨, 전등 등의 개념만 인식하고 해당 물건들을 어디에 놓을지만 생각하면 된다. 그런데 티브이나 에어컨을 만드는 사람을 보면 그 사람들은 티브이의 부품과 에어컨의 부품들로 티브이와 에어컨을 조립하는데만 신경 쓰면 된다. 에어컨과 티브이의 부품을 만드는 사람들은 그 부품을 구성할 수 있는 다른 작은 부품들을 고려해서 조립을 할 것이다. 정말 작은 부품들을 만드는 사람들은 가공된 철이라던지 가공된 재료들을 제공받아서 해당 재료들로만 규칙에 맞춰 구성할 것이다. 그리고 진짜 맨 아래로 가면 철을 가공하는 부분까지 내려가서 철을 가공하기 위한 규칙에 맞춰서 일하는 사람들이 있을 것이다. 이렇게 맨 위의 논리에 존재하는 우리들은 아래에서 무슨 일이 일어나는지 신경 쓸 필요 없이 현재 내가 생각하는 논리 규칙에 맞춰서 일을 하면 된다. 그 누구도 집을 꾸미는데 에어컨의 부품이 잘 만들어졌을지 부품을 뭐 쓰도록 할지까지 고민하지 않는다. 프로그래밍 언어도 마찬가지이다. 프로그래밍 언어를 돌리기 위해서 수많은 논리들이 돌아가지만 우리는 프로그래밍 언어에서 지켜야 할 규칙만을 인식하고 구현을 하게 되면 그 아래에 존재하는 수많은 논리들은 다 알아서 돌아가게 된다. 프로그래밍 언어가 새로운 시대의 변화 또는 혁신의 도구라고 생각할 수 있지만 프로그래밍 언어는 우리가 존재하는 세계를 컴퓨터의 관점에서 다시 표현한 도구일 뿐이니 너무 어렵게 생각하지 말자.