Loris Cro의 블로그 포스트(https://kristoff.it/blog/why-go-and-not-rust/) 옮김
본문은 원저자의 허가를 받고 번역한 글입니다. 오역과 과한 의역이 존재합니다. 넓은 양해 부탁드립니다.
당신이 Go를 메인 언어로 사용하는 개발자라고 상상해봅시다. 개발자 모임에 참석하고, 사람들과 의견을 나누다, 얼마 전 본인이 무엇인가 할 수 있는 작은 툴을 Go를 이용해 개발했다는 사실을 알려주기로 합니다. 당신은 그 툴이 Go로 만들어졌으니 빠르다는 등, 싱글 바이너리라는 등의 이야기를 합니다. 모임의 사람들은 당신의 이야기를 즐겁게 듣고 당신도 뿌듯함을 느끼기 시작합니다. 하지만 스산한 바람과 함께 누군가 당신 뒤에 다가와 이렇게 말합니다.
“Why Go and not Rust?”
들떴던 기분이 약간 가라앉습니다. 뭐, 간단하게 자신이 잘 사용할 수 있는 언어가 Go라서 이용한 것이다, 라고 말할 수도 있겠지만 만족스러운 대답이 아니겠죠. 당신은 자신의 툴이 얼마나 빠른지 자신만만하게 이야기하고 있었고, 질문을 건넸던 낯선이가 Rust가 Go에 비해 가진 장점을 이용해 간단하게 반박해버립니다.
당신은 기분이 나빠집니다. 애초에 왜 Go를 공부하기로 선택하셨던지? 당신은 Go가 빠르며, 동시성 제어가 뛰어난 언어라고 들었기 때문입니다. 그리고 이제 Rust가 화제에 오르더니 모든 사람들이 Rust가 Go보다 얼마나 뛰어난지에 대해 이야기하기 시작합니다. 조금 전까지 Go의 성능에 흥미로워하던 그들의 모습은 온데간데 없습니다. 이 사람들이 연기를 한 것일까요. 완벽한 프로그래밍 언어는 존재하지 않다는 것을 당신도 알고 있습니다. 개발자는 잘못된 선택을 하고 기술적 막다른 길(Technological cul de sac)에 얼마든지 빠질 수 있습니다. 그리고 몇 년 전의 당신은 여러 언어 중 Go를 택했고 “Why that and not Go?”라고 말하는 그룹에 합류했었죠.
위 이야기는 실화가 아닙니다. 하지만 Rust 팬덤 사이에는 타인을 구원하고자 Rust를 집요하게 어필하는, 과도하게 흥분한 몇몇 회원이 있다는 것은 많이들 알고 있는 사실입니다. 그것은 Rust의 잘못이 아닙니다. 성공적인 프로젝트는 언제나 삐뚫어진 팔로워를 동반합니다. 이는 피할 수 없습니다. 그 주변 모든 사람들이 이 Rust 광신도와 대면해야하며, 특히 Go 개발자들은 Go와 Rust의 messaging의 유사성 때문에 비교 대상으로 더 자주 언급됩니다.
Go is fast, but Rust is faster.
Go has an efficient garbage collector, but Rust has static memory management.
Go has great concurrency support, but Rust has provably-correct concurrency.
Go has interfaces, but Rust has traits and other zero-cost abstractions.
Go 개발자가 보기에는 약간 사기당한 기분이 들 수도 있습니다. 반면에, Python 개발자들은 이런 Rust에 장점에 전혀 흔들리지 않습니다. 그들은 Python이 많은 방면에서 느리고 비효율적으로 작동하는 언어임을 압니다. 하지만 Python의 역할은 “쉽게 코딩하고 퍼포먼스가 중요한 영역은 C를 이용하는 것”(make the code easy to write and offload to C when performance matters)임을 인지하고 있죠.
Go는 어떨까요?
Go is very good for writing services
Go는 구글이, 구글의 문제를 해결하기 위해 만든 언어입니다. 대부분은 네트워크에 연결된 서비스들을 수반했죠. Go의 동시성 모델은 복수의 독립적인 리퀘스트를 처리해야하는 서버사이드 어플리케이션에서 쓰이기 적절한 구조로 되어있습니다.
복잡한 result-passing scheme보다 말이죠. 이것이 await
이 아닌 go
가 주어진 이유입니다.
Go는 HTTP와 그 관련 프로토콜에 대한 지원이 뛰어나며, 잘 작동하는 웹 서비스를 빠르게 구현할 수 있습니다. 제 경험상으로는 컴포넌트 간의 인터페이스를 명쾌하게 정의하여 사용해야 할 때 Go는 관용적인 Node.js의 좋은 대체제가 되었습니다.
또한, Go는 동시성과 퍼포먼스 문제의 진단이 효과적이며, cross-compilation은 어느 플랫폼이든 배포를 쉽게 할 수 있습니다.
Go is simple
Go는 기본적으로 제공하는 기능의 종류가 다양하지 않으며 이것을 자랑스럽게 여깁니다. 이로 인해 Go는 쉽게 배우고, 프로젝트의 크기가 커져도 이해할만한 구조를 유지할 수 있습니다. Go의 개발진은 Go가 지루한 언어라고 불리기를 좋아했습니다. 그들은 언어에 새로운 것을 넣기보다, 사람들을 “적은 재료로 많은 것을 할 수 있게(do more with less)” 만들기 위해 노력했으며 그에 대해 성공을 거뒀습니다.
Rust도 마찬가지로 웹서비스 개발에 적절한 언어입니다. 하지만 이 단순성에 대해서는 Go를 이길 수 없습니다.
단순성 뿐만 아니라 Go는 사용되지 않은 변수, 임포트문, 같은 디렉터리에서 다른 패키지에 속하는 파일 등,
다른 언어들이 관대한 것에 대해 까다롭습니다. 한때는 GOPATH
외부에 저장된 프로젝트를 지원하지 않기도 했죠(다행히 지금은 아닙니다).
또한, Go는 코드에 “지문”이 남는 것을 거부합니다. go fmt
를 이용하여 통일된 작성 스타일을 강요하죠.
사실 위 특징들은 하나하나 보면 전혀 새로운 것이 아니지만, 합쳐져 Go의 철학을 표출합니다. 싫어하는 사람들도 많지만, 개인적으로 이는 끝내주는 특성입니다. 특히 엔터프라이즈 소프트웨어(Enterprise Software)에서 말입니다.
Go is great for enterprise software
이미 말씀드렸듯이 Go는 구글이 문제를 해결하기 위해 개발된 언어입니다. 그리고 구글의 문제는 확실하게 거대기업규모로 존재합니다. 이것이 개발진의 의도인지, 커다란 기업에서 쓰이기에 자연스레 발생한 것인지 확실치 않지만, Go는 의심의 여지없이 기업규모의 소프트웨어 개발에 사용되기에 특출난 언어입니다.
만약 당신도 기업에서 Go를 이용한 소프트웨어를 개발한 적이 있다면 이게 무슨 뜻인지 아실겁니다.
엔터프라이즈 소프트웨어 개발은 다른 종류의 개발과는 다른, 괴상한 짐승입니다. 만약 경험이 없다면, “엔터프라이즈 소프트웨어가 도대체 뭔데?”라고 물으실 수 있습니다. 저는 고문으로 지내면서 그런 종류의 개발 경험이 좀 있습니다. 해당 주제에 관한 제 견해는 다음과 같습니다.
ENTERPRISE SOFTWARE DEVELOPMENT IS ABOUT SCALE
전체 사용자 수나, 다루는 데이터의 크기에 대해 말하는 것이 아닙니다. 정의하자면 범위(scope)와 과정(process)에 관한 것입니다.
Enterprise software always has a big scope.
엔터프라이즈 소프트웨어는 도메인이 넓거나, 도메인은 좁지만 무지막지하게 복잡하거나, 혹은 둘 다일 수 있습니다. 그러한 소프트웨어를 만들 때, 비기술적인 문제가 대부분의 기술적인 문제보다 중요하게 여겨지는 경우가 빈번합니다.
To unravel complex domains you need a well-structured process.
우리는 도메인 전문가, 분석가, 이해관계자들이 이해할 수 있도록 잘 구축된 프로세스가 필요합니다. 당신과 같은 기술자는 해당 도메인에 대해 이해가 완벽하지 않은 경우가 많습니다. 그리고 도메인 전문가와 이해관계자들 또한 기술에 대해 이해가 부족한 경우도 많죠.
그리고 이 문제는 효율(efficiency)이나, 정확성(correctness)과 같은 기술적인 문제의 무게를 가볍게 취급합니다. 오해하지 마세요, 비즈니스또한 정확성을 중요시합니다. 하지만 그들이 말하는 정확성는 조금 다른 정의를 가지고 있죠. 기술자가 알고리즘적 정확성에 대해 생각할 때, 그들은 오피스 내외의 인력과 이해관계에 대해 생각합니다.
이와 같은 현상이 일반적으로 일어나며 엔터프라이즈 개발의 문제점이 표출되었습니다. 저는 다음 세 가지를 이야기하고 싶습니다.
- 주니어 개발자의 수가 많습니다. 그리고 대부분의 주니어들은 진정 자신에게 가르침을 많이 주는 직장을 구할만큼 운이 좋지 않습니다. 몇몇 직장에서는 채용된 후, 일주일간 구경을 하고 바로 실무에 들어갈 수 있는 것마냥 여겨집니다.
- 프로그램은 온갖 부적절한 이유로 사이즈가 커지고 복잡해집니다. 거대 프로젝트는 관계자들의 이직이 일상적이고, 깔끔한 리팩토링은 꿈도 못꾸며 다양한 수준의 레거시 코드를 남기게 됩니다. 그리고 도메인은 시간에 따라 변합니다. 기능을 잃는 가정(assumptions)과 경계가 무너지는 개념들이 생깁니다. 코드가 닳고 닳을수록 수정 요청에 대응하기 힘들어집니다.
- 툴체인의 상태가 안 좋게 되거나 쉽게 Outdated됩니다. 이것은 앞에서 설명한 것들로 인해 피할 수 없는 결과로 나타납니다. 곳곳의 오래된 코드는 개발자를 특정 툴셋에 제한하고, 주니어는 노력해보겠지만, 위의 매니저와 관계자들은 높은 확률로 기술적 결정의 리스크를 거부합니다. 그저 다른 이들이 어떻게 했는지를 베끼게 됩니다. 정확히 말하자면, 다른 이들이 어떻게 했는지에 대해 Analyst가 주장하는 대로 베낍니다.
GO IS ABOUT SURPRESSING COMPLEXITY AT SCALE
Go는 타 생태계를 이용할 때의 흔한 함정에 빠지는 것을 방지합니다.
Go is much easier to learn than Java or C#.
빠른 습득이 가능합니다. 근본적으로 복잡한 구조의 프로그램은 프로젝트가 지지부진할 때 매니저는 사람을 더 뽑아 해결하려 하지만 아쉽게도 사람이 늘어도 해결되지 않습니다.
The Go community regards as anti-patterns many abstractions regularly employed by Java / C#.
IoC 컨테이너, OOP 상속과 같은 패턴을 배제합니다. 예를 들어 variable은 두 레벨에서만 다뤄지고, 동시성 제어 모델은 CSP 하나만이 이용됩니다. Java/C#의 경우처럼 이해가 어려운 복잡한 문제에 빠지는 것을 방지합니다.
The Go compiler is fast.
이는 테스트의 실행이 빠름을 말합니다. 배포 속도가 빨라지며 전체적인 생산성이 높아집니다.
Go를 사용하면 주니어 개발자들 또한 생산적으로 일할 수 있고, mid-level 개발자의 경우 불안정한 구조를 통해 문제를 일으키는 것을 줄입니다.
이러한 이유로 Rust는 Go보다 엔터프라이즈 개발에 부적합합니다. 물론 Go가 완벽한 언어라거나, Rust가 Go에 비해 가진 장점이 없다는 얘기가 아닙니다. 하지만 Go와 Rust의 비교에서 Go에 대한 일반적인 인식이 잘못되어있다고 봅니다.
Go는 Java/C#보다 빠르며, Java/C#보다 메모리 관리 측면에서 뛰어납니다. 또한 Java/C#보다 확실히 뛰어난 동시성 제어 모델을 가지고 있습니다.
일반적인 Go 프로그램은 단순하며 일반적인 Java/C# 프로그램보다 뛰어납니다. Go가 Java/C#보다 항상 확실하게 뛰어난 속도를 가지는 지는 중요하지 않습니다.
“일반적인 Java/C#” 어플리케이션은 그들의 이론적인 최적의 구조에서 한참 벗어납니다. 예를 들어 C#의 동시성 제어에 관한 이 영상을 보시길 바랍니다.
개인적으로 await
의 사용이 항상 잘못된 것으로 보이는 것이 놀라울 따름입니다. 일반적인 비동기 C# 어플리케이션이 얼마나 뒤틀려있을지 상상해보세요.
실제로 ASP.NET의 표면적인 이유없이 일어나는 데드락 문제는 전혀 드물지 않습니다.
위 이미지는 이 블로그 글에서 따왔으며, 해당 포스트는 C#의 동시성 제어가 완전하지 않은 무수히 많은 경우에 대해 설명하는 글입니다.
In conclusion
Go는 더 좋은 Java/C#이며, Rust는 그렇지 않습니다. 엔터프라이즈 소프트웨어 개발에 Go가 가져올 수 있는 장점이 가비지컬렉터을 없애며 생산성을 떨어뜨리는 것보다 큼은 자명합니다.
Rust는 더 좋은 C++입니다. 만약 당신도 Go가 더 좋은 C라고 들었다면 그것은 옳지 않습니다. 내장된 가비지컬렉터와 런타임이 있는 언어는 절대 C와 비교될 수 없습니다. 그리고, Rust도 C가 아닌 C++과 비교되어야 합니다. 더 좋은 C가 궁금하시면 Zig에 대해 알아보세요.
마지막으로 우리의 이야기로 돌아가서, “Why not Rust” 질문은 위와 같은 예시로 반박할 수 있습니다. 다만, 본인의 아이덴티티를 하나의 언어에 한정하지 맙시다. Rustacean이나 Gopher같은 단어는 브랜딩을 강화하기 위해 만들어진 마케팅 도구에 불과합니다.