컴퓨터를 전공한다는 것 1편 - 컴퓨터 시스템

컴퓨터를 전공한다는 것 1편 - 컴퓨터 시스템

2019, Dec 28    

지난주 금요일, 정확히는 12월 20일 저녁 9시경, 이번 학기의 마지막 시험이 끝났다. 그리고 그 시험이 나의 마지막 전공필수 시험이었다. 물론 졸업을 위해서는 다음학기에 미뤄두었던 교양과목들을 수강하고 졸업연구도 해야 하지만, 어쨌든 최소한 “컴퓨터공학과”에서 수여하는 “컴퓨터공학 학사 학위”를 받기 위한 최소 기준은 충족한 것이다. 생각해보니 무슨 자기계발 과목을 들어야하긴 하는데..

그렇다면, 나는 컴퓨터공학과에 다니며 무엇을 배운 것일까? 그리고 어떤 능력을 가지게 되었을까? 궁극적으로, 컴퓨터을 전공한다는 것은 도대체 어떤 의미를 가질까? 이러한 의문을 품은지 사실 꽤 긴 시간이 지났지만, 전공필수 과목을 전부 수강한 지금에서야 그 해답이 조금이나마 보이는 것 같다. 그래서 이 질문에 대한 답을 명확히 하고, 또 이를 공유하고자 이 글을 시작했다. 컴퓨터를 공부하거나, 컴퓨터를 전공하고자 하는 많은 사람들에게 이 글이 좋은 지침서의 역할을 해 줄 것이라 믿는다.


컴퓨터를 전공하는 게 무엇인지 알 수 있는 가장 효과적인 방법은, 당연히 컴퓨터공학과에서 배우는 과목들을 살펴보는 것이다. 내가 재학중인 포항공대 컴퓨터공학과에서 16학번 기준으로 졸업을 위해 필수적으로 들어야 하는, 컴퓨터공학 / 과학과 관련된 과목들은 다음과 같다.

  • 1학년
    • 프로그래밍과 문제해결
    • 미적분학
    • 선형대수학
  • 2학년
    • 디지털시스템설계
    • 객체지향프로그래밍
    • 데이터구조
    • 이산수학
    • 확률과 통계
    • 컴퓨터 SW시스템 개론
  • 3학년
    • 컴퓨터 네트워크
    • 알고리즘
    • 컴퓨터구조
    • 프로그래밍언어론
    • 오토마타 & 형식언어
    • 운영체제
    • 소프트웨어 설계방법

뭐 졸업을 위해서는 여기에 전공 관련 선택과목들과, 타과 과목들, 교양을 추가로 수강해야 한다. 하지만 위 과목들이 필수로 지정된 과목들이니만큼 이들을 면밀히 분석하는 것만으로도 컴퓨터를 전공한다는 것이 무엇을 배우는 것인지 충분히 알 수 있을 것이다. 이 목표를 위해 먼저 위 과목들을 밀접하게 연관된 과목들끼리 묶어 분류해 보자.

  1. 컴퓨터 시스템 : 디지털시스템설계, 컴퓨터 SW시스템 개론, 컴퓨터 네트워크, 컴퓨터구조, 운영체제
  2. 프로그래밍, 개발방법론 : 프로그래밍과 문제해결, 객체지향프로그래밍, 소프트웨어설계방법
  3. 수학 : 미적분학, 선형대수학, 이산수학, 확률과 통계
  4. 계산이론 : 데이터구조, 알고리즘, 프로그래밍언어론, 오토마타 & 형식언어

좀 틀이 잡힌다. 이제 우리는 컴퓨터를 전공한다는 것이 위와 같은 4개의 대주제를 학습하는 것이라고 이야기할 수 있다. 좀 더 쉽게 설명하자면, 컴퓨터를 전공한다는 것은, 컴퓨터라는 기계장치가 어떻게 만들어졌고 작동하는지 이해하며 (컴퓨터 시스템), 이 기계를 잘 활용하기 위한 소프트웨어를 설계하고 개발하는 방법을 배우고 (프로그래밍과 개발방법론), 기초적인 수학 이론을 바탕으로 (수학) 어떤 문제를 컴퓨터로 풀 수 있는지, 만일 그렇다면 얼마나 효율적이고 빠르게 풀 수 있는지를 알게 되는 (계산이론)이라고 말할 수 있다. 그러나 아직 부족하다. 컴퓨터에 대해 잘 알고 있는 사람은 이러한 설명을 이해할 수 있겠지만, 그렇지 못한 사람에게는 너무나 불친절하고 간략화된 설명이다. 그렇다면 이제, 각각의 주제를, 분류된 과목들에 대한 설명들과 함께 하나하나씩 자세히 살펴볼 차례다.


컴퓨터 시스템

컴퓨터는 오직 0과 1만을 읽을 수 있다. 유튜브 동영상도 보여주고, 문서 작업도 할 수 있게 해주며, 바둑으로 인간 최고수를 꺾을 수도 있지만 결국 이 모든 것들이 0과 1의 나열과 그 연산으로만 이루어진다. 그렇다면 컴퓨터는 어떤 방식으로 이 신호를 읽고 해석하고, 또 계산하는 것일까? 컴퓨터과학자들과 공학자들은 이 단순한 2개의 신호만을 처리해 사용자가 쉽게 사용할 수 있는 컴퓨터 시스템을 설계하고, 구축했다. 그리고 당연히 컴퓨터를 전공한 사람이라면 이 시스템에 대해 잘 알고, 이해하고 있어야 할 것이다. 그렇기에 컴퓨터공학과에서는 0과 1을 처리하는 가장 기본적인 방법에서부터 시작해 컴퓨터 시스템에 전반에 대해 가르친다. 위에서 언급했던 커리큘럼을 따라가며 이 시스템을 간단하게나마 이해해 보자.

디지털 신호와 회로 - 디지털시스템설계

왜 컴퓨터는 0과 1만 읽을 수 있을까? 왜 0.5나 0.2 혹은 2는 불가능할까? 이 질문에 답하기 위해서는 먼저 아날로그 신호와 디지털 신호가 무엇인지에 대해 알아야 한다.

연속적인 아날로그 신호, 불연속적인 디지털 신호

컴퓨터는 전자기기다. 그렇기에 당연하게도 전기 신호를 받아들이는데, 만일 시간에 따라 신호의 전압이 변하는 정도를 측정한다면 그 신호는 연속적일 것이다. 그러나 전기 신호는 굉장히 불안정하기 때문에, 컴퓨터가 이를 받아들이고 해석하는 데 영향이 많다. 예를 들어, 누군가가 1만큼의 신호를 보냈을 때, 신호가 전달되는 과정에서 손실이 생겨 컴퓨터에게는 0.9로 받아들여질 수 있다. 반대로 외부 전기장의 영향으로 인해 1.1로 인식될 가능성도 존재한다. 만일 컴퓨터가 0.9, 1, 1.1을 모두 다른 신호로 인식한다고 가정해 보자. 컴퓨터는 누군가 계속 똑같은 신호를 보내도 이를 계속 다르다고 판단하게 될 것이고, 이는 시스템을 불안정하게 만든다. 컴퓨터공학자들은 이 문제를 해결하기 위해 비슷한 신호를 같다고 퉁쳐버리는 방법을 선택했다. 그리고 이렇게 변환된 신호를 디지털 신호라고 부른다.

위 그림에서는 아날로그 신호를 4단계로 이루어진 디지털 신호로 변환했지만, 실제로는 딱 두 단계로 이루어진 체계를 이용한다. 컴퓨터가 신호를 전류가 흐르고 있거나(1), 꺼져 있는(0) 상태로 구분하는 이 체계를 사용하기 떄문에, 컴퓨터는 0과 1만 읽을 수 있다고 이야기하는 것이다.

놀랍게도, 컴퓨터가 본격적으로 사용되기 70년 전쯤, 수학자 조지 불이 디지털 신호를 처리하는 회로를 구성하기 위한 기본 준비를 끝마쳐 놓았다. 그는 참과 거짓, 그리고 이들의 연산을 다루는 불 대수라는 체계를 완성했는데, 사실 그가 컴퓨터의 등장을 예견한 건 아니고, 논리학 연구의 산물이었다. 그런데 1930년 클로드 섀넌이 1을 참, 0을 거짓에 대응시킨다면 디지털 신호의 행동을 그대로 불 대수로 표현, 설명할 수 있다는 것을 알아냈다. 그는 이 사실을 기반으로 정보를 0과 1로만 이루어진 디지털 신호로 표현하고 처리하는 디지털 논리회로 이론을 설계해 발표했고, 이는 지금 사용하는 모든 디지털 시스템의 기반이 되었다. 여담으로, 이 위대한 논문은 섀넌이 고작 23살 때 쓴 석사 졸업논문이라고 한다…곧 24살이 되는 나는 고졸인데…

아무튼, 디지털시스템설계 과목에서는 이러한 논리회로 체계를 익히고, 이를 바탕으로 다양한 회로 - 숫자를 세는 계수기, 신호를 일시적으로 저장할 수 있는 회로, 사칙연산을 수행할 수 있는 계산기 등 - 들이 어떻게 설계되었는지 배운다. 그리고 우리가 쓰는 컴퓨터는 이런 간단한 회로들의 조합으로 이루어진다. 그렇다면 이제, 이 회로들을 어떻게 “조합”하고 “구성”할지 이야기해보자.

CPU, 그리고 하드웨어 - 컴퓨터구조

컴퓨터에 대해 잘 모르는 사람들도 CPU, 메모리, 하드디스크에 대해 들어본 적은 있을 것이다. 컴퓨터구조 과목에서는 이러한 하드웨어 디바이스들이 어떻게 설계되었는지, 또 어떻게 상호작용하는지 배운다.

CPU를 예로 들어 보자. CPU는 중앙처리장치(Central Processing Unit)의 약자로, 컴퓨터의 모든 계산을 수행하고, 데이터를 처리하며, 시스템을 통제하는, 마치 컴퓨터의 뇌와 같은 부품이다. 그러나 이 모든 업무를 수행하는 로직은 동일하다. 어떠한 프로그램이 실행될 때 CPU는 그 프로그램이 보내는 명령어를 읽고, 이를 해독해서 수행하고, 이 결과를 어딘가에 저장한다. 각 단계는 간단한 디지털 회로에 의해 수행되며, 결국 이들의 조합인 CPU 역시 복잡한 디지털 회로에 불과한 것이다. 그러나 컴퓨터구조를 배운다는 것은, 이 “다소 복잡한 회로”를 배우는 데 그 의미가 있지 않다. 어떻게 보면, 그런 회로는 위에서 이야기한 디지털시스템설계 과목에서 배운 내용으로도 충분히 이해하고 설계할 수 있다. 이 과목에서 중요한 내용은 그러한 회로들, 즉 하드웨어 장치들이 어떻게 서로 상호작용하고, 또 어떻게 장치들의 작동 속도를 극대화하는지에 대한 내용이다.

다시 CPU로 돌아가자. 방금 설명했던 “CPU가 작동하는 단계”를 정리하면 대충 다음과 같다.

  1. 명령어를 읽어들이다.
  2. 명령어를 해독한다.
  3. 명령어가 시킨 내용을 수행한다.
  4. 수행한 결과를 저장한다.

이때 각 단계가 1초씩 걸린다고 생각하자 (물론 실제 컴퓨터에서는 이것보다 훨씬 빠르다). 10개의 명령을 수행해야 한다고 했을 때, 먼저 읽은 명령어를 완전히 처리한 후에야 그 다음 명령어를 받아들일 수 있다면 총 40초가 걸릴 것이다. 그런데 만약 첫번째 명령어가 1단계를 끝내고 2단계로 넘어갔을 때 그 다음 명령어의 1단계를 시작할 수 있다면 어떻게 될까? 이 경우 1번 명령어의 4단계 수행이 끝나자마자 2번 명령어가 4번 단계를 수행할 것이고, 이런 식으로 쭉 이어진 끝에 13초 만에 모든 명령어가 처리될 것이다. 쉽게 말해, 각 단계에 쓰이는 회로를 절대 쉬지 않게 해 전체 회로의 효율성을 올리는 것이다.

위에서 설명한 방식을 파이프라이닝(Pipelining)이라고 하는데, CPU의 속도를 극적으로 향상시킨 기술 중 하나다. 그런데, 얼핏 보면 완벽해 보이는 이 방식에도 문제가 있다. 만일 1번 명령어에서 계산해 저장한 값을 2번 명령어가 써야 한다면? 이 경우 1번 명령어의 4단계가 끝날 때까지 2번 명령어가 3단계에 돌입할 수 없게 된다. 이뿐만 아니라 수행한 결과를 저장하는 저장장소, 계산의 종류 등 CPU에 영향을 미치는 다양한 변수가 있고, 현재 우리의 컴퓨터, 스마트폰에 쓰이는 CPU는 이 모든 것들을 고려해서 최대한의 속도를 뽑아낸 결과물이다. 그리고, 컴퓨터구조를 안다는 것은 컴퓨터 하드웨어와 관련된 이 모든 맥락을 이해하는 것이라 할 수 있다.

이제 계산을 빠르게 할 수 있는 하드웨어 장치를 이해했으니, 그 위에서 소프트웨어를 돌릴 차례다. 다음 단계로 넘어가자.

운영체제

컴퓨터를 써봤다면 Windows, macOS와 같은 친숙한 운영체제를 당연히 사용해 봤을 것이다. 그런데, 운영체제는 도대체 왜 필요할까? 만일 컴퓨터에서 한 개의 프로그램만 실행시킨다면 사실 운영체제가 필요없다. 그냥 컴퓨터에 프로그램을 구성하는 명령어를 입력하고, 컴퓨터는 그 명령어들을 읽어들여 실행하면 될 뿐이다. 실제로 초기 컴퓨터들은 이런 식으로 사용하도록 설계되었다. 그런데, 우리가 컴퓨터를 사용할 때는 인터넷 브라우저부터 시작해 탐색기, 메신저, 사진 뷰어, 오피스 프로그램 등등 수십 개의 프로그램을 동시에 구동하는 일이 흔하다. 문제는 컴퓨터가 제공하는 자원이 한정되어 있다는 것이다. 예를 들어, 요즘 컴퓨터는 8~16개의 CPU를 가지고 있는데, 각각의 CPU는 한번에 한 개의 프로그램만 실행시킬 수 있다. 그렇다면 컴퓨터 시스템은 어떻게 사용자로 하여금 수십 개의 프로그램을 사용할 수 있게 하는 것일까?

사실 답은 “사용할 수 있게” 해 주는 것이 아니라, “사용하는 것처럼 느껴지게” 만드는 것이다. 그리고 이 역할을 담당하는 것이 바로 운영체제다. 운영체제는 현재 실행중인 여러 개의 프로그램을 관리하면서, 각 CPU에 이들을 실행시켜 달라고 이야기한다. 이 때 각 프로그램에 대한 실행 요청을 굉장히 빠르게 전환하는 것이 “사용하는 것처럼 느껴지게” 만드는 것이 핵심이다. 예를 들어 컴퓨터에 단 한 개의 CPU가 있는데, 실행해야 할 프로그램은 10개라고 가정해 보자. 이 경우 운영체제는 CPU가 10개의 프로그램을 매우 짧은 시간 동안 실행하도록 요청을 보낸다. 쉽게 말해, 처음 0.001초는 첫번째 프로그램, 그 다음 0.001초는 두번째 프로그램…이런 식으로 각 프로그램을 실행시키는 것이다. 이 방법으로 특정 프로그램과 관련된 명령이 최소 0.01초마다 CPU에서 실행될 수 있고, 사람의 느린 반응 속도로 인해 이것이 마치 쉬지 않고 작동하는 것처럼 느껴지는 것이다.

운영체제는 이처럼 컴퓨터가 제공하는 유한한 자원을, 그 위에서 돌아가는 소프트웨어들이 쉽게 사용할 수 있도록 분배하고 관리한다. 뭐, 또 다른 예를 들자면, 운영체제는 하드디스크를 효율적으로 사용하기 위해 파일 시스템을 구축하고 관리하는 역할도 맡는다. 정리하자면, 운영체제는 컴퓨터 하드웨어에서 제공하는 기능 - 이를테면 정보를 저장하거나, 연산을 수행하는 것 - 들을 사용자가 사용하기 편한 방식으로 감싸서 제공한다. 그리고 사용자는 이 시스템 위에서 쉽게 자신이 원하는 프로그램을 사용하고 파일을 관리할 수 있게 된다.

시스템과 프로그래밍 - 컴퓨터 SW시스템 개론

만일 당신이 컴퓨터공학과, 또는 컴퓨터과학과를 졸업하고 하드웨어나 운영체제를 연구하는 학자가 되고 싶다면, 당연히 위에서 언급한 과목들이 가르치는 내용들을 모두 알아야 한다. 그런데 그저 “개발자”가 되고 싶을 뿐이라면? 그냥 운영체제 위에서 돌아가는 프로그램을 개발하고 싶을 뿐인데, 그래도 컴퓨터 시스템을 알아야 할까? 당연히 그렇다. 어떤 프로그램을 만들든, 컴퓨터 시스템을 이해하고 있으면 더 효율적인 방식으로 더 빠른 로직을 만들 수 있다. 그리고 이렇게 컴퓨터 시스템을 프로그래밍에 어떻게 접목시키는지를 배우는 과목이 바로 컴퓨터 SW시스템 개론이다.

이 과목에서는 랜달 브라이언트가 쓴 Computer System : A Programmers’s Perspective (csapp) 이라는 책과, 이 책과 연계되어 구성된 카네기멜론대학의 교육과정을 사용하는데, 세계 어디를 가도 웬만한 대학에서는 이 교육과정을 사용한 수업이 존재한다. 그만큼 유명하고 잘 짜여진 교육과정인데, 서울대와 카이스트에서도 같은 교육과정을 시스템 프로그래밍이라는 이름으로 운영한다.

그러면 어떻게 컴퓨터 시스템에 대한 지식을 프로그래밍에 접목시킬 수 있을까? 대표적인 예를 하나 들어 보자. 컴퓨터는 “캐시”라는 하드웨어 장치를 가지고 있다. 복잡한 로직은 제외하고 결론만 말하자면 이 장치를 통해 컴퓨터는 최근에 접근한 데이터에 다시 접근하거나, 그 주변의 데이터에 접근했을 때 더 빠르게 데이터를 가져올 수 있다. 이 사실을 알고 있다면, 프로그래밍을 할 때 데이터를 여기저기 저장하지 않고 한 곳에 잘 모아서 저장하는 방식으로 프로그램의 속도를 빠르게 할 수 있을 것이다.

기계어를 알고 있는 것도 효율적인 프로그래밍에 도움이 된다. 흔히 프로그래밍을 할 때는 사람이 읽기 쉽게 디자인된 프로그래밍 언어를 사용한다. 뭐 이를 테면


if(answer == 1) 
    printf("정답입니다!");
else
    printf("오답입니다!");

이러한 코드를 작성하는 것이다. 프로그래밍에 대해 전혀 모르는 사람이라도 이 코드가 answer가 1이면 정답이라고, 1이 아니면 오답이라고 출력해주는 프로그램임을 쉽게 알 수 있다. 그러나 컴퓨터는 “사람이 읽기 쉬운” 이런 코드를 바로 읽을 수 없고, 이 때문에 사람이 작성한 코드를 컴퓨터가 읽을 수 있는 기계어로 번역해주는 과정이 필요하다. 그런데 가끔은 번역 과정에서 최적화되지 않은, 비효율적인 방식으로 연산하는 기계어 코드가 생성될 수 있고, 이는 프로그램의 성능을 저하시킨다. 다행히도 몇몇 프로그래밍언어에서는 중간에 기계어 코드를 끼워넣을 수 있는 툴을 제공한다. 만일 기계어에 대한 지식이 있다면, 비효율적으로 번역되는 부분만 직접 기계어로 코드를 써서 프로그램의 성능을 높일 수 있다.

이 과목에서는 이처럼 컴퓨터 시스템에 대한 지식을 프로그래밍에 활용하는 법도 배우지만, 시스템의 취약점을 찾아서 보안을 뚫는 방법, 하드웨어에 종류에 따라 달라지는 최적화 기법 등 다양한 주제에 대해 가르쳐 준다. 이를 배우며 학생은 컴퓨터 시스템을 전반적으로 이해하고 이를 프로그래밍에 적용할 수 있는 능력을 가지게 된다.

인터넷과 네트워크 - 컴퓨터네트워크

당신은 컴퓨터 하드웨어, 운영체제가 무엇인지 이해하고, 이를 프로그래밍에 적용하는 방법도 배웠다. 그런데, 아직 겨우 컴퓨터 한 대에 대해서만 알고 있을 뿐이다. 이제 컴퓨터 여러 대를 동시에 사용할 수 있게 해주는 네트워크에 대해 알아 볼 차례다.

사실 누군가 컴퓨터를 사용한다고 했을 때, 정말 온전히 컴퓨터 한 대가 제공하는 기능만을 사용하는 경우는 거의 없다. 간단하게, 포털사이트에 접속하는 경우를 살펴보자. 사용자는 인터넷 브라우저를 열어 url을 치고 엔터만 누르면 포털 사이트 화면을 볼 수 있지만, 화면 너머에서는 다음과 같은 일련의 과정이 이루어지고 있다.

  1. DNS 서버 컴퓨터에 접속한다.
  2. 입력한 도메인에 대한 IP 주소를 받아온다.
  3. 받아온 IP주소로 포털 사이트 서버에 접속한다.
  4. 포털 사이트 서버가 보낸 페이지 정보를 받아와 브라우저에 띄운다.

부연설명을 하자면, DNS는 Domain name service의 약자로, 우리가 naver.com, daum.net, jeonhyun97.github.io와 같은 알아보기 쉬운 도메인 이름을 서버의 주소를 나타내는 IP 주소(141.223.22.22와 같은) 로 변환해준다. 네이버에 접속할려고 숫자로 이루어진 서버 주소를 쓰는 것은 굉장히 불편하기 때문에 이와 같은 서비스가 존재하는 것이다.

아무튼, 이 단순한 작업에도 무려 3대(DNS 서버 컴퓨터, 포털 서버 컴퓨터, 내 컴퓨터)의 컴퓨터가 협업한다. 이처럼, 인터넷을 통해 다른 컴퓨터에 접속하고, 정보를 주고받는 것은 이제 너무나 자연스러운 일이다.

네트워크는 또한 소위 말하는 “4차 산업 혁명” 시대를 이끄는 핵심적인 기술이기도 하다. 인공지능, 빅데이터 등의 분야에서 우리가 다뤄야 하는 데이터의 양이 너무나 커지면서, 여러 대의 컴퓨터를 네트워크를 통해 연결해 동시에 사용해야지만 이들을 처리할 수 있게 되었다. 이를 분산 컴퓨팅(Distributed Computing)이라고 하는데, 우리가 흔히 쓰는 클라우드 서비스 역시 분산 컴퓨팅의 일종이고, 비트코인으로 한창 화제가 된 블록체인도 분산 컴퓨팅 기술을 기반으로 만들어졌다.

이렇듯, 현대 컴퓨터 시스템과 네트워크, 인터넷은 서로 떼려야 뗄 수 없는 관계에 위치해 있고, 컴퓨터 시스템을 이해하는 데 있어 네트워크를 필수적으로 배워야 함은 너무나 당연한 일이다.


이렇게 컴퓨터를 전공한다면 배우게 될 컴퓨터 시스템 관련 과목들과, 가르치는 내용들을 간략하게 적어 봤다. 컴퓨터 시스템을 이해하고 알고 있는 것은 중요하다. 시스템을 이해함으로써 더 좋은 코드, 효율적인 프로그램을 작성할 수 있고, 또 이를 기반으로 새로운 시스템을 설계할 수도 있을 것이다. 그런데, 이 “시스템”이 어떤 이론적 기반 위에서 만들어진 것일까? 또 이렇게 잘 만들어 놓은 시스템을 어떻게 사용해야 효율적일까? 이에 대한 의문을 다음 포스팅에서 해결해 보자.

관련 포스팅

컴퓨터를 전공한다는 것 2편 - 프로그래밍과 개발방법론

컴퓨터를 전공한다는 것 3편 - 계산이론