2P by GN⁺ | ★ favorite | 댓글 1개
  • 작은 Phoenix 웹 프로젝트를 준비하던 과정에서, Elixir는 단순한 Ruby풍 Erlang이 아니라 BEAM 위의 Lisp적 언어로 볼 때 설계가 잘 맞아떨어짐
  • Erlang/OTP의 메시지 전달, 불변 상태, 실패 복구, 런타임 검사 위에 Elixir는 중첩 모듈, 개선된 문자열, 큰 표준 라이브러리, 강한 매크로 체계를 더함
  • scope, pipeline, def, defmodule, if 같은 구조는 특수 문법처럼 보이지만, 호출 문법과 연관 리스트, do...end 문법 설탕, 매크로 확장이 쌓인 결과임
  • Phoenix 라우터 DSL은 언어에 내장된 예외가 아니라 scope, pipeline, plug, pipe_through 같은 매크로 기반 코드 생성으로 구성됨
  • Elixir의 “마법”은 숨겨진 동작보다 드러난 메타프로그래밍에 가까우며, use, sigil, dbg!, pry, |>가 BEAM의 검사·복구·동시성 도구와 결합됨

Phoenix에 앞서 Elixir를 읽는 이유

  • 목표는 작은 프로젝트에 Phoenix 웹 프레임워크를 적용하는 것이지만, Phoenix가 Elixir로 작성되어 있어 언어 자체를 먼저 이해해야 함
  • 이 글의 역할은 Elixir나 Phoenix 튜토리얼이 아니라, 공식 문서를 보완해 “어떻게 생각할지”를 잡는 데 있음
  • 기준 환경은 2024년 5월의 Elixir 1.14, Erlang/OTP 25, Phoenix 1.7.7임
  • 출발점에는 Erlang과 웹 프로그래밍 경험 부족, 웹 프로그래밍에 대한 낮은 선호, 장애를 많이 고쳐 온 사람의 비관적이고 통제 지향적인 태도가 깔려 있음

Erlang/OTP와 Elixir가 주는 기본 매력

  • Erlang/OTP 플랫폼은 독립 프로세스들이 메시지로 통신하는 다중 처리 모델을 제공함
    • 대부분의 상태가 불변이므로 프로세스는 메시지를 통해서만 상호작용함
    • 프로세스 실패 처리, 재시작, 런타임 검사, 런타임 코드 변경을 위한 도구가 있음
  • 실행 기반은 BEAM VM이며, 일반적인 Unix 프로세스 모델만으로는 같은 수준의 견고성과 내부 관찰성을 얻기 어렵다는 문제의식이 있음
  • Erlang은 1980년대 후반 Prolog에 Lisp 요소가 섞여 만들어진 독특한 언어이고, Elixir는 2012년에 시작된 더 새롭고 관습적으로 보이는 언어로 BEAM에 컴파일되며 Erlang 코드와 호환성이 좋음
  • 처음에는 Elixir가 Erlang을 Ruby처럼 다시 입힌 언어처럼 보이지만, 실제로는 여러 의미 있는 개선을 제공함

Elixir가 Erlang 위에 더하는 것

  • 중첩 모듈

    • Erlang은 평평한 모듈 네임스페이스를 사용해 foo 안에 bar를 넣어 foo.bar 같은 구조를 만들 수 없음
    • 그래서 실제 코드에서는 foo_bar 같은 이름으로 중첩을 흉내 내는 경우가 생김
  • 문자열 기본값 개선

    • Erlang 문자열은 기본적으로 문자들의 연결 리스트이며, 이는 1980년대 통신 인프라 언어에는 더 자연스러웠을 수 있음
    • Erlang의 bit-string은 임의 바이트의 불변 배열이고 ASCII나 UTF-8 데이터를 담을 수 있음
    • Elixir는 기본 문자열을 불변 배열로 두고, Erlang 코드가 기대하는 연결 리스트 문자열은 별도로 만들 수 있음
    • Elixir 문자열은 기본적으로 UTF-8을 가정하지만, Erlang bit-string은 특정 인코딩을 가정하지 않음
  • 자료구조 문법

    • Erlang의 records는 여전히 튜플 주변의 매크로에 가깝고, Elixir의 Record 모듈은 이와의 상호운용을 도움
    • Erlang과 Elixir 모두 불변 키-값 딕셔너리인 maps를 갖고 있음
    • Elixir structs는 maps에 컴파일 타임 검사를 더한 형태임
  • 더 큰 표준 라이브러리

    • Elixir는 Erlang 표준 라이브러리 위에 추가 기능을 제공함
  • 더 강한 매크로

    • Erlang 매크로도 강력할 수 있지만 정리된 C 전처리기에 가까우며, 보통 상수 정의, include, 기본 조건부 컴파일에 쓰임
    • Elixir는 매크로를 언어 사용의 중심에 더 적극적으로 배치함

처음 Elixir가 불편하게 보이는 지점

  • Elixir는 Ruby처럼 느슨하고 유연해 보이며, 튜토리얼 초반부터 같은 코드를 여러 방식으로 쓸 수 있는 문법 설탕이 많이 등장함
  • Erlang은 작은 수의 구성 요소가 잘 맞물리는 단순한 언어처럼 보이는 반면, Elixir는 여러 층위에서 더 많은 요소를 가진 큰 언어처럼 느껴짐
  • Elixir 프로젝트는 단일 스크립트나 작은 파일 묶음으로도 만들 수 있지만, 문서는 빌드 도구 mix, OTP 애플리케이션 설계, GenServer 사용법으로 빠르게 들어감
  • mix를 통한 시작은 cargo, zig 같은 도구보다 익숙해지기 더 복잡하게 느껴짐
  • Elixir와 Phoenix의 소개 방식은 친절하고 반짝이는 기능 설명이 많아, 복잡성을 감수할 만큼 가치가 있는지 의심하게 만듦
  • 근본적 거부감은 Magic은 결국 깨진다는 경험에서 나옴
    • 분산 서비스의 자동 탐색은 중앙 인덱스 없이 네트워크 노드를 찾는 마법처럼 보였지만, 실제로는 알려진 UDP 포트부터 순차적으로 스캔하는 방식이었음
    • 원격 점검 GUI는 기존 오픈소스 프로그램이 수집한 정보를 websocket으로 전달하는 방식이었음
    • 프론트엔드 프레임워크 없이 실시간 갱신되는 화면은 5초마다 새로고침되는 iframe이었음
    • RPC 메시지가 자동으로 맞물리는 프로토콜에는 메시지 순서와 기대값에 관한 문서화되지 않은 가정이 많았음

전환점: Elixir는 Lisp처럼 이해됨

  • Elixir를 파고들수록 핵심은 Elixir is actually a Lisp에 가까워짐
  • 괄호는 없지만, Common Lisp을 유연하고 강력하며 동적이고 메타프로그래밍 중심으로 만드는 많은 요소를 갖고 있음
  • Elixir는 Erlang의 장점도 유지함
    • 진짜 불변성
    • 광범위한 패턴 매칭
    • 심볼릭 프로그래밍
    • 선택적·점진적 타입 관련 기능
  • BEAM은 내부 검사, 수정, 탐색이 쉬운 면에서 Lisp 런타임에 가까운 성격을 가짐
  • Erlang은 런타임 라이브러리 깊숙이 들어가기 전까지 이런 내부 검사 능력을 적극 활용하기 쉽지 않지만, Elixir는 시작부터 그 힘을 활용함
  • Phoenix는 크고 실제적인 Elixir 프로그램과 라이브러리를 어떻게 구성하는지 볼 수 있는 사례가 됨

Phoenix 라우터 DSL의 정체

  • 기본 Elixir 예제는 defmodule, def, IO.puts 같은 구조로 비교적 평범하게 보임
  • Phoenix 프로젝트 템플릿의 라우터에는 다음과 같은 형태가 등장함
    • use OtterWeb, :router
    • pipeline :browser do ... end
    • plug :accepts, ["html"]
    • scope "/", OtterWeb do ... end
    • if Application.compile_env(:otter, :dev_routes) do ... end
  • 처음에는 scope, pipeline, plug, pipe_through가 Elixir가 아니라 Phoenix에 특별히 내장된 DSL처럼 보임
  • 실제 핵심은 Elixir의 호출 문법이 기본적으로 함수나 매크로 호출 하나로 수렴한다는 점임
    • 기본 형태는 foo(arg1, arg2, arg3)
    • 괄호를 빼면 foo arg1, arg2, arg3처럼 쓸 수 있음
    • 마지막 인자로 키워드 인자 리스트를 받을 수 있음
    • [key1: val1, key2: val2][{:key1, val1}, {:key2, val2}]와 동등함
    • 마지막 인자가 연관 리스트이면 바깥 대괄호도 생략할 수 있음
  • do ... end도 연관 리스트를 위한 문법 설탕으로 작동함
    • foo thing do 10 endfoo thing, do: 10에 해당함
    • 더 풀면 foo(thing, [{:do, 10}]) 형태로 볼 수 있음
    • foo가 함수가 아니라 매크로라면 do ... end 내부는 평가된 값이 아니라 구문 트리로 전달됨
  • 이 구조에서 scope, pipeline, def, defmodule, if는 모두 매크로로 이해할 수 있음

매크로, sigil, use가 만드는 확장 방식

  • Elixir는 Lisp 계열의 매크로와 동형 코드 표현뿐 아니라, 실제 사용에 가까운 다른 요소들도 가져옴
  • sigil

    • 일반 문자열은 "foo"로 씀
    • Erlang식 리스트 문자열은 ~c"foo"로 만들 수 있음
    • 정규식은 ~r/foo/ 형태로 만들 수 있음
    • Elixir의 sigil은 문자열을 받아 데이터를 반환하는 함수를 작성해 직접 정의할 수 있음
    • 관련 문서: Custom sigils
    • Common Lisp의 reader macros와 비슷한 역할을 하지만, Elixir의 sigil은 실제로 원하는 대부분의 작업에 더 나은 단축법처럼 다뤄짐
  • 도구와 문법 설탕의 층

    • Elixir는 다른 언어의 좋은 기능도 받아들임
    • Rust의 dbg!()와 비슷하게 표현식 코드와 결과를 출력하는 기능이 있음
    • pry를 통해 대화형 디버거로 들어갈 수 있으며, Erlang 디버거보다 더 강화된 것으로 보임
    • Haskell/ML 계열의 |> 파이프라인 문법을 사용함
    • Python이 REPL에서 탐색하기 쉬운 이유 중 하나인 대화형 문서 기능도 갖춤
    • 여러 겹의 문법 설탕은 각각 이전 층 안에 깔끔하게 들어가는 계층 구조로 작동함
    • foo([{:do, 3}])에서 foo do 3 end까지 여러 단계가 있지만, 기본 구조는 마지막 인자로 연관 리스트를 받는 함수·매크로 호출임
    • 예외적으로 do\n ...\n enddo: ...\n 사이에는 날카로운 모서리가 있음

use는 단순 import가 아니라 코드 생성 허가

  • use Thing, option: somethingrequire처럼 Thing 모듈을 가져온 뒤 Thing.__using__(option: something)을 호출함
  • 중요한 점은 __using__()이 보통 부작용을 위해 호출되는 함수가 아니라는 데 있음
  • __using__()은 자주 매크로이며, 코드를 생성해 use를 쓴 위치의 모듈 안에 삽입함
  • 따라서 use Thing은 다음 같은 코드로 확장될 수 있음
    • require Thing1
    • require Thing2
    • def some_cool_method() ...
    • use SomeOtherThing
  • use는 어떤 모듈의 정의를 현재 네임스페이스로 단순히 가져오는 것이 아니라, 그 모듈이 현재 모듈 안에 임의 코드를 생성하도록 허용함
  • Elixir 코드를 읽거나 쓸 때 use가 보이면, 많은 새 정의가 생길 수 있는 터보 매크로로 봐야 함

왜 “더 나은 Lisp”처럼 느껴지는가

  • Common Lisp이나 Scheme은 시작 단계에서 라이브러리, 패키지 관리자, FFI, 빌드 시스템, 애플리케이션 배포 같은 문제를 해결해야 함
  • Elixir는 BEAM 위에서 동작하므로, 일반적인 Lisp들에서 보기 어려운 견고성과 다중 처리 능력을 얻음
  • Erlang의 패턴 매칭은 높은 수준이며, Elixir는 이를 그대로 상속함
  • Erlang은 실행 중인 애플리케이션을 모니터링하고 내부를 들여다보는 도구와 라이브러리를 갖고 있고, Elixir는 이를 사용하면서 더 추가함
  • Elixir의 마법은 숨겨진 것이 아니라 드러나 있음
    • 튜토리얼 앞부분에서 작동 방식을 알려줌
    • 런타임의 핵심 구조가 리스트, 튜플, atom으로 이뤄져 있어 직접 살피고 바꿀 수 있음
    • 선언적 프로젝트 명세에서 다시 깨끗한 상태로 복원할 수 있음
  • Elixir는 내부를 감추는 대신 내부를 살피고 깨뜨려 볼 수 있게 하며, 프로덕션에서 깨지지 않도록 하는 도구 사용을 전제로 함

댓글과 토론

Hacker News 의견들
  • 글이 정말 훌륭하고 읽어볼 만함
    Elixir가 사실 Lisp라는 걸 깨달았을 때, 언어가 “조금 흥미롭다”에서 “이게 최애 언어가 될 수도 있겠다”로 바뀌었음
    여기에 Phoenix, Nerves, 점점 커지는 Nx 같은 생산적이고 실용적인 프레임워크까지 더하면 강력한 도구 상자가 됨
    가장 큰 불만은 빠른 스크립트를 쓰기 어렵다는 점이었지만, 지난 몇 년 사이 많이 좋아졌고, 의존성을 스크립트 안에 넣을 수 있다는 점에서는 Ruby보다 나을 수도 있어 보임

    • “Elixir가 Lisp”라는 말이 정말 맞는지 의문임
      예전에 봤을 때는 매크로 작성 문법이 일반 코드 문법과 달라서, 동질적 메타프로그래밍도 제대로 지원하지 않는 것처럼 보였음
      다만 오래전 기억이라 틀렸을 수도 있고, 반례가 있으면 보고 싶음
    • Ruby의 단일 파일 스크립트에서 Gemfile 의존성을 다루는 이야기도 예전보다는 조금 나아졌음
      그래도 결국 gem 설치는 필요하고, 이상적으로는 시스템 기본 위치가 아닌 로컬 위치에 설치하고 싶을 텐데, bundler에서는 보통 가능하지만 inline bundler에서는 유지보수자들이 그걸 반사용 사례처럼 보는 듯함: https://github.com/rubygems/bundler/issues/7131
      설득하려는 건 아니고, 다른 독자들을 위한 추가 정보임
    • Erlang VM에서 스크립트를 쓰는 문제는 느린 시작 시간
      스크립트와 실행 파일 용도에는 잘 맞지 않지만, 그 외에는 거의 모든 면에서 좋아함
    • Elixir의 Livebook이 개인적으로는 강력한 “스크립팅” 도구가 됐음
    • Elixir를 빠른 스크립트 언어처럼 쓰는 방법을 보여주는 좋은 저장소가 있음: https://github.com/wojtekmach/mix_install_examples
  • “마법은 절대 그만한 가치가 없다”는 말에 깊이 공감함
    마법 같은 편의 기능은 대개 언젠가 깨지고, 그걸 고치려면 반짝이는 편의 계층을 전부 베어내고 실제로 무슨 일이 벌어지는지 봐야 함
    파고들어 보면 설계부터 멍청하게 깨져 있거나, 애초에 별로 마법도 아니어서 “처음부터 이렇게 말하지 왜 그랬지?”가 되는 경우가 많음
    더 나쁜 건 모두가 서로 다른 마법을 쓰거나, 그 마법을 만든 사람이 2년 전에 떠난 뒤로는 전부 카고 컬트로 굴러가는 상황임

  • Elixir에서 마음에 드는 점은 동시성이 직관적이고, Elixir와 Erlang 표준 라이브러리를 둘 다 쉽게 쓸 수 있어 gen_tcp, gb_tree 같은 도구를 공짜로 얻는다는 점임
    운영 중인 가상 머신이나 pod에 원격 접속해 REPL로 상태를 살펴보고 “프로세스”를 관리하며 명령을 직접 실행할 수 있고, observer와 터미널 UI도 유용함
    라이브러리들이 표준적인 telemetry 방식을 쓰는 점, 빠른 컴파일, 열심히 일하는 BDFL, 존중하는 커뮤니티도 장점임
    반대로 동적 언어라 컴파일 타임에 잡을 수 있는 패턴 매칭이나 타입 불일치가 런타임에 터지고, Phoenix는 커뮤니티와 최신 자료가 많지 않아 프런트엔드 경험이 없으면 React 같은 현대적 SPA 프레임워크가 더 쉽게 느껴질 수 있음
    Elixir 인력 공급이 수요보다 많고, 생태계 성장도 상대적으로 느린 편임

    • Elixir와 주요 라이브러리인 Ecto와 Phoenix의 문서는 양이 부족해도 품질이 매우 좋음
      “xyz 하는 법” 같은 저품질 블로그 글이 많을 필요 없이, 공식 문서가 필요한 내용을 읽기 쉽고 찾기 쉽게 잘 정리해 줌
    • 아직 초기 단계지만, Elixir에 타입 시스템을 도입하려는 계획이 있음: https://elixir-lang.org/blog/2023/06/22/type-system-updates-...
    • 생태계는 마음에 들지만 동적 타입이 싫다면, BEAM 위의 정적 타입 언어인 Gleam도 있음: https://gleam.run/
    • 운영 중인 코드를 REPL로 바꾸는 방식은 Pharo가 떠오름
      Pharo는 아예 IDE까지 제공함
      다만 Pharo 커뮤니티도 너무 작게 느껴지고, 곧 ESUG 콘퍼런스가 열리니 모이는 모습을 보는 건 좋음
      그런데 왜 동시성이 직관적인지 궁금함
    • Elixir 인력 공급이 수요보다 많다는 게 사실인지 궁금함
  • Elixir는 언어 자체 외에도 개발자 도구가 훌륭하고, 전부 그 언어로 작성되고 설정된다는 점이 편안함
    외부 스크립트 언어가 필요 없고, 어떤 작업에서는 외부 라이브러리조차 필요 없을 때가 있음
    전체적으로 운영체제 같은 느낌이 들고, BEAM 자체도 운영체제 같은 성격이 있음
    Erlang이나 Elixir가 고민된다면 Sasa Juric의 The Soul of Erlang and Elixir를 볼 만함: https://youtu.be/JvBT4XBdoUE
    Ericsson의 Erlang 팀이 얼마나 시대를 앞섰고 지금도 그렇다는 점이 놀라움

    • Erlang을 정말 좋아함
      간결하고 강력하며 놀라울 정도로 잘 설계된 시스템이고, VM도 아주 잘 어울림
      다만 Elixir의 추가적인 장황함은 아직 넘기 어렵지만, 이 글을 보니 언젠가는 가능할지도 모르겠다는 희망이 듦
  • if, def, defmodule이 전부 매크로라는 점이 재미있음
    매크로를 정의하는 매크로인 defmacro 의 정의도 볼 만한데, 그것 역시 defmacro로 정의돼 있음: https://github.com/elixir-lang/elixir/blob/v1.15/lib/elixir/...

  • Elixir를 찾아오는 사람이 늘어나는 건 언제나 반가움
    사람들이 가장 자주 걸려 넘어지는 지점은 자유도라고 봄
    “크고 복잡한 프로젝트 구조”가 장애물이라고 했지만, 사실 언어 차원에서 강제되는 구조는 없고 원하는 대로 할 수 있음
    다만 Mix가 추천하는 관례가 있고 대부분 그 방식을 따를 뿐임
    테스트/개발/운영 실행 모드까지 포함해 거의 모든 것이 관례에 가깝고, Erlang도 표준 접근법은 있지만 반드시 따라야 하는 건 아님
    구조체도 언어 기능이 아니라 특별한 키와 Elixir 라이브러리 지원이 붙은 맵이라는 점이 좋은 예임
    장점이자 단점은 시간과 실력이 있다면 거의 무엇이든 구현할 수 있다는 것임

  • “아래까지 전부 매크로”라는 느낌을 제대로 받고 싶다면, 3분 39초짜리 이 영상이 있음: https://www.youtube.com/watch?v=x5W3CmZJMLs

  • Python/C++에서 와서 Elixir와 Phoenix를 보고 있는데 멋져 보임
    Python에서 실시간 기능을 하려면 Tornado나 asyncio 같은 게 필요해져서 이상적이지 않다고 봄
    큰 구조에서는 불변성과 메시지 전달 아키텍처를 선호하지만, 프로세스 내부에서는 Python 스타일 코드를 그냥 쓸 수 있으면 좋겠음
    어차피 VM은 프로세스 경계에서 모든 자료구조를 복사함
    Lisp를 20년 전에 Python보다 먼저 썼지만, 지금은 별 이득 없어 보이는 방식으로 코드를 전부 “뒤집어” 써야 하는 게 조금 성가심
    예를 들어 재귀로 리스트 합계를 쓰는 대신, Python처럼 for 루프와 누적 변수로 쓰고 싶고, 동시에 Erlang VM이 프로세스 스케줄링과 불변 메시지 전달을 해줬으면 함

    • Elixir의 리스트 컴프리헨션을 봤는지 궁금함
      for n <- 1..4, do: n*n처럼 쓸 수 있고, 예시의 합계는 Enum.reduceEnum.sum으로 처리 가능함
      https://elixir-lang.org/getting-started/comprehensions.html
      https://hexdocs.pm/elixir/1.12/Enum.html#reduce/2
      https://hexdocs.pm/elixir/1.12/Enum.html#sum/1
      그래도 Python/Go/JavaScript와는 다른 사고방식이라는 점은 맞음
    • Elixir에서도 리스트 컴프리헨션으로 reduce를 할 수는 있지만 흔하지는 않음
      보통은 Enum.reduce/2 가 더 나은 선택이고, 더 복잡한 상태가 필요하면 누산기에 더 많은 값을 넣으면 됨
      다만 어느 쪽이든 다른 댓글에서 말한 유연한 가변성은 제공하지 않음
    • 내장 함수는 제쳐두고, 대부분의 재귀 사용은 Enum.reduce가 더 잘 맞는다고 봄
    • Ray를 살펴볼 만함
      Python 라이브러리로, 액터인 클래스와 작업인 함수를 배포하고 리소스 관리, 메시지 전달, 자동 복구 같은 것을 추상화해 줌
      Erlang이나 Elixir를 써본 건 아니라 그쪽 해법이 더 사용하기 좋을 수도 있지만, Ray는 주로 머신러닝 용도를 겨냥하면서도 꽤 범용적임
    • Enum.reduce로 충분하지 않은지, 더 좋게는 Enum.sum이면 되지 않는지 궁금함: https://hexdocs.pm/elixir/1.12/Enum.html#reduce/3
  • Elixir 매크로의 유일한 문제는 사람들이 core++에 해당하는 ExUnit, Plug, Phoenix, Ecto의 매크로를 보고 모든 걸 도메인 전용 언어로 만들려 한다는 점임
    특히 commanded, Tesla, Absinthe, exmachina, 최근에는 ash가 심하다고 봄
    핵심 라이브러리들은 Plug의 get이나 Ecto의 SQL처럼 Elixir 바깥에서도 알아야 하는 문법이나 키워드와 맞추지만, 문제 되는 라이브러리들은 영리해 보이는 이름을 새로 만들어 모든 작업에 정신적 부담을 더함
    컴파일 경로를 지나치게 복잡하게 만들거나, 리팩터링과 코드 검색을 악몽으로 만드는 함수명을 생성하기도 함

    • 개인적으로는 라이브러리 내부에서 매크로를 많이 쓰는 것 자체는 괜찮음
      문서가 좋다면 구현 방식은 크게 신경 쓰지 않고, 많은 경우 더 깔끔한 API가 낫다고 봄
      다만 과해질 수는 있고, Phoenix 라우터에서도 특이한 일이 생기지만 그중 상당수는 순환 컴파일 의존성을 줄이기 위한 것임
      반면 비즈니스 코드의 매크로는 순수한 악이고, 지난 10년간 Rails를 겪은 사람이라면 그 교훈을 배웠을 것임
    • 이건 분명 문제지만, 다행히 Elixir 문화와 커뮤니티는 보통 불필요한 매크로를 좋게 보지 않음
      “일반 함수로 될 수 있다면 일반 함수여야 한다”는 말을 여러 번 들었고, 그 점이 마음에 듦
      전반적으로 Elixir를 쓰면서 크게 자주 부딪힌 문제는 아니었음
    • Tesla는 매크로 없이 완전히 쓸 수 있는 모드가 있고, 점점 그 방식만 쓰도록 권장하고 있음
      작성자도 2020년 기준으로 같은 방향을 말했음: https://github.com/elixir-tesla/tesla/issues/367#issuecommen...
      대안으로 최근 글에서 언급된 req도 있고 좋아 보이지만, 모든 경우에 Tesla를 대체할 수 있는지는 아직 실험 중임
      Absinthe는 GraphQL 명세에 맞춰 엄격히 정의해야 하므로 일종의 컴파일러에 가깝고, 이제 SDL 파일을 가져올 수는 있지만 resolver와 middleware 연결은 여전히 필요함
      매크로 기반이라 직접 매크로로 코드 재사용을 하려 할 때 날카로운 모서리가 있지만, 스키마 정의를 제외하면 매크로 없이도 쓸 수 있음
      Exmachina는 factory_bot 남용을 너무 많이 봐서 끌리지 않았고, Ash는 흥미로운 실험처럼 보이지만 Phoenix에 비해 많이 쓰이는지는 모르겠음
      commanded는 CQRS/ES를 쓸 일이 없어 다뤄볼 이유가 없었고, 이 셋을 Elixir의 주요 플레이어라고 보지는 않음
    • Ash를 사이드 프로젝트에서 쓰고 있는데 점점 마음에 들고 있음
      완전히 이해하려면 머리를 좀 비틀어야 하지만, Ecto보다 보일러플레이트가 적고 쿼리, 변경 처리, API 접근, 연결 작업을 하나의 리소스와 몇 개의 매크로로 모을 수 있음
      일반 Elixir로 빠져나갈 수 있는 탈출구도 꽤 많아서, 액션 뒤의 마법이 싫으면 여러 방식으로 끼어들거나 직접 구현할 수 있고 호출 코드는 그대로 보임
      매크로가 많아도 오류는 보통 꽤 좋고 문제 위치를 잘 짚어줌
      단점은 Ash를 정말 이해하는 사람이 너무 적고, 문서가 종종 너무 짧고 압축적이며, 오류가 항상 좋은 것은 아니라는 점임
      생태계 일부에서 매크로에 지나치게 의존한다는 데는 동의함
    • Elixir를 좋아하고 잘 아는 입장에서도, 이 점 때문에 개인적으로 Phoenix 학습이 어려웠음
      매크로 위에 매크로가 너무 많아서 거의 다른 언어를 배우는 느낌이 듦
  • “Elixir가 사실 Lisp”라는 걸 이해하는 순간은 정말 신남
    모든 요소가 아주 잘 맞물리는 아름다운 언어임