부록 G — Rust는 어떻게 만들어지고, "Nightly Rust"는 뭔가요
부록 G — Rust는 어떻게 만들어지고, "Nightly Rust"는 뭔가요
이 부록은 Rust가 어떻게 만들어지는지, 그리고 그게 여러분 같은 Rust 개발자에게 어떤 의미인지를 다뤄요. 언어가 태어나고 자라는 과정을 이해하면 "왜 nightly를 써야 하지?", "feature flag는 뭐지?" 같은 궁금증이 자연스럽게 풀려요.
출처: Rust 공식문서
본문
안정성은 유지하되, 성장은 멈추지 않기 (Stability Without Stagnation)
Rust는 언어로서 여러분의 코드 안정성을 아주 신경 써요. Rust가 단단한 기반이 되어주길 바라니까요. 만약 계속 바뀌는 언어였다면 안정적인 기반 위에 뭔가를 쌓는 게 불가능했을 거예요. 그런데 반대로, 새 기능을 실험하지 못한다면 중요한 결함을 출시가 끝난 뒤에야 발견하게 되고, 그땐 이미 손을 쓸 수 없을 수도 있어요.
이 문제에 대한 해결책이 바로 "안정성을 유지하되 정체되지는 않는다(stability without stagnation)"라는 원칙이에요. 핵심 원칙은 이거예요: 새로운 안정판(stable) Rust로 업그레이드하는 걸 절대 두려워하지 않아도 된다. 업그레이드마다 고통 없이 지나갈 뿐 아니라, 새 기능, 더 적은 버그, 더 빠른 컴파일 시간까지 얻을 수 있어요.
칙칙폭폭! 릴리스 채널과 열차 타기 (Choo, Choo! Release Channels and Riding the Trains)
Rust 개발은 *열차 시간표(train schedule)*에 맞춰 돌아가요. 즉 모든 개발은 Rust 저장소의 main 브랜치에서 이뤄져요. 릴리스는 Cisco IOS 같은 다른 소프트웨어 프로젝트에서도 쓰이는 소프트웨어 릴리스 열차 모델을 따르죠. Rust에는 세 가지 *릴리스 채널(release channels)*이 있어요:
- Nightly
- Beta
- Stable
대부분의 Rust 개발자는 주로 stable 채널을 쓰지만, 실험적인 새 기능을 써보고 싶은 사람들은 nightly나 beta를 쓸 수 있어요.
개발과 릴리스 과정이 어떻게 돌아가는지 예를 들어볼게요. Rust 팀이 Rust 1.5를 릴리스한다고 가정해 봐요. 실제로는 2015년 12월에 릴리스됐지만, 지금은 그냥 현실적인 버전 번호를 위한 예시로 보면 돼요. 새 기능이 추가되면 main 브랜치에 새 커밋이 쌓여요. 그리고 매일 밤, 새로운 nightly 버전의 Rust가 만들어져요. 하루가 곧 릴리스 날인 셈이고, 이 릴리스들은 릴리스 인프라가 자동으로 만들어내요. 그래서 시간이 지나면 릴리스가 이렇게 매일 밤 하나씩 쌓여가요:
nightly: * - - * - - *
6주마다 새 릴리스를 준비할 때가 와요! 그러면 nightly가 쓰던 main 브랜치에서 beta 브랜치가 갈라져 나와요. 이제 릴리스가 두 개가 됐죠:
nightly: * - - * - - *
|
beta: *
대부분의 Rust 사용자는 beta를 적극적으로 쓰지는 않지만, CI 시스템에서 beta를 상대로 테스트하면서 Rust가 혹시 모를 회귀(regression)를 찾아내도록 도와줘요. 그 사이에도 nightly 릴리스는 매일 밤 계속 나가요:
nightly: * - - * - - * - - * - - *
|
beta: *
회귀가 하나 발견됐다고 해볼게요. 회귀가 stable 릴리스에 스며들기 전에 beta를 테스트할 시간이 있었다니 다행이죠! 수정은 main 브랜치에 적용돼서 nightly가 고쳐지고, 그 수정이 beta 브랜치로 백포트(backport)된 다음 새 beta 릴리스가 나와요:
nightly: * - - * - - * - - * - - * - - *
|
beta: * - - - - - - - - *
첫 beta가 만들어진 지 6주가 지나면, 이제 stable 릴리스 차례예요! beta 브랜치에서 stable 브랜치가 만들어져요:
nightly: * - - * - - * - - * - - * - - * - * - *
|
beta: * - - - - - - - - *
|
stable: *
만세! Rust 1.5가 완성됐어요! 그런데 한 가지 잊은 게 있어요. 6주가 지났으니 다음 버전인 Rust 1.6의 beta도 필요하거든요. 그래서 stable이 beta에서 갈라져 나온 다음, 다음 버전의 beta는 다시 nightly에서 갈라져 나와요:
nightly: * - - * - - * - - * - - * - - * - * - *
| |
beta: * - - - - - - - - * *
|
stable: *
이걸 "열차 모델(train model)"이라고 불러요. 6주마다 릴리스가 "역을 떠나지만", stable 릴리스로 도착하기 전에 반드시 beta 채널을 거쳐 오는 여정을 해야 하거든요.
Rust는 시계처럼 6주마다 릴리스돼요. 한 번의 Rust 릴리스 날짜를 알면 다음 릴리스 날짜도 알 수 있어요. 정확히 6주 뒤니까요. 6주마다 릴리스가 잡혀 있다는 건, 다음 기차가 곧 온다는 뜻이라 좋은 점이 있어요. 어떤 기능이 특정 릴리스에 놓치더라도 걱정할 필요 없어요. 얼마 안 있어 또 다른 기차가 오니까요! 이렇게 되면 릴리스 마감에 임박해서 다듬어지지 않은 기능을 욱여넣고 싶은 압박도 줄어들어요.
이 과정 덕분에 언제든 다음 Rust 빌드를 확인해 보고 업그레이드가 정말 쉽다는 걸 직접 검증해 볼 수 있어요. beta 릴리스가 예상대로 동작하지 않으면 팀에 보고해서, 다음 stable 릴리스가 나오기 전에 고칠 수 있어요! beta 릴리스에서 문제가 생기는 건 비교적 드물지만, rustc도 어쨌든 소프트웨어라서 버그가 존재하긴 해요.
유지보수 기간 (Maintenance time)
Rust 프로젝트는 가장 최신의 안정판 버전만 지원해요. 새 안정판 버전이 릴리스되면 이전 버전은 지원 종료(end of life, EOL)에 도달해요. 즉 각 버전은 6주 동안만 지원받는 거예요.
불안정한 기능들 (Unstable Features)
이 릴리스 모델에는 함정이 하나 더 있어요. 바로 unstable(불안정) 기능이에요. Rust는 특정 릴리스에서 어떤 기능이 켜져 있는지를 정하기 위해 "feature flag"라는 기법을 사용해요. 새 기능이 활발히 개발 중이면 main 브랜치, 따라서 nightly에 들어가지만, feature flag 뒤에 가려져 있어요. 사용자인 여러분이 진행 중인 기능을 시험해 보고 싶다면 가능하지만, 반드시 nightly 릴리스의 Rust를 쓰고, 소스 코드에 해당 flag를 명시해서 옵트인(opt in)해야 해요.
beta나 stable 릴리스의 Rust를 쓰고 있다면 feature flag를 쓸 수 없어요. 이 점이 바로, 새로운 기능을 영원히 stable로 선언하기 전에 실제로 써볼 수 있게 해주는 핵심이에요. 아주 최첨단(bleeding edge)을 시험해 보고 싶은 사람은 그렇게 할 수 있고, 단단한 경험을 원하는 사람은 stable에 머물면서 코드가 깨지지 않을 거란 걸 알 수 있죠. 안정성이면서 동시에 정체되지 않는 것. 그게 바로 이 모델이에요.
이 책은 stable 기능에 대한 정보만 담고 있어요. 진행 중인 기능은 아직 계속 바뀌고 있어서, 이 책이 쓰인 시점과 그 기능이 stable 빌드에서 켜지는 시점 사이에 분명 달라져 있을 테니까요. nightly 전용 기능에 대한 문서는 온라인에서 찾아볼 수 있어요.
Rustup과 Rust Nightly의 역할 (Rustup and the Role of Rust Nightly)
rustup은 Rust의 서로 다른 릴리스 채널을 전역(global) 또는 프로젝트별로 손쉽게 바꿀 수 있게 해줘요. 기본적으로는 stable Rust가 설치돼 있어요. 예를 들어 nightly를 설치하려면 이렇게 하면 돼요:
$ rustup toolchain install nightly
rustup으로 설치한 모든 툴체인(각각의 Rust 릴리스와 그에 딸린 구성 요소)을 확인할 수도 있어요. 저자 중 한 명의 Windows 컴퓨터에서의 예시를 볼게요:
> rustup toolchain list
stable-x86_64-pc-windows-msvc (default)
beta-x86_64-pc-windows-msvc
nightly-x86_64-pc-windows-msvc
보시다시피 stable 툴체인이 기본값이에요. 대부분의 Rust 사용자는 대부분 stable을 써요. 여러분도 대부분은 stable을 쓰되, 어떤 특정 프로젝트에서만 최첨단 기능이 필요해서 nightly를 쓰고 싶을 수 있어요. 그러려면 그 프로젝트 디렉터리에서 rustup override를 써서, 그 디렉터리에 있을 때 rustup이 사용할 툴체인을 nightly로 지정하면 돼요:
$ cd ~/projects/needs-nightly
$ rustup override set nightly
이제부터 ~/projects/needs-nightly 안에서 rustc나 cargo를 호출할 때마다, rustup이 기본값인 stable이 아니라 nightly Rust를 쓰도록 보장해줘요. Rust 프로젝트가 많을 때 아주 유용한 기능이에요!
RFC 과정과 팀들 (The RFC Process and Teams)
그럼 이런 새 기능들은 어떻게 알 수 있을까요? Rust의 개발 모델은 Request For Comments(RFC) 과정을 따라요. Rust에 개선을 원한다면, RFC라고 부르는 제안서를 작성할 수 있어요.
누구나 Rust를 개선하기 위한 RFC를 작성할 수 있고, 제안서는 여러 주제별 하위 팀으로 이뤄진 Rust 팀이 검토하고 논의해요. 팀의 전체 목록은 Rust 웹사이트에서 볼 수 있는데, 프로젝트의 각 영역을 담당하는 팀들이 있어요: 언어 설계, 컴파일러 구현, 인프라, 문서 등등. 담당 팀이 제안서와 댓글을 읽고, 자체적인 의견을 달면서 결국 기능을 수용할지 거부할지에 대한 합의가 이뤄져요.
기능이 수용되면 Rust 저장소에 이슈(issue)가 열리고, 누군가가 그걸 구현할 수 있어요. 그 구현자는 처음에 기능을 제안한 사람이 아닐 수도 있어요! 구현이 준비되면, "Unstable Features" 절에서 이야기했듯이 feature gate 뒤에 가려진 채 main 브랜치에 들어와요.
시간이 흐르면서 nightly 릴리스를 쓰는 Rust 개발자들이 새 기능을 시험해 볼 수 있게 되고 나면, 팀원들이 그 기능에 대해, nightly에서 어떻게 굴러갔는지를 논의하고 이걸 stable Rust에 넣을지 말지를 결정해요. 진행하기로 결정되면 feature gate가 제거되고, 그 기능은 이제 stable로 간주돼요! 그리고 열차를 타고 새 stable 릴리스의 Rust에 실려 나가게 되죠.