개발자

"러스트"와 "고"를 선택하는 방법

Serdar Yegulalp | InfoWorld 2021.03.16
10년이 채 되지 않는 기간 동안 2개의 새로운 프로그래밍 언어가 엔터프라이즈 개발의 주요 언어로 부상했다. 구글에서 만들어진 고(Go), 모질라에서 탄생한 러스트(Rust)다.

2개의 언어 모두 현대 소프트웨어 개발의 필수 요소인 정교하고 통합된 툴체인, 메모리 안전성, 오픈소스 개발 모델, 강력한 사용자 커뮤니티를 제공한다.

이렇게 비슷한 부분을 제외하면 러스트와 고는 서로 극명하게 다른 언어다. 서로 다른 목적으로, 다른 요구를 해결하고 다른 종류의 프로그램을 작성하도록 만들어졌다.

따라서 러스트와 고를 비교할 때는 어느 언어가 '객관적으로 더 우수한가'가 아니라, 주어진 프로그래밍 작업에서 '어떤 언어가 더 적합한가'의 시각에서 봐야 한다. 이를 염두에 두고 러스트와 고의 주요 차이점과 각 언어가 적합한 작업의 종류에 대해 살펴보자.


성능 측면에서의 러스트 vs. 고 

러스트의 대표적인 장점은 안정성, 사용의 용이함과 함께 성능인데, 이 가운데서도 성능을 가장 큰 장점으로 볼 수 있다. 러스트 프로그램은 메모리 취급 및 처리를 위한 러스트의 제로 코스트(zero-cost) 런타임 추상화 덕분에 C 및 C++와 대등하거나 거의 근접한 속도로 실행된다.

물론 러스트 프로그램도 어떻게 작성되느냐에 따라 느려질 수 있지만, 최소한 러스트는 안전이나 편리함을 위해 선택의 여지없이 성능을 희생하지는 않는다. 러스트의 비용이라면 개발자가 메모리 관리를 위한 러스트의 추상화를 배우고 마스터해야 한다는 점이다. 

반면 고는 개발자 편의성을 위해 어느 정도의 런타임 속도를 희생한다. 메모리 관리는 고 런타임이 담당하므로 런타임 관련 오버헤드가 필연적으로 발생한다. 그러나 많은 시나리오에서 이 타협은 무시해도 되는 수준이다.

고는 프로그래머가 모든 객체에 대해 강력한 형식을 요구해야 한다는 작은 대가를 치르는 대신 기본적으로 파이썬(Python)과 같은 다른 편의성 중심 언어에 비해 몇 배 더 빠른 속도를 제공한다(파이썬의 편리성과 유연성에는 성능 측면에서 상당한 비용이 따른다).

요약하자면 러스트는 전체적으로 더 빠르지만 대부분의 일상적인 사용례에서 러스트와 고 사이의 속도 차이는 미미하다. 성능이 절대적인 요구 사항인 상황에서는 여러 측면에서 러스트가 고보다 우수하다.


메모리 관리 측면에서의 러스트 vs. 고 

러스트와 고의 메모리 관리는 언어들의 성능 특성과 밀접하게 관련된다.

러스트는 메모리 관리를 위해 제로 코스트 추상화를 통한 컴파일 타임 소유권 전략을 사용한다. 이는 러스트 프로그램은 라이브 상태가 되기 전에 대부분의 메모리 관리 문제를 포착할 수 있음을 의미한다. 메모리 안전성이 확보되지 않은 러스트 프로그램은 컴파일이 되지 않는다.

개발 세계의 상당 부분이 잘못된 메모리 관리 탓에 안전하지 않은 것으로 드러난 소프트웨어 위에서 돌아가고 있음을 감안한다면, 러스트의 접근 방법은 진작에 도입되었어야 마땅하다.

앞서 언급했듯이 러스트의 이 안정성에 따르는 대가는 비교적 높은 학습 난이도다. 프로그래머는 러스트의 메모리 관리 기능을 적절히 사용하는 방법을 알아야 하며 이를 위해서는 시간과 연습이 필요하다. C, C++, C# 또는 자바에서 건너온 개발자가 러스트를 익히려면 코드를 쓰는 방법에 관한 생각의 변화가 필요하다.

참고로 서드파티 프로젝트를 사용한 추가 요소의 형태로 러스트에 가비지 수집을 도입할 수 있다. 이미 'gc crate'라는 프로젝트가 존재하지만 여전히 초기 상태다. 그러나 러스트의 기반 패러다임은 가비지 수집을 사용하지 않는다.

고도 러스트와 마찬가지로 메모리 안전성을 제공하는데, 메모리 관리가 런타임에 자동으로 처리되는 방식이다. 프로그래머는 수천 라인의 고 코드를 쓰면서도 메모리 할당이나 해제에 대해서는 전혀 생각하지 않아도 된다.

런타임 가비지 수집기의 경우 프로그래머에게 어느 정도 통제 기능이 있다. 가비지 수집 임계값을 변경하거나 수동으로 수집을 트리거해서 가비지 수집 주기로 인해 다른 작업이 방해를 받는 경우를 방지할 수 있다.

또한 고에서도 프로그래머의 수동 메모리 관리도 일부 가능하지만 언어의 기능 자체가 가급적 그렇게 하지 않도록 되어 있다. 예를 들어 포인터를 사용해서 변수에 접근할 수 있지만 unsafe 패키지(프로덕션에서 실행되는 소프트웨어에 대해서는 현명하지 않은 선택)를 사용하지 않는 한 포인터 연산을 수행해서 메모리의 임의 영역에 접근할 수는 없다.

당면한 프로그래밍 작업에서 수동 메모리 할당과 해제가 필요하다면(예를 들어 저수준 하드웨어 또는 최대 성능 시나리오) 애초에 이런 작업을 위해 설계된 언어인 러스트가 더 낫다. 고의 자동 메모리 관리가 그 조건에서 무조건 결격 사유라고 전제해서는 안 되지만 고성능 시나리오에서 러스트가 더 적합하다는 것은 입증된 사실이다.

최근 사례로 디스코드(Discord)가 핵심 백엔드 서비스용 언어를 고에서 러스트로 바꿨는데, 그 이유 중에는 고의 메모리 모델 및 가비지 수집과 관련된 문제도 있다. 디스코드 서비스의 러스트 버전은 수동 튜닝이 전혀 적용되지 않은 첫 버전부터 성능에서 고 버전보다 앞섰다.


개발 속도 측면에서의 러스트 vs. 고

개발 속도가 프로그램 속도보다 더 중요할 때도 있다. 파이썬은 비록 실행 속도는 내세울 것이 없지만 소프트웨어를 가장 빨리 쓸 수 있는 언어로 지금의 자리에 올랐다. 고의 매력도 이와 비슷하다. 명료함과 단순함은 빠른 개발 프로세스의 기반이 된다. 컴파일 시간이 짧으며 고의 런타임은 파이썬(및 다른 개발자 친화적인 인터프리트 언어)보다 훨씬 더 빠르다

간단히 말해 고는 단순함과 속도를 모두 제공한다. 빠진 것은 무엇일까? 더 쉽게 배우고 더 쉽게 마스터하고 더 쉽게 유지할 수 있도록 하기 위해 다른 언어에 있는 일부 기능(예를 들어 제네릭)이 빠졌다. 이로 인한 단점은 일부 프로그래밍 작업에서는 꽤 많은 상용구 코드가 필요하다는 것이다. 고에 제네릭을 구현하기 위한 작업이 진행 중이지만 아직 미완성이고 제네릭을 사용하는 새 고로 전환하려면 기존 코드를 새로운 관용구적 코드로 만들기 위해 대해 상당한 재작업이 필요할 것이다.

러스트에는 고보다 언어 기능이 더 많고 따라서 배우고 마스터하는 데 그만큼 더 오래 걸린다. 또한 러스트의 컴파일 시간은 특히 종속성 트리가 큰 애플리케이션에서 고 프로그램보다 대체로 더 오래 걸린다. 러스트 프로젝트는 컴파일 시간을 줄이기 위해 꾸준히 노력해왔지만 여전히 차이가 있다.

빠른 개발 주기와 인력의 신속한 프로젝트 합류가 무엇보다 중요하다면 고가 더 나은 선택이다. 개발 속도보다는 메모리 안전성과 실행 속도가 더 중요하다면 러스트를 선택하면 된다. 어느 경우든 팀이 두 언어 중 하나에 이미 상당히 숙련됐다면 그 언어를 선택하는 편이 유리하다.


동시성과 병렬성에서의 러스트 vs. 고 

현대 하드웨어는 멀티코어이며 현대 애플리케이션은 네트워크로 연결되고 분산된다. 이러한 현실에 대비하지 않는 언어는 뒤쳐진다. 프로그래머는 단일 스레드에서든 여러 스레드에서든 독립적으로 작업을 실행하고 데이터 손상 위험 없이 작업 간에 상태를 공유할 수 있어야 한다. 러스트와 고는 모두 이를 위한 기능을 제공한다.

동시성(Concurrency)은 고루틴(가벼운 스레드) 및 채널(고루틴의 통신 메커니즘)을 통해 처음부터 고 언어의 구문에 구현됐다. 이러한 프리미티브는 경합 조건과 같은 일반적인 문제 없이 많은 작업을 동시에 처리해야 하는 네트워크 서비스와 같은 애플리케이션을 쉽게 쓸 수 있게 해준다. 고는 경합 조건을 불가능하게 하지는 않지만 런타임에 경합 조건이 발생할 수 있는 경우 프로그래머에게 경고하는 네이티브 테스트 메커니즘을 제공한다.

러스트의 경우 2019년 말 버전1.39.0에서 async/.await 키워드 형태로 네이티브 동시성 구문이 도입됐다. async/.await이 나오기 전에는 futures라는 러스트용 크레이트 또는 패키지로 동시성을 구현했다.

러스트의 동시성에는 고의 동시성과 같은 다년간에 걸쳐 축적된 개발자 경험이 없지만 메모리 안전성이라는 러스트의 이점을 물려받았으므로 경합 조건을 노출할 수 있는 러스트 코드는 애초에 컴파일이 되지 않는다. 동시성 또는 비동기 연산은 러스트의 구문 규칙으로 인해 러스트에서 쓰기가 더 어렵지만 장기적으로 높은 내구성을 제공한다.


레거시 코드와의 상호운용성 측면에서의 러스트 vs. 고 

러스트 및 고와 같은 새로운 언어는 과거의 언어에서는 고려하지 않았던 방식으로 메모리 안전성과 프로그래머 편의성을 추구한다. 그러나 새로운 것은 항상 일정 수준 과거의 것과 공존해야 한다. 러스트와 고는 모두 레거시 C 코드와 상호운용되지만 서로 다른 제약이 있다.

러스트는 extern 키워드와 libc 크레이트(러스트에서 패키지를 의미하는 이름)를 통해 C 라이브러리와 직접 상호작용하지만 이와 같은 라이브러리에 대한 모든 호출은 안전하지 않은 것으로 간주해야 한다. 즉, 러스트는 이들의 메모리 또는 스레드 안전성을 보장할 수 없으므로 C 코드와의 인터페이스가 안전한지 여부를 개발자가 수동으로 확인해야 한다. 러스트 FFI 옴니버스(Rust FFI Omnibus) 웹사이트에서 C 및 다른 언어를 위한 러스트의 FFI(Foreign Function Interface)를 사용해 코드를 래핑하는 방법을 보여주는 예제를 많이 찾아볼 수 있다.

러스트의 형식 시스템과 정적 분석 기능을 사용해 언어 간 안전한 다리를 만드는 프로젝트도 있다. 예를 들어 CXX 프로젝트는 러스트에서 C++로의 안전한 바인딩을 제공한다.

고는 C 연계를 위한 cgo 패키지를 제공한다. cgo 패키지를 사용하면 고 코드에서 C 라이브러리를 호출하고 C 헤더 파일을 사용하고 C와 고 사이에서 공통적인 데이터 형식을 변환할 수 있다(문자열 등). 그러나 고는 메모리 관리 및 가비지 수집 언어이므로 C에 전달하는 포인터가 적절히 처리되는지를 확인해야 한다. 또한 cgo는 호출당 오버헤드를 일으키는 경향이 있으므로 C를 직접 다룰 때보다 속도는 느릴 수밖에 없다.

요약하면, C 상호운용성 측면에서 러스트가 고보다 약간 더 유리하므로 기존 C에 대한 중대한 종속성이 있다면 선택의 추는 러스트 쪽으로 기운다. 그러나 러스트와 고 모두 C 상호운용성을 위해서는 개발자가 조금 더 많은 부담을 짊어져야 한다. 더 많은 개념적 오버헤드, 더 긴 컴파일 시간, 더 복잡한 도구, 더 어려운 디버깅 등이다.

참고로 러스트와 고는 높은 수준에서 항상 다른 코드와 상호작용이 가능하지만(예를 들어 함수 호출이 아닌 네트워크 소켓이나 명명된 파이프를 통한 데이터 교환) 이러한 대안에는 속도 저하라는 비용이 따른다.


러스트 vs. 고의 결론

소프트웨어 개발 프로젝트를 위해 러스트와 고 중에 선택하는 경우 당면한 프로젝트에서 가장 필요한 특징을 가진 언어를 선택하면 된다. 러스트와 고의 특징은 다음과 같이 정리할 수 있다.

- 러스트의 이점
  • 런타임 정확도(일반적인 실수가 있을 때 컴파일이 되지 않음)
  • 최상급 실행 속도
  • 가비지 수집 없는 메모리 안정성(서드파티 프로젝트를 통한 실험적인 가비지 수집 선택 가능)
  • 하드웨어 수준 코드

- 고의 이점
  • 빠른 개발 주기
  • 높은 실행 속도
  • 가비지 수집을 통함 메모리 안전성(원할 경우 수동 메모리 관리도 가능)
  • 개발자 편의성
  • 단순명료한 코드 editor@itworld.co.kr 
 Tags 러스트 Go Rust

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.