Erlang 가상 머신(BEAM)과 런타임(ERTS), OTP 애플리케이션과 릴리스 개념을 고수준에서 개관합니다. 표준 배포에 포함된 구성요소, 릴리스를 구성하는 방식, 그리고 Rebar3 같은 도구가 전제하는 OTP 중심 개발 방식을 설명합니다.
Erlang/OTP는 다른 많은 프로그래밍 환경, 심지어 가상 머신을 사용하는 환경과도 다릅니다. Erlang은 애플리케이션을 어떻게 구성해야 하는지, 어느 수준의 격리를 가져야 하는지, Erlang의 VM이 할 수 있는 일과 여러분의 소프트웨어가 할 수 있는 일을 어떻게 분리해야 하는지에 대해 강한 의견을 가지고 있습니다. 단순한 프로그래밍 언어가 아니라, 시스템을 구축하기 위한 전체 프레임워크입니다. 그 핵심 원칙을 이해하는 것이 나중에 모든 것을 다시 쓰지 않고 빠르게 시작하는 열쇠입니다. 이렇게 하면 모든 애플리케이션이 잘 어우러지고, 업데이트를 실시간으로 수행할 수 있으며, 계측과 관측 가능성을 쉽게 확보할 수 있습니다.
이 장에서는 Erlang 가상 머신과, 가장 높은 수준에서 본 OTP의 핵심 개념을 다룹니다.
모든 것의 토대는 BEAM이라 불리는 Erlang 가상 머신 자체입니다. 기술적으로 BEAM은 Erlang 가상 머신의 하나의 구현이며, 다른 구현이 존재할 수도 있습니다. 예를 들어 Erllvm은 LLVM 위에서 동작하는 구현(가능하게 하기 위한 커스텀 패치를 사용)이고, 90년대의 오래된 구현으로 JAM이 있었습니다. Erlang VM은 C로 구현되어 있으며, 많은 멋진 것들을 포함합니다: 프로세스를 실행하는 스케줄러, 가비지 컬렉션, 메모리 할당자, 이벤트를 위한 타이머 휠, 운영체제 기능을 추상화하고 통합된 인터페이스를 제공하는 각종 스마트 스위치(시간 관리, 파일 처리 드라이버 등), Erlang만으로 구현하는 것보다 더 빠르게 동작하는 일부 빌트인 함수(BIFs), 다른 언어로 네이티브하게 구현된 함수를 위한 인터페이스(NIFs)와 이를 위한 특별한 스케줄러까지. 당연히 더 많은 요소가 있지만, 이러한 것들을 BSD나 Linux의 커널처럼 생각해도 됩니다: 더 멋진 것들을 만들기 위해 필요한 저수준 요소들 말이죠.
만약 가상 머신만 있고 그 외에 아무것도 없다면 Erlang 코드를 실행할 수 없습니다. 표준 라이브러리도 없고, 코드를 로드하는 라이브러리조차 없습니다. 이를 모두 동작하게 하려면 우리가 굳이 이해할 필요는 없는 까다로운 부트스트래핑이 있습니다. 다만, 가상 머신과 함께 제공되는 제한된 집합의 사전 로드된 Erlang 모듈이 있고, 이 모듈들을 사용해 네트워킹과 파일 처리 기능을 설정하고, 이것을 바탕으로 더 많은 모듈을 로드하고 실행한다는 점만 알고 있으면 됩니다. 더 자세히 알고 싶다면 The BEAM Book이나 BEAM Wisdoms를 참고하세요.
가상 머신과 사전 로드된 것들, 그리고 코드 로딩을 가능하게 하는 작은 유틸리티들을 모두 합치면, 본질적으로 Erlang Run-Time System(ERTS)이 됩니다. 런타임 시스템은 시작할 때 무엇을 시작할지 지정하는 부트 스크립트(아무도 손으로 작성하지 않습니다)의 지시를 따릅니다.
Erlang은 기본적으로, 셸을 시작하고 직접 애플리케이션을 작성하는 데 필요한 최소한의 코드를 로드하는 부트 스크립트를 제공합니다. 이것만 갖춰지면, 우리는 이제 단순한 가상 머신이 아니라 Erlang 자체에 대해 생각하기 시작할 수 있습니다.
지금까지 설명한 것은 운영체제의 커널에 해당합니다. 이제 사용자 공간 컴포넌트를 위한 토대가 필요합니다. Erlang에서는 이것이 바로 OTP가 담당하는 영역입니다. OTP는 가상 머신 위에서 실행되는 “컴포넌트”를 어떻게 구조화해야 하는지를 규정합니다. 언어는 단지 “프로세스와 메시지”만 있는 것이 아닙니다. 코드를 구조화하는 잘 정의된 한 가지 방식이 있습니다.
참고
OTP는 _Open Telecom Platform_의 약자이지만, Ericsson에서 Erlang을 오픈 소스화하던 시절에 쓰였던, 말 그대로 별다른 의미가 없는 이름입니다.
Erlang/OTP 시스템은 OTP 애플리케이션이라 불리는 컴포넌트로 구조화됩니다. 여러분이 설치한 모든 Erlang 버전 또는 그것으로 구축된 시스템에는 몇 가지 OTP 애플리케이션이 함께 제공됩니다. OTP 애플리케이션에는 기본적으로 두 가지 변형이 있습니다: 단순히 모듈 모음인 라이브러리 애플리케이션과, 모듈 모음일 뿐 아니라 슈퍼비전 트리 아래에 저장되는 상태 있는 프로세스 구조를 함께 지정하는 실행 애플리케이션입니다. 명확성을 위해, 이 책 전체에서 OTP 애플리케이션을 다음과 같은 용어로 사용하겠습니다:
기본적으로 누구나 포함하는 두 개의 OTP 애플리케이션은 stdlib과 kernel입니다. stdlib은 list, maps 같은 코어 표준 라이브러리 모듈을 담은 라이브러리 애플리케이션이고, kernel은 실행 애플리케이션으로, OTP 애플리케이션에 의존해 동작하는 Erlang 시스템의 핵심 구조를 설정합니다.
노드가 부팅되면, 필요한 모든 OTP 애플리케이션의 모듈이 메모리에 로드됩니다. 그 다음 kernel이 시작됩니다. 이 시점부터 kernel이 시스템의 수명 주기를 관리합니다. 다른 모든 OTP 애플리케이션과 그 설정은 이를 통해 관리되고, 분산이나 핫 코드 업데이트 같은 고유한 기능도 마찬가지입니다. 운영체제의 비유로 돌아가자면, kernel OTP 애플리케이션은 Linux 커널에 대한 systemd(또는 systemd를 싫어하거나 BSD를 사용한다면 init—Windows 사용자에게는 다른 서비스를 실행하는 서비스)에 비유할 수 있습니다.
실제로 kernel과 stdlib은 기본적인 Erlang 셸을 동작시키는 데 필요한 유일한 두 애플리케이션입니다. erl을 입력(또는 Windows에서 werl을 시작)하면, VM이 부팅되고, kernel이 시작되며, stdlib이 사전 로드됩니다. 그 외의 모든 것은 선택 사항이며 나중에 로드할 수 있습니다.
표준 Erlang 배포판에는 다음과 같은 애플리케이션이 포함됩니다:
wx 위에 구축됨)이 모든 것은 Erlang 릴리스라고 불리는 형태로 함께 묶입니다. 릴리스는 OTP 애플리케이션 모음으로, 필요하면 가상 머신 전체 복사본과 함께 번들링되기도 합니다. 따라서 Erlang을 다운로드해 설치하면, 보통 Erlang/OTP-21.3.4 같은 이름의 릴리스를 받게 됩니다. 표준 배포판의 일부 OTP 애플리케이션을 취하고, 여기에 여러분이 만든 애플리케이션을 더해 자체 릴리스를 만들 수도 있습니다.
예를 들어 ssh와 ssl(각각이 public_key, crypto, stdlib, kernel에 의존)을 사용하는 proxy라는 앱을 작성한다면, 다음과 같은 구성 요소들을 모두 포함한 릴리스를 만들 것입니다:
이에 대한 시각적 표현은 그림 1에서 볼 수 있습니다.

그림 1: proxy 릴리스를 구성하는 시각적 표현
본질적으로, Erlang 시스템을 빌드한다는 것은 VM을 다시 번들링하면서, 기본 배포판이 제공하는 일부 표준 애플리케이션과 여러분의 앱과 라이브러리를 함께 묶는 일입니다.
커뮤니티가 개발하고 사용하는 표준 도구인 Rebar3는 여러분이 작성하고 배포하는 것이 OTP 애플리케이션이라는 전제 위에서 동작하며, 이에 필요한 모든 기능을 포함합니다. 이는 많은 프로그래밍 언어가 여러분에게 파일 어딘가에 main()이라는 함수를 두기만을 요구하는 것과 큰 차이입니다. 이런 이유로 이 프로그래밍 언어를 종종 단순히 ‘Erlang’이 아니라 Erlang/OTP라고 부릅니다. 단지 프로그래밍 언어가 아니라, 여러분이 하는 모든 일에 기본 구조를 요구하는 일반적인 개발 프레임워크이기 때문입니다.
그리고 임베디드 소프트웨어, 블록체인 시스템, 분산 데이터베이스를 작성하든 모두가 이를 따릅니다. OTP 아니면 아무것도 아닙니다. 다른 언어들은 보통 시작할 때는 아무 구체적인 요구 사항이 없지만, 나중에(패키지 매니저와 통합할 때처럼) 몇 가지 요구 사항을 추가하곤 합니다. 반면 Erlang—그리고 그 전체 커뮤니티—은 여러분이 그냥 OTP 애플리케이션을 작성하리라 기대하며, 나머지 도구들은 그것을 처리할 수 있습니다.
따라서 Erlang을 빠르게 시작하는 핵심은, 종종 고급 주제로 분류되는 이 프레임워크를 아는 것입니다. 여기서는 완전히 동작하는 릴리스에서 출발한 다음, 그 구조를 파고드는 방식으로 거꾸로 진행하겠습니다. 다음 장에서는 이러한 요구 사항 안에서 어떻게 작업하는지 이해하는 데 집중할 것입니다.