10 스트림의 기원 — 펀치카드, 라인프린터, 프린터 터미널
먼저 알아야 할 것
돌아보기
9장에서 제어 문자 — “종이를 한 줄 올려라” 같은, 장치에 내리는 지시 — 가 문자표의 한 구석에 산다고 했다. 글자들의 표에 왜 장치 조작 지시가 끼어 있는가?
답. 그 표가 만들어진 시대에는 텍스트가 곧 장치의 움직임이었기 때문이다. 글자는 화면에 그려지는 것이 아니라 종이에 물리적으로 찍히는 것이었고, “다음 줄로”는 실제로 기계 부품이 움직이는 동작이었다. 그 시대의 장치들을 구경하면, 오늘의 입출력이 왜 이런 모양인지 전부 풀린다.
이 장의 필요성과 맥락
printf 가 어디에 대고 말하는지, \n 이라는 두 글자가 왜 그 모양인지가 여기서 풀린다. 종이의 시대를 먼저 보아야 스트림이 「C 의 설계」가 아니라 「물려받은 것」임을 안다.이 장이 끝나면
\n이라는 두 글자와 tty라는 이상한 이름의 유래도 여기서 풀린다. 이 장이 끝나면 곧 만날 첫 프로그램의 printf가 어디에 대고 말하는 것인지 미리 이해하게 된다.이 장에서 답할 질문
- 카드 뭉치를 떨어뜨리면 어떻게 되는가?
- 요즘 프로그램은 창과 버튼으로 소통하는데, 스트림은 옛날이야기 아닌가?
10.1 펀치카드 — 한 장이 한 줄이다
초기 컴퓨팅의 입력은 펀치카드였다. 뻣뻣한 종이 카드에 구멍을 뚫어 글자를 기록한다 — 구멍의 조합이 글자 하나이고, 카드 한 장에 글자가 80칸 들어간다. 프로그램 한 줄을 카드 한 장에 뚫고, 프로그램 전체는 카드 뭉치가 된다. 뭉치를 기계에 먹이면 카드가 한 장씩 빨려 들어가며 읽힌다.
여기서 두 가지 유산이 태어났다. 첫째, “한 장 = 한 줄”이라는 감각. 텍스트는 줄들의 열이라는, 지금은 공기처럼 당연한 관념이 물리적 실물에서 왔다. 둘째, 80이라는 수. 카드의 80칸은 이후 터미널 화면의 80컬럼으로, “코드 한 줄은 80자 안쪽으로”라는 오늘날의 코딩 관행으로 반세기를 건너 살아남았다.
문. 카드 뭉치를 떨어뜨리면 어떻게 되는가?
답. 대참사다 — 순서가 섞인 카드 수백 장이 곧 프로그램이 뒤섞인 것이다. 그래서 카드 구석에 일련번호를 뚫어 두고 기계로 다시 정렬하는 요령이 있었다. 카드 한 귀퉁이를 사선으로 잘라 뒤집힌 카드를 눈으로 찾게 한 것도 같은 지혜다. 우스개 같지만, “데이터에 순서 번호를 붙여 두면 섞여도 복구할 수 있다”는 착상은 지금도 통신과 데이터베이스 설계의 기본기다.
10.2 라인프린터 — 줄 단위로 찍는 출력
출력 쪽의 실물은 라인프린터였다. 이름 그대로 한 번에 한 줄을 통째로 종이에 찍는 인쇄기다. 계산 결과는 화면이 아니라 연속 용지에 줄줄이 찍혀 나왔다. 입력은 카드 한 장씩(= 한 줄씩), 출력은 프린터 한 줄씩 — 컴퓨터의 입출력은 태생부터 줄 단위의 흐름이었다.
10.3 프린터 터미널 — 종이 위의 대화, 그리고 tty
시분할의 시대가 오며(여러 사람이 한 컴퓨터를 동시에 쓰는 방식) 사람과 컴퓨터가 대화할 장치가 필요해졌다. 그 자리에 들어온 것이 전신 타자기 — 텔레타이프(Teletype)다. 타자기처럼 생긴 이 기계로 글쇠를 치면 그 글자가 컴퓨터로 가고, 컴퓨터의 응답이 같은 기계의 종이에 찍힌다. 화면은 없다. 대화 전체가 종이 두루마리에 인쇄된다.
Unix가 바로 이 장치들 곁에서 태어났고(4장), 단말 장치를 가리키는 이름을 텔레타이프의 약자에서 따 왔다 — tty. 오늘날의 터미널 창 밑바닥에 여전히 살아 있는 이름이다.
타자기의 몸에서 온 유산이 또 있다. 타자기에서 줄을 바꾸는 동작은 사실 두 개다 — 활자 뭉치(캐리지)를 왼쪽 끝으로 되돌리는 동작과, 종이를 한 줄 올리는 동작. 문자표는 이 두 동작에 각각 제어 문자를 배정했다. 캐리지 리턴(CR, C 표기 \r)과 라인 피드(LF, \n)다. 줄 하나를 바꾸는 데 지시 두 개가 필요했던 것은 기계 부품이 실제로 둘 움직였기 때문이다.
실제 사례. CRLF — 타자기가 남긴 백 년짜리 숙제
물리적 이유가 사라진 뒤에도 두 문자는 남았고, 세계는 줄바꿈 표기를 통일하지 못했다. Windows 계열은 두 동작을 다 적는 \r\n(CRLF)을, Unix 계열은 \n 하나를 줄의 끝으로 삼았다. 그 결과가 오늘날의 일상적 사고들이다 — 한쪽에서 만든 텍스트 파일을 다른 쪽에서 열면 줄 끝에 유령 문자가 붙거나 온 파일이 한 줄로 보이고, 버전 관리 도구(git)는 “CRLF를 LF로 바꿀 것”이라는 경고를 낸다. 타자기 부품의 움직임이, 부품이 사라진 지 반세기 뒤의 협업 현장에서 아직 경고문을 띄우고 있는 셈이다.
그리고 이 두 문자는 보안에도 얼굴을 내민다. 인터넷 규약들(HTTP, 전자우편)은 줄바꿈으로 항목을 나누는 텍스트 규약이라서 — 사용자가 준 값에 CR·LF가 섞여 들어가면 공격자가 새 항목을 만들어 끼워 넣을 수 있다. 응답 헤더에 남의 값을 그대로 넣었다가 헤더가 통째로 조작되거나(HTTP 응답 분할), 로그 한 줄에 남의 값을 넣었다가 가짜 로그 줄이 위조되는 식이다(로그 위조). 9장의 후렴구가 여기서도 반복된다 — 데이터를 틀에 섞지 말 것, 그리고 경계에서 제어 문자를 걸러 낼 것.
10.4 화면 터미널, 그리고 스트림이라는 유산
이윽고 종이가 화면으로 바뀌었다 — 그러나 새 화면 장치들은 프린터 터미널을 흉내 내는 방식으로 만들어졌다. 글자가 아래에서 위로 밀려 올라가는 오늘의 터미널 창은, 종이가 위로 밀려 올라가던 텔레타이프의 유리판 버전이다. 겉모습만 바뀌고 대화의 문법은 그대로 남은 것이다.
이 물리적 계보 전체가 하나의 관념으로 응축됐다. 입출력이란 문자가 한 줄씩, 순서대로 흘러가는 띠라는 관념 — 이것이 스트림(stream)이다. 카드가 한 장씩 빨려 들어가듯 입력이 흘러 들어오고, 프린터가 한 줄씩 찍듯 출력이 흘러 나간다. C는 이 관념을 언어의 입출력 모델로 그대로 받아들였다. 프로그램마다 기본으로 주어지는 표준 입력과 표준 출력(standard output) 이라는 두 흐름이 그것이고, 이 책의 다음 부에서 만날 printf는 바로 표준 출력이라는 띠에 글자를 흘려보내는 함수다.
10.5 띠를 갈아 끼우다 — 리다이렉션과 파이프라인
스트림이라는 관념에는 뜻밖의 힘이 하나 숨어 있었다. 프로그램이 자기 출력의 끝이 어디인지 모른다면 — 그 끝을 바깥에서 갈아 끼울 수 있다는 뜻이다.
Unix가 이 착상을 제도로 만들었다. 프로그램은 표준 입력·표준 출력이라는 띠에 대고 읽고 쓸 뿐이고, 그 띠의 반대편을 무엇에 연결할지는 프로그램을 실행하는 쪽이 정한다. 터미널에서 명령 뒤에 > 파일.txt라고 적으면 출력의 띠가 화면 대신 파일로 이어지고(리다이렉션), < 입력.txt라고 적으면 입력의 띠가 키보드 대신 파일에서 흘러온다. 프로그램의 코드는 한 글자도 바뀌지 않는다.
여기서 한 걸음 더 나간 것이 파이프라인(pipeline)이다. 어떤 프로그램의 출력 띠를 다른 프로그램의 입력 띠에 곧장 잇는 것 — 터미널의 | 기호가 그 연결이다.
$ 목록내기 | 걸러내기 | 세기세 프로그램이 각각 자기 띠만 보고 일하는데, 셋을 이어 붙이면 하나의 새 도구가 된다. 이 연결을 발명한 더글러스 매클로이(Doug McIlroy)와 Unix 사람들이 남긴 원칙이 이후 소프트웨어 설계의 격언이 됐다 — “한 가지 일을 잘하는 작은 프로그램을 만들고, 텍스트 스트림으로 협력하게 하라.” 프로그램들을 부품으로 쓰는 조립의 문법이, 겨우 이 “띠를 갈아 끼우는” 단순함에서 나온 것이다.
이 자유의 대가로 프로그래머가 지켜야 할 것도 생긴다. 내 프로그램의 출력이 언제나 사람의 화면으로 간다고 가정하면 안 된다는 것 — 장식용 글자나 진행 표시가 뒤 프로그램의 입력을 오염시킬 수 있다. 그래서 Unix 전통은 통로를 하나 더 둔다: 진짜 결과는 표준 출력으로, 사람에게 하는 말(경고·오류)은 표준 오류라는 별도의 띠로 보내는 것이다. 세 번째 띠의 존재 이유가 이것이다.
실제 사례. 이 책의 실행 결과도 리다이렉션의 산물이다
추상적인 이야기가 아니다 — 지금 읽고 있는 이 책이 그 증거다. 이 책의 모든 코드 시연은 기계가 예제를 실행해 얻은 진짜 출력인데, 그 출력을 사람이 받아 적은 것이 아니라 출력의 띠를 파일로 돌려 (리다이렉션) 조판 도구가 그 파일을 읽어 인쇄한 것이다. 입력이 필요한 예제도 마찬가지로 파일을 표준 입력의 띠에 이어 준다 — 그래서 키보드를 두드리는 사람이 없어도 예제가 재현된다. 25장에서 “입력은 키보드에서 온다”는 오개념을 깰 때, 그 산 증거가 바로 이 책의 제작 과정이다.문. 요즘 프로그램은 창과 버튼으로 소통하는데, 스트림은 옛날이야기 아닌가?
답. 화면의 앞쪽만 보면 그렇게 보이지만, 뒤쪽에서는 스트림이 오히려 전성기다. 서버 프로그램들의 기록(로그)은 스트림이고, 프로그램과 프로그램을 이어 붙이는 파이프라인도, 인공지능 챗봇이 답을 한 글자씩 “흘려보내는” 것도 스트림이다. 무엇보다 프로그래밍을 배우는 자리에서는 스트림이 가장 단순하고 정직한 소통로다 — 이 책의 예제가 전부 스트림 위에서 도는 이유다.
표현의 사다리가 완성됐다. 사물함에 수를 담는 법(8장), 글자를 담는 법(9장), 그리고 글자를 흘려보내는 법(10장)까지 — 이제 기계에 무엇이든 담고 내보낼 수 있다.
다음 장부터는 이 부의 마지막 등반이다. 지금까지의 단순한 기계 그림이 사실 얼마나 과감한 거짓말이었는지 — 기억이 갈라지고(11장), 실행이 겹쳐지고(12장), 컴파일러가 끼어들면서(13장), C가 왜 “추상적인 언어”일 수밖에 없는지(14장)의 이야기가 남았다.