Skip to content

필사 모드: 모던 Elixir & Phoenix 2026 — Elixir 1.18 / Phoenix 1.8 / LiveView 1.0 / Oban / Broadway / Bumblebee / Nx / Livebook 심층 가이드

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — 30년 묵은 VM이 2026년에 다시 뜨는 이유

Elixir는 새로운 언어가 아니다. 2012년 José Valim이 Erlang/OTP 위에 사람이 쓸 만한 문법을 얹어 시작했다. 그리고 그 Erlang은 1986년 에릭슨에서 전화 교환기를 위해 태어났다 — 즉 BEAM(Erlang VM)은 사실상 마흔 살이다.

2026년, 그 마흔 살짜리 VM이 다시 뜨고 있다. 이유는 한 줄로 말할 수 있다.

**"동시성·분산·내고장성을 처음부터 박아둔 VM은 BEAM 하나뿐이고, 멀티코어/실시간/AI 추론 시대가 결국 그쪽 문법을 강제한다."**

- **Elixir 1.18(2024년 12월)** — set-theoretic types의 첫 프리뷰. 점진적 정적 타입의 길이 열렸다.

- **Phoenix 1.8(2024년 12월)** — Scope API로 인증 컨텍스트를 함수 시그니처에 박았고, daisyUI/Tailwind v4가 기본이 됐다.

- **LiveView 1.0(2024년 11월)** — 5년의 베타 끝에 정식 1.0. 서버 렌더 HTML과 실시간 양방향 통신이 한 추상 안에 들어왔다.

- **Bumblebee + Nx + Axon** — Hugging Face Transformers를 Elixir에서 그대로 돌리고, GPU 가속까지 받는다.

- **Tidewave(2025년 9월)** — José Valim이 공개한 Elixir용 AI 추론 플랫폼. BEAM 위에서 LLM 서빙을 한다.

이 글은 2026년의 Elixir 스택을 처음부터 끝까지 — VM의 액터 모델부터 LiveView, Oban, Broadway, Membrane, Bumblebee, Tidewave, 그리고 한국·일본의 실제 채택 사례까지 — 한 호흡으로 정리한다.

1장 · 왜 BEAM인가 — 액터·소프트 리얼타임·내고장성

먼저 한 가지를 분명히 하자. Elixir가 매력적인 이유의 절반은 **언어 그 자체**가 아니라 **그 아래의 VM**이다.

BEAM의 세 가지 핵심.

1. **경량 프로세스(액터)** — 한 노드 안에 수백만 개의 프로세스가 동시에 산다. 각 프로세스는 메모리(기본 ~2KB 힙)와 메일박스를 가진 격리된 단위다. OS 스레드가 아니다.

2. **선점형 스케줄링** — 스케줄러가 일정 reduction 수마다 프로세스를 강제로 스왑한다. 한 프로세스가 무한 루프를 돌아도 다른 프로세스가 굶지 않는다 — "소프트 리얼타임".

3. **let it crash + supervisor 트리** — 한 프로세스가 죽으면 격리된 단위 안에서만 죽고, 위에 있는 supervisor가 미리 정해진 전략(one_for_one, rest_for_one 등)으로 재시작한다. 방어 코드 대신 회복 코드.

그림으로 보면 이렇다.

[Application Supervisor]

|

+-------------+-------------+

| | |

[DB Pool Sup] [Web Sup] [Worker Sup]

| | |

[Pool Worker] [Endpoint] [Job Worker x N]

|

[LiveView Conn x M]

이 트리 한 장이 Elixir 앱의 운영을 거의 다 설명한다. 한 LiveView 연결이 죽어도 그 연결만 죽고, 다른 사용자는 영향이 없다. DB 풀이 흔들리면 Pool Sup이 재시작한다. 워커 하나가 OOM이 나도 Worker Sup이 새로 띄운다.

**기억할 한 줄**: "BEAM은 죽지 않는 VM이 아니라, **죽어도 괜찮은** VM이다."

2장 · Elixir 1.18 (2024년 12월) — set-theoretic types 프리뷰

2024년 12월 19일 릴리스된 Elixir 1.18은 그 자체로도 충분히 큰 사건이다.

2.1 set-theoretic types — 점진적 정적 타입의 첫 발

오랫동안 Elixir 사용자가 가장 자주 받은 질문은 "왜 정적 타입이 없냐"였다. 답은 두 가지였다 — (1) Dialyzer 있다 (2) BEAM의 동시성과 hot code reload는 동적 타입과 잘 맞는다. 하지만 그 두 답이 충분치 않다는 걸 코어 팀이 받아들였다.

1.18은 그 첫 번째 답이다. set-theoretic types를 함수 시그니처와 패턴 매칭에 적용하기 시작했다. union(`A or B`)·intersection(`A and B`)·negation(`not A`)을 1급 시민으로 다루는 타입 시스템이다.

컴파일러가 자동으로 추론한다 — 어노테이션이 없어도

defmodule Numbers do

def add(x, y) when is_integer(x) and is_integer(y), do: x + y

def add(x, y) when is_float(x) and is_float(y), do: x + y

end

잘못된 호출은 컴파일 시점에 잡힌다

Numbers.add(1, 2.0)

warning: incompatible types — integer 와 float 의 union 에 모두 속하지 않음

지금은 아주 한정된 범위(시그니처·패턴 매칭·일부 가드)지만, 로드맵상 1.19~1.20에서 본격적인 타입 어노테이션 문법이 들어온다.

2.2 mix 작업 개선

1.18은 mix 쪽도 손을 봤다. `mix test --partitioned`가 안정화됐고, `mix format`이 prettier처럼 import 정렬까지 다룬다. `mix deps.tree`가 SAT 솔버 진단을 출력해 의존성 충돌의 원인을 알려준다.

2.3 Elixir 버전 정책

Elixir는 1.x를 길게 가져간다. 2.0이 나올 가능성은 거의 없다. 대신 1.x가 매년 12월쯤 마이너 릴리스를 한다 — 1.15(2023), 1.16(2024 초), 1.17(2024 중), 1.18(2024 말), 그리고 1.19가 2025년 12월 예정이다.

3장 · Phoenix 1.8 (2024년 12월) — Scope API, daisyUI 기본

Phoenix는 Elixir의 웹 프레임워크다. Rails 비유로 시작하지만 — 컨트롤러·뷰·라우터의 구조는 비슷하다 — 핵심은 다르다. Phoenix는 **LiveView**를 기본으로 깔고, **Plug** 미들웨어 위에 올라가고, **PubSub**으로 노드 간 메시지를 라우팅한다.

3.1 Scope API — 인증 컨텍스트를 함수 시그니처에 박는다

1.8의 가장 큰 변화는 Scope다. 기존에는 인증된 사용자를 `conn.assigns.current_user`에서 꺼냈고, 이게 컨트롤러 안에서만 통했다. Scope는 그 사용자(와 그 사용자가 속한 organization, tenant 등)를 첫 인자로 명시적으로 받는다.

defmodule MyApp.Posts do

alias MyApp.Accounts.Scope

스코프를 첫 인자로 — 인증 컨텍스트가 시그니처에 박힌다

def list_posts(%Scope{user: user, org: org}) do

Post

|> where(org_id: ^org.id)

|> where(author_id: ^user.id)

|> Repo.all()

end

def create_post(%Scope{} = scope, attrs) do

%Post{}

|> Post.changeset(attrs)

|> Ecto.Changeset.put_change(:org_id, scope.org.id)

|> Repo.insert()

end

end

이게 왜 중요한가? **컨트롤러뿐 아니라 LiveView, Oban 잡, 백그라운드 작업, GenServer까지 같은 인증 컨텍스트를 일관되게 받는다.** 기존에는 LiveView가 mount 시점에 user를 따로 assign 해야 했고, Oban 잡은 args에 user_id를 넣어 다시 fetch 해야 했다.

3.2 daisyUI + Tailwind v4 기본

1.8부터 `mix phx.new`의 기본 UI 스택이 Tailwind v4 + daisyUI다. Heroicons도 기본. 별다른 설정 없이 즉시 모던한 UI가 떠진다.

1.8 기본 템플릿

<.button class="btn btn-primary" phx-click="save">저장</.button>

<.input field={@form[:title]} class="input input-bordered" />

이전엔 Tailwind만 깔고 컴포넌트는 직접 만들어야 했다. 1.8은 daisyUI 컴포넌트 클래스(btn, card, modal, alert 등)를 바로 쓴다.

3.3 새 프로젝트 만들기

mix archive.install hex phx_new

mix phx.new my_app --live # LiveView 포함

cd my_app

mix ecto.create

mix phx.server

이 네 줄로 LiveView·Tailwind v4·daisyUI·Ecto·Postgres가 다 깔린 프로젝트가 4000번 포트에서 떠 있다.

4장 · LiveView 1.0 — 5년의 베타 끝에 정식 출시

LiveView는 Elixir의 시그니처 기술이다. 2018년 ElixirConf에서 Chris McCord가 데모를 한 이후, 5년 넘게 0.x 베타를 거쳐 2024년 11월 1.0이 정식 출시됐다.

4.1 LiveView가 푸는 문제

기존 SPA(React, Vue) 스택의 비용은 두 가지다.

1. **API 두 번 만들기** — REST/GraphQL 백엔드 + 그걸 호출하는 프런트.

2. **상태 동기화** — 서버 상태와 클라이언트 상태가 따로. 그 차이가 모든 버그의 근원.

LiveView는 이 둘을 없앤다. **상태는 서버에만 있다. UI는 서버에서 렌더된 HTML이 WebSocket으로 푸시된다.** 사용자 이벤트(click, submit, keypress)는 WebSocket으로 서버로 가고, 서버는 상태를 바꾸고 변경된 DOM 일부만 다시 push 한다.

defmodule MyAppWeb.CounterLive do

use MyAppWeb, :live_view

def mount(_params, _session, socket) do

{:ok, assign(socket, count: 0)}

end

def handle_event("inc", _params, socket) do

{:noreply, assign(socket, count: socket.assigns.count + 1)}

end

def render(assigns) do

~H"""

"""

end

end

이게 끝이다. 100ms 안에 서버 왕복이 일어나고, 변경된 텍스트 노드만 patch 된다. JS는 한 줄도 안 썼다.

4.2 1.0의 핵심 변화

- **Streams** — 대량 리스트의 효율적 갱신. 추가/삭제/이동을 서버에서 명시하고, 클라이언트는 DOM 키 기반으로만 처리한다. 무한 스크롤 뉴스피드를 메모리 폭발 없이 만든다.

- **Async assigns** — `assign_async(:user, fn -> ... end)`로 비동기 데이터 로딩이 1급 시민이 됐다. Suspense 비슷한 UX.

- **Function components + slots** — React의 children prop과 같은 슬롯 패턴이 안정화됐다.

- **JS Commands** — 단순 토글/숨김/포커스는 서버 왕복 없이 `JS.toggle("#menu")` 같은 클라이언트 사이드 명령으로 처리.

4.3 LiveView가 잘 안 맞는 경우

LiveView가 만능은 아니다.

- **오프라인 우선 PWA** — WebSocket 끊기면 못 쓴다.

- **극단적 인터랙티브 UI** — 60fps 드래그/스크럽처럼 ms 단위 응답이 필요한 곳은 클라이언트 사이드가 더 낫다.

- **CDN 끝단 렌더링** — 서버 상태가 필요하므로 edge에 뿌리기 어렵다.

이런 경우엔 **LiveView Hooks**로 일부만 클라이언트 JS로 빼거나, 차라리 React로 가는 게 맞다.

5장 · Ecto — DB 레이어의 표준

Ecto는 Elixir의 ORM이 아니라 **DB 쿼리·검증·마이그레이션 라이브러리**다. ActiveRecord 비교는 표면적이고, 본질은 다르다.

Ecto의 네 가지 모듈.

1. **Repo** — DB 풀과 실행 인터페이스. `Repo.all`, `Repo.insert`, `Repo.transaction`.

2. **Schema** — 테이블과 Elixir 구조체의 매핑. ActiveRecord와 달리 **모델이 아니라 매핑만**이다.

3. **Changeset** — 입력 검증·변환의 파이프라인. 이게 Ecto의 진짜 무기다.

4. **Query** — 컴파일 타임에 검사되는 DSL. SQL 그대로보다 안전.

5.1 Changeset — 진짜 무기

Changeset은 "이 입력을 받아서, 이런 검증을 거쳐, 이런 모양으로 DB에 넣는다"를 한 파이프라인으로 표현한다.

def changeset(post, attrs) do

post

|> cast(attrs, [:title, :body, :published_at])

|> validate_required([:title, :body])

|> validate_length(:title, min: 3, max: 200)

|> validate_format(:title, ~r/^[^<>]+$/)

|> unique_constraint(:slug)

|> put_change(:slug, slugify(attrs["title"]))

end

이게 단순히 폼 검증이 아니다. **모든 DB 쓰기가 changeset을 거친다.** 그래서 비즈니스 규칙 위반은 컴파일 타임이 아니라 데이터 레이어에서 한 곳에서 차단된다.

5.2 Multi — 트랜잭션 빌더

복잡한 트랜잭션은 `Ecto.Multi`로.

Multi.new()

|> Multi.insert(:post, Post.changeset(%Post{}, attrs))

|> Multi.update(:user, User.bump_post_count(user))

|> Multi.insert(:notification, fn %{post: post} ->

Notification.new_post(post)

end)

|> Repo.transaction()

각 단계는 이전 단계의 결과를 받을 수 있고, 어디서 실패해도 전체 롤백된다.

5.3 Ecto 3 — 표준 자리잡음

2024~2026 사이 Ecto는 안정기다. 3.x가 사실상 표준이고, 큰 변화 없이 점진적 개선만 일어나고 있다. Postgres 배열·jsonb·LISTEN/NOTIFY, MySQL 8, SQLite — 다 잘 지원한다.

6장 · Oban — 잡 처리, 그리고 Pro 999달러 논란

Oban은 Elixir 진영의 표준 잡 큐다. **Postgres 기반**이라는 게 핵심이다 — Sidekiq처럼 Redis를 따로 쓰지 않고, 이미 있는 Postgres에 잡 테이블 하나를 더 둔다.

6.1 왜 Postgres 기반인가

- **트랜잭션 한 번에 비즈니스 데이터 + 잡 enqueue가 같이 커밋된다.** Sidekiq는 "Redis에 큐 넣고 DB에 데이터 저장" 사이에 실패하면 데이터 따로/잡 따로가 된다. Oban은 둘 다 같은 트랜잭션.

- **운영 도구가 SQL이다.** 잡 상태를 쿼리로 직접 본다. 별도 어드민 UI가 필수가 아니다.

- **백업·복제·HA가 Postgres 그대로.** 잡 큐를 위한 별도 인프라가 없다.

잡 정의

defmodule MyApp.Workers.EmailWorker do

use Oban.Worker, queue: :emails, max_attempts: 5

@impl Oban.Worker

def perform(%Oban.Job{args: %{"user_id" => user_id, "kind" => kind}}) do

user = Repo.get!(User, user_id)

MyApp.Mailer.deliver(user, kind)

:ok

end

end

enqueue — 비즈니스 로직과 같은 트랜잭션

Multi.new()

|> Multi.insert(:user, User.changeset(%User{}, attrs))

|> Oban.insert(:email_job, fn %{user: user} ->

EmailWorker.new(%{user_id: user.id, kind: "welcome"})

end)

|> Repo.transaction()

6.2 Oban Pro 999달러 논란

Oban 본체는 무료/오픈소스다. 하지만 운영에 자주 필요한 기능들 — Cron, Batch Jobs(N개가 다 끝나면 콜백), Workflow, Smart Engines — 은 Pro에 있다. 2024년 중반 Pro의 단일 라이선스 가격이 **연 999달러로 인상**되며 커뮤니티가 시끄러웠다.

찬반은 갈렸다.

- **찬성** — Oban 메인테이너 한 명이 풀타임으로 라이브러리에 매달려야 BEAM 진영에 이 정도 품질의 잡 큐가 유지된다. 999달러는 시니어 시간당 1~2시간 값.

- **반대** — Sidekiq Pro도 219달러/연이다. 999달러는 너무 비싸고, Pro에 있는 기능 중 일부는 오픈소스로 가야 했다.

결과적으로 커뮤니티는 Pro로 가는 곳, OSS 본체만으로 직접 짜는 곳, 그리고 Quantum + 직접 GenServer 조합으로 가는 곳으로 갈렸다. 2026년 시점에서 **새 프로젝트는 대부분 그냥 Pro로 간다** — 1년에 999달러는 시니어 한 명의 며칠 값.

7장 · Broadway — Kafka·RabbitMQ·AMQP·SQS 파이프라인

Oban이 "DB 잡 큐"라면 Broadway는 "데이터 파이프라인". 외부 메시지 시스템(Kafka, RabbitMQ, AMQP, AWS SQS, Google PubSub)에서 메시지를 끌어와, 배치 처리하고, 백프레셔를 관리하는 추상이다.

7.1 Broadway가 푸는 문제

스트리밍 데이터 처리에는 보통 다음이 필요하다.

1. **컨슈머 그룹** — 여러 노드가 동시에 같은 토픽을 나눠 읽는다.

2. **배치 처리** — 1건씩이 아니라 100건/1000건 단위로 묶어 처리.

3. **백프레셔** — 다운스트림이 느리면 업스트림에서 안 받는다.

4. **재시도/dead letter** — 실패한 메시지의 격리.

Broadway는 이걸 BEAM의 GenStage 위에 다 얹은 표준이다.

defmodule MyApp.KafkaPipeline do

use Broadway

def start_link(_opts) do

Broadway.start_link(__MODULE__,

name: __MODULE__,

producer: [

module: {BroadwayKafka.Producer, [

hosts: [localhost: 9092],

group_id: "my_group",

topics: ["events"]

]},

concurrency: 1

],

processors: [

default: [concurrency: 10]

],

batchers: [

default: [batch_size: 100, batch_timeout: 1_000]

]

)

end

@impl true

def handle_message(_, message, _) do

메시지 1건 변환

message

end

@impl true

def handle_batch(:default, messages, _, _) do

100건 한 번에 DB INSERT

Enum.map(messages, &Message.update_data(&1, transformed(&1)))

end

end

이게 다다. Kafka·RabbitMQ·SQS·PubSub은 producer 모듈만 갈아끼우면 된다.

7.2 사례 — 이벤트 ETL

쇼핑몰에서 주문 이벤트(초당 수천 건)를 Kafka에서 읽어, 풍부화(enrichment)하고, ClickHouse에 적재하는 파이프라인. Broadway로 짜면 한 BEAM 노드에서 producer 1, processor 64, batcher 8로 초당 만 건 가뿐히 처리한다. 노드 추가하면 컨슈머 그룹이 알아서 파티션을 재분배한다.

8장 · Membrane — 오디오/비디오 파이프라인

Membrane은 Software Mansion에서 만든 오디오/비디오 처리 프레임워크다. WebRTC SFU(Jitsi/LiveKit 같은 것)부터 라이브 스트리밍 트랜스코딩, RTSP 카메라 통합, HLS 분절 생성까지 한다.

8.1 왜 BEAM이 미디어 처리에 맞는가

- **동시 스트림 수천 개** — 각 스트림이 별도 프로세스. 한 통화가 끊겨도 다른 통화 영향 없음.

- **백프레셔 빌트인** — 처리가 느린 단계가 있으면 앞에서 안 보냄.

- **분산** — 여러 노드 사이에 부하 분산이 자연스럽다.

물론 코덱(H.264/VP9/Opus) 자체의 인코딩·디코딩은 C/Rust(Native Implemented Function, NIF)로 빠진다. Membrane은 그 파이프라인을 조립하고, 라이프사이클을 관리하고, 흐름을 제어한다.

8.2 간단 파이프라인

defmodule MyApp.AudioPipeline do

use Membrane.Pipeline

@impl true

def handle_init(_ctx, opts) do

structure = [

child(:file_src, %Membrane.File.Source{location: opts.input}),

child(:mp3_decoder, Membrane.MP3.MAD.Decoder),

child(:opus_encoder, %Membrane.Opus.Encoder{}),

child(:rtp, Membrane.RTP.PayloadFormatResolver),

child(:udp_sink, %Membrane.UDP.Sink{destination: opts.dest})

]

{[spec: structure], %{}}

end

end

MP3 파일을 디코드해서 Opus로 인코드하고 RTP로 패킷화해 UDP로 보낸다. 노드는 다 별도 프로세스고, 백프레셔가 자동으로 흐른다.

8.3 실제 사례

- **Jellyfish** — Membrane이 만든 WebRTC SFU 서버. Zoom 같은 다자간 회의를 BEAM 위에서 돌린다.

- **Veedo** — Membrane 기반 라이브 스트리밍 플랫폼.

9장 · Igniter — 코드 변환 도구

Igniter는 비교적 새 프로젝트(2024년)다. Elixir 코드를 안전하게 자동 수정하는 도구로, 라이브러리 설치/업그레이드 시 보일러플레이트를 알아서 박아준다.

9.1 무엇을 푸는가

기존 `mix deps.get`은 라이브러리만 다운로드한다. 그 라이브러리를 쓰려면 `config/config.exs`에 설정 넣고, `application.ex`에 supervisor child를 넣고, router에 plug를 추가하고 — 이걸 다 README 보고 손으로 했다.

Igniter는 이걸 자동화한다. 라이브러리가 Igniter Recipe를 제공하면, `mix igniter.install <lib>`이 AST 레벨로 안전하게 모든 파일을 수정한다.

mix igniter.install ash_authentication

config/config.exs 자동 수정

lib/my_app/application.ex 에 supervisor child 자동 추가

lib/my_app_web/router.ex 에 라우트 자동 추가

9.2 직접 쓰는 경우

Igniter는 단순 라이브러리 설치뿐 아니라 마이그레이션 도구로도 쓰인다 — 예를 들어 LiveView 0.20에서 1.0으로 갈 때 deprecated API 자동 변환. Phoenix 1.7 → 1.8 마이그레이션 일부도 Igniter recipe로 제공된다.

10장 · Bumblebee — Elixir에서 HF Transformers

여기서부터가 "Elixir도 AI 한다"의 이야기다.

Bumblebee는 Hugging Face Transformers의 Elixir 포팅이다. 같은 모델 아티팩트(safetensors), 같은 토크나이저 설정을 그대로 읽어서 Elixir에서 추론한다.

10.1 BERT 한 줄 추론

{:ok, model} = Bumblebee.load_model({:hf, "bert-base-uncased"})

{:ok, tokenizer} = Bumblebee.load_tokenizer({:hf, "bert-base-uncased"})

serving = Bumblebee.Text.fill_mask(model, tokenizer)

Nx.Serving.run(serving, "The capital of [MASK] is Paris.")

=> [%{token: "France", score: 0.97}, ...]

이게 다다. CPU에서 잘 돈다. GPU 가속을 원하면 백엔드를 EXLA(XLA, JAX의 그것)로 바꾼다.

10.2 백엔드 — Nx → EXLA / Torchx

Bumblebee 자체는 Nx 위에 산다. Nx는 NumPy-like 텐서 API다. 실제 연산은 백엔드가 한다.

- **EXLA** — Google XLA를 통한 CPU/GPU/TPU. 가장 빠른 게 많다.

- **Torchx** — LibTorch(파이토치 코어)를 통한 CPU/GPU. PyTorch 모델 호환성이 좋다.

config/config.exs

config :nx, default_backend: EXLA.Backend

config :nx, default_defn_options: [compiler: EXLA]

10.3 Stable Diffusion, Whisper, Llama

Bumblebee가 지원하는 모델군은 꾸준히 늘었다.

- **Stable Diffusion XL** — 텍스트 → 이미지.

- **Whisper** — 음성 → 텍스트. 라이브 자막에 자주 쓴다.

- **Llama 3, Mistral, Gemma** — 7B~70B LLM. 양자화로 노트북에서도 돈다.

- **CLIP** — 이미지/텍스트 임베딩.

10.4 Phoenix와 묶기 — Nx.Serving

Bumblebee의 추론은 `Nx.Serving`이라는 추상 안에서 돈다. 이게 결정적이다 — Phoenix 앱의 supervisor 트리에 그냥 child로 박으면, 자동으로 **배치/큐잉/타임아웃**을 처리한다.

application.ex

children = [

MyAppWeb.Endpoint,

{Nx.Serving,

serving: build_whisper_serving(),

name: WhisperServing,

batch_size: 8,

batch_timeout: 200}

]

어디서든 호출

Nx.Serving.batched_run(WhisperServing, audio_input)

10개 동시 요청이 와도 `batch_size: 8`이 알아서 8개씩 묶어 GPU로 보낸다. GPU 활용률이 올라간다.

11장 · Nx + Axon + Explorer — BEAM 데이터 사이언스

Bumblebee 한 줄 추론을 봤으니, 그 아래의 스택을 보자.

11.1 Nx — NumPy for BEAM

Nx는 텐서 라이브러리. NumPy/PyTorch와 비슷한 API.

defn softmax(t) do

Nx.exp(t) / Nx.sum(Nx.exp(t))

end

Nx.tensor([1.0, 2.0, 3.0]) |> softmax()

=> Nx.Tensor<f32[3] [0.09, 0.24, 0.67]>

`defn`은 **숫자 전용 함수**다. 일반 Elixir 함수가 아니라 JIT 컴파일된다 — XLA로 GPU/TPU에서 돌고, EXLA로 컴파일된다.

11.2 Axon — 딥러닝 라이브러리

Axon은 Nx 위의 신경망 라이브러리. Keras 스타일의 함수형 API.

model =

Axon.input("input", shape: {nil, 784})

|> Axon.dense(128, activation: :relu)

|> Axon.dropout(rate: 0.2)

|> Axon.dense(10, activation: :softmax)

학습

model

|> Axon.Loop.trainer(:categorical_cross_entropy, :adam)

|> Axon.Loop.metric(:accuracy)

|> Axon.Loop.run(train_data, %{}, epochs: 10)

작은 모델은 BEAM 노드에서 직접 학습한다. 큰 모델은 PyTorch로 학습하고 Bumblebee로 서빙하는 게 일반적이다.

11.3 Explorer — DataFrames, Polars 백엔드

Explorer는 Pandas/Polars의 Elixir 버전. **내부적으로 Polars(Rust)를 NIF로 부른다**. 그래서 성능이 좋다.

require Explorer.DataFrame, as: DF

df = DF.from_csv!("sales.csv")

df

|> DF.filter(col("amount") > 100)

|> DF.group_by("region")

|> DF.summarise(total: sum(col("amount")), n: count(col("id")))

|> DF.sort_by(desc: "total")

이걸 Livebook 안에서 시각화하면 Jupyter 비슷한 데이터 분석 환경이 된다.

12장 · Livebook — Elixir 노트북

Livebook은 José Valim이 만든 Elixir용 Jupyter다. 단, Jupyter보다 두 가지가 다르다.

1. **협업이 1급** — 한 노트북을 여러 사람이 동시에 편집한다. Google Docs 비슷.

2. **버전 관리가 깨끗** — 노트북이 `.livemd`(markdown)다. git diff가 사람이 읽을 수 있다.

Mac/Linux 한 줄 설치

mix escript.install hex livebook

livebook server

http://localhost:8080

12.1 Livebook이 잘 맞는 곳

- **데이터 탐색** — DB 쿼리, Explorer, Vega-Lite 차트.

- **ML 추론 실험** — Bumblebee 모델을 노트북에서 띄워보고, 하이퍼파라미터 조정.

- **운영 노트북** — "프로덕션 DB에 이런 쿼리 한 번 돌려야 함"을 검토 가능한 형태로.

- **튜토리얼/교재** — `.livemd`는 실행 가능한 글이다.

12.2 Smart Cells

Smart Cell은 UI 폼으로 코드를 만드는 셀이다. 차트, DB 쿼리, 신경망 시각화 등이 폼 입력만으로 코드 셀이 생성된다.

[Smart Cell: SQL Query]

Connection: prod_replica

Query: SELECT * FROM users WHERE created_at > $1

Variables: cutoff_date = ~D[2026-01-01]

→ 셀 아래에 자동으로 Ecto/Postgrex 코드 생성

13장 · Tidewave (2025년 9월, José Valim) — Elixir AI 추론 플랫폼

2025년 9월, José Valim과 Dashbit 팀이 **Tidewave**를 공개했다. "BEAM 위의 LLM 서빙 플랫폼"이다.

13.1 무엇을 푸는가

LLM 서빙에는 다음이 필요하다.

1. **동시 추론 요청 수백~수천 개를 GPU에 잘 묶기** — vLLM이 푸는 문제.

2. **스트리밍 응답** — 토큰 단위로 클라이언트에 push.

3. **세션/상태 관리** — 멀티턴 대화, 컨텍스트.

4. **장애 격리** — 한 사용자의 잘못된 요청이 다른 사용자에게 영향 없도록.

5. **분산** — 여러 GPU 노드에 라우팅.

이 5가지 중 (3)(4)(5)는 **BEAM이 30년 전부터 잘하던 것**이다. 나머지 (1)(2)도 Nx.Serving + Phoenix 채널 위에 자연스럽게 얹힌다.

13.2 아키텍처

Tidewave는 대략 이렇게 생겼다.

[Phoenix Endpoint]

|

+-------+-------+

| |

[HTTP /chat] [WS /stream]

| |

v v

[Session Sup] [Stream Channel]

| |

v v

[Session GenServer x N]

|

v

+---------+---------+

| |

[Nx.Serving] [Tool Runtime]

| |

v v

[GPU Workers] [DB / HTTP / RAG]

각 사용자 세션이 별도 GenServer다. 한 세션이 꼬여도 다른 세션에 영향 없다. Nx.Serving이 GPU 배치를 관리한다. 툴 호출은 별도 supervisor 트리.

13.3 vLLM 대비

Tidewave는 vLLM과 정면 경쟁한다기보단 **다른 곳을 푼다**. vLLM은 GPU 효율 1등. Tidewave는 GPU 효율은 vLLM에 못 미치지만, **운영성·세션 관리·장애 격리·분산**이 BEAM 강점.

선택 기준은 단순하다.

- **단일 노드에서 짧은 응답을 최대 처리량으로** — vLLM.

- **다중 세션·장시간 대화·복잡 툴 호출·고가용성** — Tidewave.

14장 · BEAM 운영 — Observer, recompile, hot reload

Elixir 앱을 운영해 본 사람만 아는 즐거움이 몇 가지 있다.

14.1 Observer — 라이브 노드 들여다보기

iex --sname dev --remsh my_app@hostname

iex> :observer.start()

GUI가 뜬다. 프로세스 트리, 메시지 큐 길이, 메모리 사용량, CPU 스케줄러 상태 — 다 라이브로 본다. 한 프로세스의 메일박스가 비정상적으로 길면 거기서 병목을 찾는다.

14.2 IEx 원격 셸

iex --sname remote@host --cookie SECRET --remsh prod@host

프로덕션 노드에 원격 IEx 셸을 띄운다. 거기서 `Repo.get(User, 1)`, `Phoenix.PubSub.broadcast(...)` 다 된다. **운영 디버깅의 끝판왕**.

14.3 Hot code reload

iex> r MyApp.SomeModule

운영 중인 노드에 모듈을 재컴파일/재로드한다. 전체 재시작 없이 한 함수만 바꿔도 즉시 반영. **단, 프로덕션에서 이걸 쓰는 건 신중히**. 보통은 새 릴리스를 배포해 rolling restart 한다.

15장 · 한국·일본의 Elixir 채택

Elixir는 거대 기업의 메인 스택은 아니지만, 특정 영역에서 강하다.

15.1 한국

- **토스(Toss)** — 핀테크 메인 스택은 Kotlin/JVM이지만, 일부 실시간 시스템(알림, 채팅, 일부 결제 워크플로)에 Elixir/Phoenix가 들어가 있다고 알려져 있다.

- **BabyDragon(베이비드래곤)** — 한국 모바일 게임 회사. Elixir/Phoenix를 게임 백엔드와 매칭 서버에 광범위하게 쓴다.

- **Banksalad** — 가계부 앱. 초기에 일부 백엔드를 Elixir로 했다는 자료가 있다.

- **개인 개발자/스타트업** — 실시간 채팅·협업·게임 백엔드를 단일 BEAM 노드로 처리하는 작은 팀들이 꾸준히 있다.

한국에서 Elixir의 보급은 더디다. 1순위 이유는 **채용 풀이 작다**는 것. JVM/Kotlin/Node 풀에 비해 한국어 자료도 적다. 하지만 한 번 적용한 팀은 잘 이탈하지 않는다.

15.2 일본

- **ドリコム(Drecom)** — 모바일 게임 회사. 게임 서버 일부에 Elixir/Phoenix.

- **DMM** — 일부 실시간 시스템.

- **任天堂(Nintendo)** — 공식 발표는 적지만, 일부 온라인 서비스의 백엔드에 BEAM 계열이 들어가 있다고 알려져 있다(Erlang 포함).

- **Cookpad** — Ruby 본진이지만 일부 백엔드 실험에 Elixir를 본 자료가 있다.

- **사이버에이전트(CyberAgent)** — 광고/AdTech의 일부 실시간 부분.

일본은 Erlang 전통이 일찍부터 있었다(NTT·Ericsson 일본 영향). 그래서 Elixir로의 이행이 한국보다 자연스럽다. 일본어 자료(Qiita, Zenn)도 한국어보다 풍부.

15.3 전 세계

- **Discord** — Erlang/Elixir 메시지 라우팅. 한 채널에 수백만 동시 접속을 BEAM 클러스터로 처리.

- **WhatsApp** — Erlang. 메타 인수 전까지 작은 팀이 수억 사용자를 운영한 전설.

- **Pinterest** — 일부 알림/SMS 시스템.

- **Heroku** — Routing layer를 한때 Erlang으로.

- **Bleacher Report** — 트래픽 스파이크 대응을 Elixir 하나로.

16장 · 누가 Elixir를 골라야 하나

마지막 장. 결정 매트릭스.

Elixir/Phoenix가 잘 맞는 경우

1. **실시간 동시성이 핵심** — 채팅, 게임, 라이브 스트림, 협업 도구, 트레이딩.

2. **장기간 연결을 많이 유지해야** — WebSocket 수만 개, IoT 디바이스 수만 대.

3. **소프트 리얼타임·내고장성이 요구사항** — 통신, 결제 알림, 미디어.

4. **작은 팀이 풀스택으로 빠르게** — LiveView 한 추상으로 백+프 동시 처리.

5. **메시지 파이프라인** — Broadway로 Kafka/SQS/RabbitMQ ETL.

Elixir가 잘 안 맞는 경우

1. **CPU-bound 무거운 단일 계산** — JS 엔진/네이티브 코드보다 BEAM이 느리다. NIF으로 빠지긴 하지만 단순한 건 Go/Rust가 낫다.

2. **거대한 ML 학습 메인 스택** — PyTorch 생태계가 압도적. Elixir는 서빙·MLOps 보조에 가깝다.

3. **풍부한 라이브러리 생태계가 핵심** — npm/PyPI 같은 양은 없다. Hex 패키지 수는 1만 단위.

4. **채용/팀 구성이 1순위 제약** — 한국에서 Elixir 시니어를 단기간에 5명 뽑기는 어렵다.

한 줄 가이드

**"동시 연결 1만 개 이상, 또는 실시간이 핵심, 또는 운영 안정성이 1순위라면 Elixir를 고민할 가치가 있다. CRUD 위주 SaaS라면 Phoenix는 매력적이지만 Rails/Django/Next로도 충분히 잘 된다."**

17장 · 참고 / References

- Elixir 공식: https://elixir-lang.org/

- Elixir 1.18 릴리스 노트: https://elixir-lang.org/blog/2024/12/19/elixir-v1-18-0-released/

- Phoenix Framework: https://www.phoenixframework.org/

- Phoenix 1.8 릴리스 노트: https://www.phoenixframework.org/blog/phoenix-1.8-released

- Phoenix LiveView: https://github.com/phoenixframework/phoenix_live_view

- LiveView 1.0 발표: https://www.phoenixframework.org/blog/phoenix-liveview-1.0-released

- Ecto: https://github.com/elixir-ecto/ecto

- Oban: https://github.com/oban-bg/oban

- Oban Pro: https://oban.pro/

- Broadway: https://github.com/dashbitco/broadway

- Membrane Framework: https://membrane.stream/

- Igniter: https://github.com/ash-project/igniter

- Bumblebee: https://github.com/elixir-nx/bumblebee

- Nx: https://github.com/elixir-nx/nx

- Axon: https://github.com/elixir-nx/axon

- Explorer: https://github.com/elixir-explorer/explorer

- Livebook: https://livebook.dev/

- Tidewave: https://tidewave.ai/

- Dashbit(José Valim 회사): https://dashbit.co/

- Fly.io Elixir 가이드: https://fly.io/docs/elixir/

- BEAM Book(VM 내부): https://github.com/happi/theBeamBook

- Erlang Solutions 블로그: https://www.erlang-solutions.com/blog/

- ElixirConf US: https://elixirconf.com/

- ElixirConf EU: https://www.elixirconf.eu/

- Code BEAM: https://codebeamamerica.com/

- ElixirForum: https://elixirforum.com/

- Awesome Elixir: https://github.com/h4cc/awesome-elixir

- Thinking Elixir 팟캐스트: https://thinkingelixir.com/

- BeamCommunity Slack: https://erlef.org/slack-invite

- Discord 엔지니어링 블로그: https://discord.com/category/engineering

- WhatsApp Erlang 사례(과거): https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf

- ドリコム TechCon Elixir 트랙: https://tech.drecom.co.jp/

- 토스 기술 블로그: https://toss.tech/

- BabyDragon: https://babydragon.com/

현재 단락 (1/383)

Elixir는 새로운 언어가 아니다. 2012년 José Valim이 Erlang/OTP 위에 사람이 쓸 만한 문법을 얹어 시작했다. 그리고 그 Erlang은 1986년 에릭슨에서...

작성 글자: 0원문 글자: 17,548작성 단락: 0/383