Programming Phoenix 1.4 (Chris McCord 외 2명, 2019) 독후감

5 minute read

elixir 언어로 만든 웹 프레임워크 phoenix를 설명한다. phoenix에 관심이 없더라도 추천하고픈 책이다. 잘 만든 웹 프레임워크를 내공 짱짱 맨들이 설명해주기 때문이다. elixir 언어를 만든 Jose Valim도 저자 중 한 명이다. 설명도 좋고 예제 코드도 좋다. 책에서 설명하는 테스트 코드도 따라 할 게 많다.

defmodule MyApp.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  plug(Plug.Static)
  plug(Plug.RequestId)
  plug(Plug.Telemetry)
  plug(Plug.Parsers)
  plug(Plug.MethodOverride)
  plug(Plug.Head)
  plug(Plug.Session)
  plug(MyApp.Router)
end

Our method of composing services with a series of functional transformations is reminiscent of Clojure’s Ring.

Phoenix is an opinionated framework that favors convention over configuration. But rather than hiding complexity, it layers complexity, providing details piece by piece.

Plugs are functions. Your web applications are pipelines of plugs.

phoenix에선 router, endpoint, controller 등에서 많은 plug 함수(사실은 매크로)를 호출을 볼 수 있다. 요청(request)을 일련의 plug를 통해 데이터를 변환해서 응답(response)을 만들어낸다. phoenix 설계 철학에서 가장 중요한 부분이라고 생각한다. 복잡성을 숨기지 않고 계층화를 선택했다.

Most frameworks have hidden functions that are only exposed to those with deep, intimate internal knowledge. The Phoenix experience is different because it encourages breaking big functions down into smaller ones. Then, it provides a place to explicitly register each smaller function in a way that’s easy to understand and replace. We’ll tie all of these functions together with the Plug library.

이번에 처음 웹 프레임워크를 사용해봤다. 경험이 없어서 비교할 게 없어서 아쉽다. plug 함수의 나열이 처음에는 어지러웠지만 명확한 목적을 가진 단위로 만든 plug의 조합이라서 이해할 수 있었다. 인증 plug를 구현해 쉽게 끼워 넣을 수 있었다.

def changeset(user, attrs) do
  user
  |> cast(attrs, [:name, :username])
  |> validate_required([:name, :username])
  |> validate_length(:username, min: 1, max: 20)
end

def registration_changeset(user, params) do
  user
  |> changeset(params)
  |> cast(params, [:password])
  |> validate_required([:password])
  |> validate_length(:password, min: 6, max: 100)
  |> put_pass_hash()
end

One size does not fit all when it comes to update strategies. Validations, error reporting, security, and the like can change. When they do, if your single update policy is tightly coupled to a schema, it’ll hurt. The changeset lets Ecto decouple update policy from the schema, and that’s a good thing because you can handle each update policy in its own separate changeset function.

phoenix 책인데, ecto 철학도 배웠다. 별 생각 없이 시키는 대로 스키마(schema)를 정의하고 업데이트 정책(update policy)을 담은 changeset을 구현했다. 스키마에 유효성을 검사할 수 있는 정보를 추가하면 한 곳에서 검사를 해서 실수할 가능성이 줄어든다. 하지만 ecto는 스키마와 업데이트 정책을 분리하는 설계를 선택했다. 스키마에 정의하는 유효성 검사가 모든 업데이트 정책을 반영할 수 없기 때문이다. 하긴 예로 든 코드만 봐도 이해가 된다. 계정만 하더라도 소셜 아이디 연결 계정, 봇 계정, 테스트 계정, 일반 사용자 계정 등 여러 종류가 있다. 스키마에 욱여넣기보단 스키마를 가볍게 가고 업데이트 정책을 분리하는 게 더 나은 설계라고 생각한다. 사실 난 한 곳에서 검사하는 코드를 좋아한다. 하지만 듣고 보니 분리하는 게 더 낫다. 팔랑팔랑.

테스트 코드에서도 배울 게 많았다. fixture를 만드는 방법. tag를 사용해 setup 인자로 넣는 방법. stub을 최소로 사용해 결과물을 파싱하는 부분부터 실제 코드를 적용하는 방법. database sandbox를 사용하는 방법. 무엇보다 테스트 코드 자체가 좋아서 많이 배웠다.

pheonix에 관심 없더라도 ecto, test를 망라한 좋은 설명과 예제 코드 때문에 추천하고 싶은 책이다

책에서 한 수 배워서 쓴 글 리스트

기억에 남는 문장

  • Phoenix is an opinionated framework that favors convention over configuration. But rather than hiding complexity, it layers complexity, providing details piece by piece.
  • Thanks to the Erlang VM, Elixir provides I/O concurrency without callbacks, with user-space multi-core concurrency. In a nutshell, this means Elixir developers write code in the simplest and most straight-forward fashion and the virtual machine takes care of using all of the resources, both CPU and I/O, for you.
  • You can think of any web server as a function. Each time you type a URL, think of it as a function call to some remote server. That function takes your request and generates some response. As we will see, a web server is a natural problem for a functional language to solve.
  • When you think about it, typical web applications are just big functions. Each web request is a function call taking a single formatted string—the URL—as an argument. That function returns a response that’s nothing more than a formatted string.
  • Most frameworks have hidden functions that are only exposed to those with deep, intimate internal knowledge. The Phoenix experience is different because it encourages breaking big functions down into smaller ones. Then, it provides a place to explicitly register each smaller function in a way that’s easy to understand and replace. We’ll tie all of these functions together with the Plug library.
  • Plugs are functions. Your web applications are pipelines of plugs.
  • One size does not fit all when it comes to update strategies. Validations, error reporting, security, and the like can change. When they do, if your single update policy is tightly coupled to a schema, it’ll hurt. The changeset lets Ecto decouple update policy from the schema, and that’s a good thing because you can handle each update policy in its own separate changeset function.
  • The way the sandbox operates is quite efficient too: instead of deleting all of the data once the suite finishes, which would be expensive, it just wraps each test in a transaction. Once the test is done, the transaction is rolled back and everything behaves as if the data was never there.
  • The >, or pipe operator, takes the value on the left and passes it as the first argument to the function on the right. We call these compositions pipes or pipelines, and we call each individual function a segment or pipe segment.
  • In Phoenix, we like to encapsulate all business logic in simple modules called contexts.
  • After compilation, templates are functions. Since Phoenix builds templates using linked lists rather than string concatenation the way many imperative languages do, one of the traditional bottlenecks of many web frameworks goes away. Phoenix doesn’t have to make huge copies of giant strings.