"Stray Pointers"에서 Forth, C, CGI, Python, Go, AWK를 이야기하다
원문은 Ben Hoyt님이 에 게재했습니다. 이 블로그 구독하기
이 글은 다채롭고 흥미로운 팟캐스트 Stray Pointers의 진행자 짐 로리스와 나눈 대화를 가볍게 다듬고 추임새인 음을 덜어낸 전사본이다. 녹음은 1월 15일(미국 시각)에 이루어졌다. 우리는 Forth, C, CGI, Python, Go, AWK에 대해 이야기했다.
에피소드 웹 페이지나 YouTube에서 들을 수 있다.
Jim: 오늘 밤에는 엔지니어링 매니저이자 소프트웨어 엔지니어인 벤 호이트 님을 모셨습니다. 벤 님, 방송에 오신 것을 환영합니다!
Ben: 초대해 주셔서 정말 감사합니다.
Jim: 벤 님, 『The AWK Programming Language 책의 신판』의 기술 편집자 혹은 기술 감수자로 이름을 보게 되면서 업적을 눈여겨봤고, 거기서부터 찾아보니 프로그래밍 언어와 관련해 꽤 흥미로운 이력을 갖고 계시더군요. 그래서 프로그래밍을 어떻게 시작하셨는지, 처음에는 어떤 언어를 쓰셨는지 등 프로그래밍 배경에 대해 이야기 나눠보고 싶습니다.
Ben: 네, 이야기가 꽤 오래전으로 거슬러 올라갑니다. 80년대 후반에서 90년대 초반, 어릴 때 시작했어요. 기억나는 첫 컴퓨터는 Sega SC-3000 콘솔이었는데, 키보드가 일체형으로 달린 작은 컴퓨터였고 BASIC이 깔려 있었습니다. 아버지께서는 지금도 계시지만 은퇴하셨고, 본업은 목사님이셨지만 밤에는 취미로 프로그래밍을 하시는, Forth 프로그래머셨죠.
아버지는 그 컴퓨터로 이것저것 만져보시면서 Forth를 올려 돌리기도 하고 여러 가지를 해보셨고, 그래서 저도 자연스레 그런 것들에 노출됐습니다. 그때는 대부분 그걸로 그냥 게임이나 팩맨 같은 걸 하며 놀았지만, 집에 컴퓨터 잡지가 굴러다니곤 했는데 뒤쪽에 말도 안 되는 간단한 BASIC 게임 코드가 실려 있더군요. 잡지 뒤에서 그걸 전부 직접 입력해야 했죠. 몇 번 따라 쳐 봤는데, 하나만 틀려도 전체가 그냥 작동을 안 하더라고요. 그래도 그런 경험과 아버지의 관심이 저를 이쪽으로 이끈 것 같습니다.
Jim: 특히 Forth에 대한 관심이 궁금합니다. 아직 십 대였을 때 한스 베제머와 함께 Forth Dimensions 잡지에 글을 쓰셨다고요, 맞나요?
Ben: 네, 맞습니다. 아마 그게 제가 처음으로 발표한 기술 글이었을 거예요. 그 뒤로 기술 글쓰기를 꽤 즐기게 됐고 블로그도 운영하고 있지만, 그때는 좀 성급했죠. 제대로 된 동료 검토를 거친 것도 아니었고, 뭐 그 나름으로는 괜찮았지만요. 그 글은 Forth에서 룩업 테이블이 얼마나 훌륭한지에 대한 내용이었고, 그와 함께 공동으로 썼습니다.
Jim: 한스 분과는 어떻게 알게 되셨나요? 그분은 지금 Forth 커뮤니티에서 숫자 4와 T-H를 쓰는 4tH 컴파일러로 꽤 유명하신 분이잖아요.
Ben: 네, 맞아요. 그때도 이미 그랬던 것 같고, 오래 활동하신 분이죠. 그 뒤로는 사실상 연락한 적이 없는데, 아주 예전에 comp.lang.forth나 뉴스그룹 중 하나에서 어떻게든 연결이 됐어요. 제가 룩업 테이블 관련 작업을 하고 있는데 정말 최고라고 생각한다고 하니, 그분도 좋다고 하시면서 Forth Dimensions에 같이 글을 써보자고 하시더군요. 누가 먼저 제안했는지는 기억이 안 나지만 아마 그분이셨을 겁니다. 함께 쓰자고 하셔서요.
좋은 기회였죠. 그래서 코드를 작성하고 다른 기법보다 얼마나 빠른지 나름의 결과를 정리했습니다. 다만 좀 무리하게 해석한 면이 있어요. 손가락을 제대로 모으고 웃는 각도까지 맞추면 더 빠른 것처럼 보이지만, 사실 대부분은 그렇지 않았거든요. 그런데도 그렇게 밀고 나갔죠. 그래서 좀 성급했다고 말씀드린 겁니다. 그때는 그게 반짝이는 신기한 것이었고, 룩업 테이블을 꼭 쓰고 싶었어요. 그러다 보니 그게 만능 망치가 되어 모든 문제의 답처럼 여겨졌죠.
Jim: 그렇군요. 그럼 진정한 Forther라면 으레 그렇듯 직접 Forth를 구현하셨겠네요. 그 얘기 좀 들려주실 수 있을까요?
Ben: 네. 아버지가 Forth에 깊이 빠져 계셔서 직접 Forth 컴파일러를 하나, 혹은 여러 개 만드셨거든요. 그래서 저도 본격적으로 프로그래밍에 빠져들면서 처음 배운 두 언어가 x86 어셈블리와 Forth였습니다. 그리고 DOS 시절에 8086 어셈블리로 제 Forth를 직접 만들었죠. Forth의 가장 큰 장점은 아주 작다는 겁니다. 완전히 스스로를 부트스트랩하는 컴파일러를 코드 몇천 줄 정도로 쓸 수 있어요. 어셈블리로 작성된 작은 커널이 있는데, 이를 프리미티브라고 부르며 Forth 커널을 구성하는 기본 연산들입니다. 그리고 그 위의 나머지는 Forth 자체로 작성합니다. 제 첫 버전은 아마 A86 어셈블러라는 걸로 만들었는데, 아시면 아시겠지만 꽤 괜찮은 도구였습니다.
Jim: 네, 당시에 꽤 인기 있던 셰어웨어 어셈블러였죠.
Ben: 맞아요. 십 대였고 돈이 없어서 무료 버전만 썼던 것 같아요. 그래도 정말 좋은 어셈블러였죠. 그래서 제 Forth 첫 버전은 A86으로 커널을 만들고 그 위에 Forth 코드를 얹는 방식이었습니다. 몇 버전을 더 거치면서 Forth 커뮤니티에서 메타 컴파일이라고 부르는 걸 깨우쳤어요. Forth로 Forth를 빌드하는, 즉 Forth 컴파일러를 Forth로 만드는 방식이죠. 그러려면 Forth로 작성된 어셈블러가 거의 필수인데, 인터넷에서 하나를 가져와 살짝 손보고, 이해하려고 거의 다시 타이핑하다시피 하며 고쳐 썼던 기억이 납니다. 그 시절에는 그렇게 배우는 경우가 많았어요.
Jim: 빌 래그스데일이 80년대 초 Dr Dobbs에 발표한 6502용 유명 어셈블러가 Forth로 작성된 게 있는데, 그걸 모델로 하신 건가요, 아니면 x86에 맞는 다른 것이 있었나요?
Ben: 아니요, x86 전용 어셈블러가 따로 있었습니다. 이름은 기억이 안 나네요. 코드 주석에 있는지 한번 볼까요… 보기에는 출처를 주석에 남기지도 않은 것 같습니다. 어쨌든 x86 어셈블러 기반이었고, 어떤 것이었는지는 기억이 안 납니다.
Jim: 알겠습니다. 그렇게 Forth 시스템을 만들고 나서는 Forth로 무엇을 만드셨나요? 어떤 소프트웨어를 작성하셨는지요?
Ben: Forth의 묘한 점이 바로 그거예요. Forth로는 소프트웨어를 만드는 게 아니라 Forth 컴파일러를 만든다는 거죠. [웃음] 뭘 만들었냐고요? 조그만 게임이나 그래픽 관련 것들이었습니다. Forth를 만지작거리던 시기와 겹쳐 데모씬에도 빠져 있었는데, 모르시는 청취자를 위해 설명하자면 직접 그래픽 데모나 음악 데모 같은 걸 만들어 뽐내는 프로그래머들의 하위문화 같은 겁니다. 4K 데모처럼 멋진 그래픽을 4K 안에 얼마나 담을 수 있는지 겨루는 대회도 있었죠. 뉴질랜드에서 BBS 등을 통해 그 씬에 살짝 발을 담갔고, BBS에서 많은 걸 배웠습니다. 그래픽이나 게임 쪽이었고, 그런 것들을 Forth로 조금 해봤죠.
그리고 몇 년 뒤에 Forth로 한 또 다른 작업은 386용 32비트 운영체제를 직접 만든 것이었습니다. Forth OS였죠. 아주 가벼운, OS라고 하면 극도로 미니멀한 키보드 드라이버와 디스크 입출력, 화면 드라이버 정도만 있는 수준이었습니다.
Forth는 제 사고방식을 정말 많이 형성했고 지금도 그럴 겁니다. 이제는 쓰지 않지만 여전히 사고에 영향을 줘요. Lisp처럼 머리를 뒤흔들고 철학적으로 정말 멋진 언어 중 하나죠. 그래서 저는 언제나 작고 빠르고 가벼워서 커널까지 위에서 아래까지 전부 이해할 수 있는 것들을 좋아하게 됐습니다.
그러다 데모씬 활동의 일환으로 Turbo Pascal을 접했는데, 씬 사람들이 많이 쓰던 언어였습니다. C도 조금 만져봤지만 그때는 늘 좀 난해하게 느껴졌어요. 아마 그 무렵 고등학교를 마치고 대학에 가서 전기공학을 전공했습니다. 전기공학과 과정에 좀 웃긴, 약간 이상한 제도가 있었는데, 3년 과정 초반에 프로그래밍 과목을 하나 들을 수 있고, 그 과목에서 — C로 하는 작은 수업이자 테스트인데 C 코드를 쓰고 질문에 답하는 — 테스트를 통과하면 나머지 과정 동안 다른 프로그래밍 수업은 전혀 듣지 않아도 되는 거였어요. 저는 그걸 통과했고, 그게 실수였습니다. 프로그래밍 자체는 할 줄 알았지만 소프트웨어 엔지니어링이나 더 큰 그림에 대해서는 몰랐거든요. 그래서 진짜로 C를 제대로 익히고 더 큰 프로그램을 구조화하는 법, 소프트웨어 엔지니어링을 배운 건 대학을 졸업하고 직장에서 일하면서였습니다. 일하면서 배운 셈이죠. C가 제 첫 번째 진짜 언어였습니다.
Jim: 그때는 전기 엔지니어로 일하신 건가요?
Ben: 전기공학을 전공했지만 첫 직장에서 자연스레 코딩 쪽으로 기울었고, 마침 그쪽 인력이 필요하던 때라 그 이후로 쭉 그 길로 왔습니다. 전기 쪽은 거의 손을 놓게 됐죠. 네, 학위 전공을 활용하지는 않았습니다.
Jim: 그건 임베디드 C였나요, 아니면 일반 애플리케이션용이었나요?
Ben: 좋은 질문입니다. 첫 직장에서 절반 정도는 임베디드였고, 작은 마이크로컨트롤러용 조그만 부트로더나 아주 작은 마이크로컨트롤러용 디바이스 드라이버를 만들었습니다. 요즘 사람들이 “임베디드 리눅스” 얘기하는 걸 들으면 문득 이런 생각이 들어요. 그건 임베디드가 아니잖아! 2기가헤르츠 프로세서에 램이 1기가바이트나 되는데 말이죠. 제가 말하는, 제가 했던 의미의 임베디드는 8비트나 16비트짜리 아주 작은 마이크로에 램이 2킬로바이트인 수준이었습니다. 회사가 쓰던 칩 중 하나가 MSP430이었고, 이후에는 ARM7으로 넘어가면서 32비트 작업도 조금 하게 됐죠.
Jim: 당시에는 팀 단위로 일하셨나요?
Ben: 아주 작은 회사였고, 팀이라고 해봤자 다른 한 분, 정말 뛰어난 전기 엔지니어이자 펌웨어 개발자 한 분이 전부였습니다. 제 첫 멘토였고 정말 좋은 분이었는데, 게리라는 분이셨어요. 멘토 역할을 해주시면서 프로그래밍의 진짜 소프트웨어 엔지니어링 측면을 배우도록 많이 도와주셨습니다.
Jim: 두 명으로 구성된 팀에서도 그런 걸 배울 수 있었나요? 버전 관리나 소스 공유, 빌드 파이프라인 같은 것들 말입니다.
Ben: 그런 건 나중에 배우게 됐습니다. 그분은 소프트웨어 엔지니어링과 컴퓨터 과학의 기초 같은 것들을 가르쳐 주셨고, 옆에서 보고 배우며 익힌 것도 있습니다. 다만 그때 그분은 버전 관리를 그다지 믿지 않으셨고, 그래서 저도 배우지 못했죠. 그래서 그런 것들은 한참 뒤에야 접하게 됐습니다. 참고로 지금은 버전 관리를 굳게 믿습니다.
Jim: 다행이네요.
Ben: 정말 좋은 거죠! 저는 모든 작업에 Git을 씁니다. 버전 관리나 CI 같은 건 경력에서 훨씬 뒤에 접했습니다. 그때는 프로그래밍 기초와 구조화에 대한 것이었죠. 그래서 임베디드 작업에는 C를 썼는데, 당시 제가 제대로 다룰 줄 아는 유일한 전문 도구도 C였습니다. 그래서 우리가 작업하던 기상 시스템용 웹 백엔드를 만들어야 했을 때 웹 페이지를 렌더링하고 예쁜 그래프를 그리고 GIF 파일들을 출력해야 했는데, 제가 아는 언어는 C뿐이라 C 기반 CGI 스크립트로 작성했습니다. CGI 스크립트나 웹 작업으로는 정말, 정말 최악의 선택이지만 그래도 일은 해냈습니다. 제가 떠난 뒤에 온 분들은 분명 저를 원망했을 것 같지만, 그래도 그걸로 몇 년간 당면한 문제는 해결됐습니다.
Jim: 그 다음에는 어떤 프로그래밍 언어와 기술들을 배우셨나요?
Ben: 그 뒤로는 두 형제와 함께 스타트업을 했습니다. microPledge라고 불렀는데, 자세한 얘기도 할 수 있지만 프로그래밍 측면에 집중하자면 그때 Python을 배웠습니다. 그 전부터 Python에 관심이 있었고, 웹 애플리케이션을 만드는 데 C보다 훨씬 낫겠다고 생각했는데 실제로 그랬습니다. 그래서 형제들과 그 스타트업을 하면서 Python을 익혔죠.
그때 이미 형 중 한 명은 꽤 탄탄한 Python 개발자였고, 덕분에 Python은 물론 테스트, 버전 관리 같은 것도 배웠습니다. 모두 그 스타트업 경험의 일부였죠. 스타트업 자체는 실패했지만 가르침 측면에서는 성공이었습니다. Python과 소프트웨어 엔지니어링 측면에서 커리어에 필요한 기술을 얻게 됐으니까요. 그래서 Python이 그때의 언어였고, 지금도 제 주력 언어입니다. 현재 직장에서도 주로 쓰는 언어죠. 그때 정말 좋은 경험이었습니다.
Jim: Python의 os.scandir에 기여하셨다고 하셨죠?
Ben: 네, 그건 몇 년 뒤의 일입니다. 타임라인을 한번 확인해 볼게요. 2016년에 글을 쓰긴 했지만 시작은 그보다 몇 년 전인 2012년이네요. 재귀적인 디렉터리 구조를 순회할 방법을 찾고 있었는데, 당시에는 Windows에서 Python을 쓰고 있었습니다. Windows 탐색기에서는 재귀 디렉터리를 우클릭해 파일 크기를 보면 정말 빠르게 나오더군요. 스캔하는 게 보이긴 하지만 엄청 빨랐습니다. 그런데 Python에서 os.walk로 같은 걸 하면 훨씬 느렸어요. 왜 그럴까 살펴보기 시작했죠. Python이 탐색기가 하는 만큼은 빨라질 수 있지 않을까 싶었거든요.
알고 보니 os.walk의 설계와 구현 때문에 불필요한 시스템 호출을 엄청나게 하고 있었습니다. Windows에서 파일 이름을 가져오기 위해 쓰는 FindFirst와 FindNext 호출에 더해 모든 파일에 대해 os.stat 호출을 추가로 하고 있었던 거죠. 사실 그 추가 stat 호출은 필요하지 않습니다. Python은 호출을 하고 나서 정보의 절반을 버린 뒤, 다시 추가 호출을 하고 있었던 셈이죠. 그래서 scandir는 버려지던 정보를 버리지 않고 디렉터리를 재귀적으로 순회하면서 필요한 정보를 얻을 수 있게 해줍니다.
Jim: 그렇지만 이건 모든 플랫폼에서 동작하도록 이식된 거죠?
Ben: 네. Windows와 Linux에서 서로 다른 OS 호출을 쓰지만 아이디어는 같습니다. Windows는 FindFirst와 FindNext에서 파일 속성을 전부 넘겨줍니다. Linux는 크기 같은 건 안 주고 파일 타입, 즉 디렉터리인지 파일인지 정도만 알려줍니다. 하지만 Linux에서도 그 정보만으로 os.stat 호출을 아낄 수 있어요. 디렉터리인지 판단해 맞으면 재귀적으로 들어가고 아니면 안 들어가면 되니까요. 그래서 Windows에서는 훨씬 빨라졌고 Linux에서도 속도 향상이 있었습니다. 네트워크 파일 시스템에서는 네트워크 호출 횟수가 줄어드니 효과가 더 큽니다. 상상하시듯 호출 횟수가 적을수록 더 좋으니까요.
Jim: 그러면 os.walk는 폐기된 건가요?
Ben: 아니요. os.walk는 scandir를 이용해 재구현됐습니다. scandir는 좀 더 낮은 수준의 도구이고, 전체 아이디어는 os.walk를 더 빠르게 만드는 것이었죠. 그래서 구조는 거의 비슷하게 유지하면서 listdir와 os.stat 호출 대신 scandir를 쓰도록 os.walk를 다시 작성할 수 있었습니다. 그래서 os.walk의 시그니처를 전혀 바꿀 필요가 없었고, 이를 쓰던 사람들은 scandir의 존재도 모른 채 성능 향상을 그대로 누릴 수 있었죠. 돌이켜보면 당연해 보이지만, 내부 구현을 완전히 갈아끼워 모두가 공짜로 속도 향상을 얻게 한다는 아이디어가 정말 멋지다고 기억에 남아 있습니다. os.walk를 쓰는 스크립트가 워낙 많았으니 혜택을 본 사람도 많았고요.
Jim: CGI 프로그램을 작성하셨다고 했고 형제들과 스타트업을 하며 Python으로 웹 작업을 하셨다고 하셨는데, 그러면 제대로 동작하는 웹사이트를 만들려면 HTML, JavaScript, 데이터베이스 같은 다른 기술들도 익혀야 했겠네요?
Ben: 네, 그런 것들을 꽤 많이 익혔습니다. 데이터베이스와 HTML은 — HTML은 C로 일하던 직장에서 이미 배웠지만 — 특히 SQL 데이터베이스는 형제들과 스타트업을 시작할 때 처음 접했습니다. 처음 마주하면 관계형 모델이 좀 머리를 복잡하게 합니다. 사고방식 자체가 꽤 다르거든요. 그래서 익숙해지는 데 시간이 좀 걸렸지만 정말 강력한 기술이고 그 이후로 계속 SQL DB를 써오고 있습니다. 확실히 데이터베이스를 배웠고 JavaScript도 조금 접했습니다. 그때는 JavaScript가 웹사이트 전체를 만드는 핵심이라기보다는 덧붙이는 요소 정도였죠. 지금도 사람들이 HTML로 충분히 할 수 있는 것까지 JavaScript부터 찾는 건 여전히 마음에 들지 않습니다.
Jim: 그 얘기는 “the small web”이라는 에세이에서도 조금 다루셨죠.
Ben: 네, 그렇습니다. 앞서 말씀드린 것과도 통하는데, 저는 언제나 빠르고 가볍고 작은 것들을 좋아했습니다. 요즘 웹은 페이지가 비대하고 크고 느린 경우가 많잖아요. 그 와중에 이미지 용량을 줄이거나 속도를 느리게 하는 JavaScript를 줄이는 등 신경 쓰는 분들이 있는데, 그런 노력을 정말 좋아합니다. 지금은 그와 관련된 움직임도 조금 있습니다. “small web”이라는 느슨한 표현이 있고 “indie web”이라는 말도 있는데, 사람들이 직접 웹사이트를 만들고 더 손수 작업하면서 기본으로 돌아가려는 흐름입니다.
Jim: 웹만의 문제는 아니죠. Electron으로 작성된 애플리케이션들은 두꺼운 웹 기술 위에 세워진 데스크톱 애플리케이션이니, 그 비대함을 그대로 데스크톱으로 가져오고 있잖아요.
Ben: 네, 확실히 그렇습니다. 그 모든 기술을 재활용하는 가치를 이해합니다. 부분적으로는 패키징 문제라고 생각하고, Electron을 통째로 끌어오지 않고 OS에 기본으로 있는 브라우저 기술, 이미 기기에 깔려 있는 브라우저를 활용해 데스크톱 앱을 만드는 작업들도 본 적이 있습니다. 도구 이름은 기억이 안 나지만, 그렇게 가려는 방향이라면 더 나은 방식처럼 보입니다.
Jim: 그 뒤로 기술 스택이 또 조금 더 발전했겠네요. 다음에는 어떤 기술들을 배우셨나요?
Ben: 뭐, 조금 건너뛰어 말하자면 Postgres나 JSON 같은 웹 기술들이 있었지만, 그래도 수년간은 대부분 Python을 썼습니다. 그러다 — 언제였는지 제 블로그를 보니 2017년이라고 하네요 — 2017년에 Google의 프로그래밍 언어인 Go를 배웠습니다. 개인 프로젝트 때문에 배운 건데, 얼마 지나지 않아 다음 직장에서 Go를 광범위하게 쓰고 있어 업무적으로도 매우 유용했고 지금까지도 그렇습니다.
Jim: 왜 하필 Go를 배우게 되셨나요? 배울 수 있는 언어가 그렇게 많은데 Go의 어떤 점이 끌렸나요?
Ben: 겉모습이 마음에 들었습니다. 뒤에 벨 연구소 출신 분들이 있다는 점도 좋았고요. The Go Programming Language 책이 브라이언 커니핸과 지금도 Google Go 팀에 있는 앨런 도노반이라는 분이 함께 쓴 책이라는 걸 봤을지도 모릅니다. 어쨌든 브라이언 커니핸 이름이 있었고, Go를 만든 세 사람 중 두 명이 벨 연구소 출신이었죠. Go를 설계한 세 사람은 켄 톰슨, 롭 파이크, 로버트 그리즈머였는데 그중 두 분이 예전 Unix와 벨 연구소 분들이라고 알고 있습니다.
원래 관심을 끌게 된 이유는 그게 아니었습니다. 해커 뉴스 같은 곳에서 Go를 보게 됐고 디자인 철학이 마음에 들었어요. 문법이 단순하고 언어 설계에 일종의 Keep It Simple, Stupid 태도가 담겨 있는데, 그럼에도 툴링은 그때도 정말 전문적이고 훌륭했습니다. Go 팀도 그 점을 솔직하게 말합니다. 언어 자체에는 새로운 것이 없다고요. 독특한 개념이 몇 가지 있긴 하지만 혁명적이거나 획기적인 것은 없습니다. 가장 큰 매력은 툴링과 전체가 함께 짜여진 방식, 그리고 라이브러리의 품질입니다. 컴파일러 속도라든가, go test만 치면 모든 테스트가 자동으로 실행된다는 점, go build를 치고 환경 변수 하나만 설정하면 크로스 컴파일이 된다는 점 같은 것들이요.
Jim: Go로 일거리를 찾았다고 하셨는데, Go를 쓰는 일자리를 직접 찾으신 건가요, 아니면 우연히 그렇게 된 건가요?
Ben: 우연히 그렇게 된 경우지만, 결국에는 그들이 Go를 쓴다는 점 때문에 제가 그 직장을 선택한 부분도 있습니다. 어느 정도는 그쪽에서 저를 선택한 셈이기도 한데, Go 기반 면접이었고 Go 문제를 풀었던 기억이 납니다. 그래서 제 Go 실력을 좋게 봐주신 것 같아요.
Jim: 나중에는 Go로 AWK 프로그래밍 언어의 인터프리터와 변환기를 만드셨죠. AWK는 처음 어디서 접하셨나요?
Ben: AWK를 처음 접한 게 언제였냐고요? AWK라는 게 있다는 건 오래전부터 알고 있었습니다. 70년대에 나온 오래된 도구지만, 제대로 써본 적은 없었어요. 제 이력이 DOS와 Windows 쪽이라 Linux나 Unix가 아니었기 때문이기도 하죠. 그래서 AWK가 곁에 있지 않았지만 존재 자체는 알고 있었고, 이후 Unix 기반인 Mac에서 개발을 시작하고 본격적으로 Linux를 쓰면서 AWK를 더 자주 접하게 됐습니다.
정말 불을 지핀 건 브라이언 커니핸과 A.W.K. 세 사람, 즉 AWK의 창시자인 앨프리드 아호, 피터 와인버거, 브라이언 커니핸이 함께 쓴 The AWK Programming Language의 앞 두 챕터를 읽으면서였습니다. 그들이 함께 쓴 책인데, 주로 브라이언이 집필했다고 생각합니다. 어쨌든 그 책을 읽기 시작했는데, The C Programming Language인 K&R 책과 같은 스타일이더군요. 간결하고 알차게 꽉 채워져 있습니다.
그래서 2장을 마치면 AWK의 모든 것을 알게 됩니다. 그들이 AWK를 전부 가르쳐주고, 나머지 장에서는 세부 내용을 다루며 AWK로 작은 컴파일러를 만들거나 작은 데이터베이스 시스템을 만드는 식입니다. AWK로 할 수 있는 일들을 보면 꽤 멋지죠. 그래서 AWK가 흔히 보이는 난해한 원라이너 문법에서 ‘아, 사실 꽤 단순한 개념이구나. 매 줄을 읽어 필드로 나누고 필터가 맞으면 액션을 수행하는 거구나’ 하는 이해로 확장됐습니다. 단순한 개념이지만 밑바탕에는 완전한 프로그래밍 언어가 있는 셈이죠. 그 생각이 머리를 계속 굴리게 했습니다.
마침 그때 파싱이나 프로그래밍 언어 인터프리팅을 이것저것 만지작거리고 있었고, 두 가지를 합쳐 Go로 이걸 아주 작게 부분 집합으로 만들어볼 수 있겠다 싶었습니다. 그래서 실제로 시작했습니다. 필터 하나와 액션 하나만 받는 아주 미니멀한 AWK 버전을 만들다가 점점 기능을 추가했고, 어느 날 문득 아예 완전한 POSIX 구현으로 만들어보자고 마음먹었습니다. 그때가 뉴욕에 있을 때였는데, 아내와 가족과 함께 뉴욕에서 10년 정도 살았거든요. 매일 출퇴근에 버스와 지하철로 한 시간씩 걸렸습니다. 그래서 아침에 버스에서 30~40분씩 앉아 제 GoAWK 프로젝트를 붙잡고 해킹했습니다. 정말 뉴저지 트랜짓 버스를 타고 뉴욕으로 들어가며 쓴 겁니다.
Jim: 『새로운 버전의 The AWK Programming Language』 2판, 즉 신판 작업에 참여하게 된 계기는 무엇이었나요?
Ben: 네, 그건 — 제가 브라이언 커니핸과 처음 연락하게 된 건 커니핸 인터뷰를 보면서였습니다. 거기서 AWK 얘기를 하시면서 자신의 버전, 사람들이 “단 하나의 진짜 AWK”라고 부르는 “one true AWK”를 업데이트해 CSV(쉼표로 구분된 값) 파일 지원을 추가하고 있다고 하시더군요. 그건 저도 GoAWK에 추가하고 싶던 기능이었습니다. 아니면 그때 이미 추가했을지도 모르겠네요. 타임라인이 가물가물합니다. 어쨌든 GoAWK에 추가했고 정확한 시점은 기억이 안 나지만, 그 인터뷰를 보고 와, 나도 같은 걸 하고 있구나 싶었습니다.
그래서 브라이언, 즉 커니핸의 코드를 보기 시작했는데, 오, 같은 걸 하고 있구나 싶어 좋았습니다. 그러다 그분이 UTF-8을 처리하는 방식에서 성능 문제를 발견했습니다. 제가 보기에 성능 문제였고 실제로도 정당한 문제라고 생각합니다. 그분이 하던 또 다른 작업이 CSV와 함께 오랜만에 AWK에 UTF-8 혹은 유니코드 지원을 추가하는 것이었습니다. 참고로 덧붙이자면, UTF-8을 처리하는 경우라면 AWK는 실제로 유니코드를 전혀 문제없이 지원합니다. UTF-8이 유니코드를 8비트로 인코딩한 것이고, 유니코드 문자를 문자 단위로 처리하지만 않는다면 모든 게 잘 동작하거든요. 그래서 대부분의 AWK 스크립트는 예전 버전의 AWK에서도 이미 잘 동작하지만, 문자 단위 처리가 필요한 특정 기능들이 있어서 그걸 추가하고 계셨던 겁니다.
그래서 문자열의 길이를 구할 때, 유니코드 문자 단위라면 바이트 수가 아니라 문자열 안에 있는 유니코드 문자 수를 원하게 됩니다. 그런데 성능 문제는 strlen 함수, 즉 AWK의 length 함수가 문자열 길이에 대해 선형 시간, 즉 N에 비례하는 연산이 되어버린다는 점이었습니다. 바이트를 하나하나 세며 UTF-8을 디코딩해 문자가 몇 개인지 알아내야 하기 때문이죠.
그렇게 되면 문자열에 대한 length 연산이 상수 시간의 초고속 연산에서 문자열 길이에 비례하는 선형 시간 연산으로 바뀌게 됩니다. 대부분 문자열이 짧으니 괜찮지만, AWK로 JSON 파일을 처리한다든가 — 사실 AWK로 해서는 안 될 미친 짓이지만 누군가는 하는 — 긴 문자열을 다루면서 length를 반복해서 호출하면 순식간에 N 제곱 문제가 됩니다. 루프 안에서 length를 호출하고 있었는데 갑자기 의도치 않게 N 제곱이 되어버리는 거죠. 그래서 요컨대 이 문제를 제기했습니다.
저는 GoAWK도 같은 방식으로 유니코드를 인식하도록 바꾼 적이 있고, 그때 length를 N에 비례하는 연산으로 만들었습니다. 그런데 GoAWK를 쓰던 분 한 분이 “아이고, 이거 때문에 제 조그만 JSON 처리기가 중간 크기 JSON 파일 하나 처리하는 데 25분이 걸려요! 스크립트가 망가졌어요!”라고 하시더군요. 그 지수적 동작 때문이었죠. 그래서 GoAWK에서는 그 변경을 되돌리고, 아, 이건 너무 어렵다, 다시 바이트 기준으로 돌아가자고 했습니다. 이미 GoAWK에서 같은 실수를 겪어봤기 때문에 커니핸에게 연락해 “GoAWK에서 이런 성능 문제를 겪었는데, JSON 처리 같은 특정 유형의 AWK 사용에서 성능을 망가뜨릴 가능성이 높습니다”라고 말씀드렸습니다. 그분도 그게 문제임을 인정하셨지만 되돌리지 않기로 하셨고, “이 동작을 유지하고 싶다. 많은 스크립트에 영향을 주지는 않을 것 같다”고 하셨습니다. 아마 맞는 말씀일 겁니다. 대부분의 실제 사용에는 영향을 주지 않겠지만, 그래도 작은 성능 함정이 하나 있는 셈이죠.
그렇게 커니핸과 AWK에 대해 이야기를 나누기 시작했고, 몇 년 뒤 제 이름을 GoAWK에서 보시고 프로젝트를 좋게 봐주셨는지 책을 다시 쓰실 때, 『The AWK Programming Language』 2판을 준비하시면서 이메일로 연락을 주셔서 “이 책을 검토하고 기술 감수자 중 한 명이 되어줄 수 있겠냐”고 제안하셨습니다.
Jim: 정말 기분 좋았겠네요.
Ben: 네, 정말 멋진 일이었습니다. 커니핸 책의 기술 감수자 명단에 제 이름이 들어간다는 게 조금은 자랑스러운 순간이었습니다.
Jim: 물론이죠. 앞으로 벤 호이트 님에게는 어떤 계획이 있나요?
Ben: 지금은 Canonical에서 일하고 있는데, 거기서는 Go와 Python을 많이 씁니다. 사실 Canonical에는 GoAWK 프로젝트 덕분에 채용됐습니다. 제게는 그냥 사이드 프로젝트였는데, Canonical에서 꽤 높은 분이 연락을 주셔서 “Canonical에서 하는 작은 사이드 작업에 GoAWK 프로젝트를 쓰고 있는데 꽤 멋지더군요. 혹시 일자리 찾고 계신가요?”라고 하시더군요.
마침 그때 일자리를 찾고 있던, 눈여겨보고 있던 때라 그렇게 Canonical에서 일하게 됐습니다. 사실 GoAWK를 업무에서 직접 쓰지는 않습니다. Canonical에서 실제로 쓰이고 있지도 않고요. 하지만 그 프로젝트 덕분에 일자리를 얻게 됐죠. 그래서 지금 하는 일은 Canonical에서 클라우드와 네트워킹, 인프라 관련 작업을 하는 것이고, 현재는 주로 Python을 쓰지만 Go도 광범위하게 쓰기 때문에 제가 맡은 프로젝트 중 일부는 Go로도 작업합니다. 그래서 제 주력 언어 두 개를 모두 쓰고 있습니다.
Jim: 요즘 인기를 얻고 있는 Rust나 Zig 같은 언어에는 관심이 없으신가요?
Ben: Rust는 계속 지켜보고 있고, 그 약속은 마음에 듭니다. 약속은 좋지만 문법은 싫어요. 정의상 안전성, 타입 시스템을 통한 안전성, 소유권을 추적하는 방식 같은 약속 말이죠. 하지만 배워본 적은 없어서 깊게 논평하지는 않겠습니다. 학습 곡선이 매우 가파른 것 같고, Rust를 배운 사람들도 그 점을 인정하더군요. 문법도 꽤 많고 그래서 제게는 그다지 끌리지는 않지만, 사용하기 시작한 분들에게는 박수를 보내고 싶습니다.
반면 Zig은 제가 본 바로는 제 사고방식에 정말 잘 맞습니다. 그래서 Zig에 꽤 끌립니다. 제가 보기에는 Zig은 더 나은 C이고 Rust는 더 나은 C++ 같은 느낌으로 느슨하게 생각하고 있습니다. 그래서 Zig이 좋습니다. 아직 써보진 않았지만 본 것들이 마음에 들고, 명시성이나 모든 메모리 할당이 명시적으로 드러나는 점이 좋습니다. Go의 일부 기능을 가져온 부분도 있고요. Zig의 모습이 마음에 들어 배워보고 싶습니다.
Jim: 그 밖에 정복하고 싶은 분야가 또 있을까요?
Ben: 글쎄요 — GiftyWeddings.com이라는 작은 사이드 프로젝트가 있습니다. 웨딩 레지스트리 웹사이트인데, 한동안 운영해 온 재미있는 작은 프로젝트입니다. 한 달에 몇 달러 정도 벌어다 주긴 하지만 버는 돈으로 보면 정말 푼돈 수준이고, 주로 새로운 언어나 도구를 시험해 보는 테스트베드로 활용하고 있습니다. 그래서 가끔 백엔드를 다시 쓰거나 프론트엔드를 다시 쓰면서 새로운 도구를 시도하곤 합니다. 꽤 재미있는 방식이었고, Go도 그렇게 배웠습니다. Go를 배우게 된 사이드 프로젝트가 바로 그거였죠.
요즘은 htmx라는 라이브러리 혹은 툴킷을 보고 있습니다. 들어보셨나요? 꽤 인기를 얻고 있는 것 같고 작동 방식이 마음에 듭니다. 기존 백엔드 도구를 유지하고 HTML을 유지하면서 이를 확장하는 방식이잖아요. “이걸 전부 JavaScript로 교체하자”는 게 아니라요. 물론 여전히 JavaScript를 쓰긴 하지만 완전히 다른 모델이라 — 네, htmx가 보기 좋아 보입니다. 한번 시도해 보고 싶고, 제 Gifty Weddings 프로젝트에 적용해 볼까 생각 중입니다.
Jim: 아주 좋습니다. 벤 님, 오늘 밤 시간 내주셔서 감사합니다. 대화 나눠서 정말 즐거웠습니다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기