5 minute read

Elixir, OTP, Phoenix로 웹 애플리케이션을 짜는 걸 설명하는 책이다. 어떤 애플리케이션을 만들까? 번갈아 가며 섬의 위치를 추측하는 Island Game이라는 게임을 만든다. 책 한 권을 할애해 처음부터 끝까지 짜면서 설명하기 적당한 예제다.

We’re taking two radical steps with this approach, beginning an application without a framework, and choosing not to use a database for working state. We shouldn’t make those decisions lightly. There should be some tangible benefits in return. By beginning this way, we gain focus, clarity, and simplicity.

이 접근 방식에서는 프레임워크 없이 애플리케이션을 시작하고 작업 상태에 데이터베이스를 사용하지 않기로 하는 두 가지 급진적인 조치를 취하고 있습니다. 이러한 결정을 가볍게 내려서는 안 됩니다. 그 대가로 가시적인 이점이 있어야 합니다. 이렇게 시작하면 집중력, 명확성, 단순성을 얻을 수 있습니다.

Functional Web Development with Elixir, OTP, and Phoenix (Lance Halvorsen, 2018)

접근 방식이 마음에 들었다. GenServer Behaviour 구현이 한참이나 뒤에 나온다. 자료형을 선택하고 비즈니스 로직에 집중한다. 그 후에 state를 넣어서 상태를 기억하게 하고 Phoenix를 사용해서 웹 서비스에서 비즈니스 로직을 호출하게 한다. 집중력, 명확성, 단순성을 얻을 수 있다는 걸 예제로 충분히 보여준다. 애플리케이션을 개발할 때 참고할 수 있는 좋은 Best Practice다.

개념 정리를 할 수 있었다. Behaviour도 쓰기 바빴다. 책이 이참에 개념 정리를 하고 넘어가야겠다는 동기 부여를 해줬다. Application Behaviour는 Erlang과 Elixir가 마이크로서비스가 유행하기 전에 이미 준비된 상태로 만들어 놨다. Actor ModelSupervision tree를 통한 Fault Tolerance에 집중해서 다른 개념에 디테일이 떨어지는 걸 알게 됐다. Elixir 경험이 있다면 쉽게 읽을 수 있는 책이다. 빠진 개념을 착실히 정리하는 데 도움이 된다.

Early client-server applications were stateful. Clients connected to the server and stayed connected while they passed messages back and forth. Think mainframe applications with dedicated terminals as clients.

That worked well, but it meant that the number of possible clients was limited by system resources like memory, CPU, and the number of concurrent processes the system could support.

The web gets around these limitations because of the nature of HTTP. When a client makes an HTTP request to a server, it must supply all the data the server will need to fulfill that request. Once the server sends its response, it forgets everything it just knew about both the request and the client.

초기 클라이언트-서버 애플리케이션은 상태 저장 방식이었습니다. 클라이언트는 서버에 연결하여 메시지를 주고받는 동안 연결 상태를 유지했습니다. 전용 터미널이 있는 메인프레임 애플리케이션을 클라이언트라고 생각하면 됩니다.

이는 잘 작동했지만 메모리, CPU, 시스템이 지원할 수 있는 동시 프로세스 수와 같은 시스템 리소스에 의해 가능한 클라이언트 수가 제한된다는 것을 의미했습니다.

웹은 HTTP의 특성으로 인해 이러한 한계를 극복합니다. 클라이언트가 서버에 HTTP 요청을 할 때는 서버가 해당 요청을 처리하는 데 필요한 모든 데이터를 제공해야 합니다. 서버가 응답을 전송하면 요청과 클라이언트 모두에 대해 방금 알고 있던 모든 정보를 잊어버립니다.

Functional Web Development with Elixir, OTP, and Phoenix (Lance Halvorsen, 2018)

왜 stateless인 HTTP 프로토콜을 많이 사용했을까? 왜 stateless인 HTTP 프로토콜을 설계했을까? 책을 읽다 보면 주요 설명이 아닌 곁다리 설명에서 미처 생각해 보지 않는 설명을 보고 반가워할 때가 있다. 이 책에서 state를 유지하는 건 기억하는 행위라는 걸 설명하면서 자원의 효율적인 사용을 위해 stateless인 HTTP를 사용한다는 설명이 그랬다.

We’re about to make a big leap. Up to this point, we’ve focused on modules, functions, and data. This is the synchronous side of Elixir, the world within a process where code executes sequentially. In this chapter, we’ll be moving into the asynchronous side, the world of processes and message passing that provides Elixir’s world-class concurrency and parallelism.

이제 큰 도약을 앞두고 있습니다. 지금까지는 모듈, 함수, 데이터에 중점을 두었습니다. 이것은 코드가 순차적으로 실행되는 프로세스 내의 세계인 Elixir의 동기적 측면입니다. 이 장에서는 Elixir의 세계 최고 수준의 동시성 및 병렬성을 제공하는 프로세스 및 메시지 전달의 세계인 비동기 측면으로 이동하겠습니다.

Functional Web Development with Elixir, OTP, and Phoenix (Lance Halvorsen, 2018)

GenServer Behaviour 사용하는 코드를 추가하면서 이렇게 거창하게 말할 수 있다니. 모듈, 함수, 데이터는 동기(syncronous) 사이드라고 볼 수 있고 Elixir Process는 비동기(asyncronous) 사이드라고 볼 수 있다.

애플리케이션 하나를 Elixir로 만들면서 가르쳐준다. 나보다 더 디테일이 높은 파트가 많아서 많이 배울 수 있었다. 이런 게 전체를 훑어주는 책에서 배울 수 있는 점이다.

책을 읽으며 배우다

밑 줄

  • We’re taking two radical steps with this approach, beginning an application without a framework, and choosing not to use a database for working state. We shouldn’t make those decisions lightly. There should be some tangible benefits in return. By beginning this way, we gain focus, clarity, and simplicity.
  • Our state machine will help us coordinate events and transitions as well. Most importantly, we’ll keep our code clean by separating state management from business logic.
  • Early client-server applications were stateful. Clients connected to the server and stayed connected while they passed messages back and forth. Think mainframe applications with dedicated terminals as clients. That worked well, but it meant that the number of possible clients was limited by system resources like memory, CPU, and the number of concurrent processes the system could support. The web gets around these limitations because of the nature of HTTP. When a client makes an HTTP request to a server, it must supply all the data the server will need to fulfill that request. Once the server sends its response, it forgets everything it just knew about both the request and the client.
  • We’re about to make a big leap. Up to this point, we’ve focused on modules, functions, and data. This is the synchronous side of Elixir, the world within a process where code executes sequentially. In this chapter, we’ll be moving into the asynchronous side, the world of processes and message passing that provides Elixir’s world-class concurrency and parallelism.
  • The team reasoned that it’s nearly impossible to predict all possible failures in advance, so they decided to focus on recovering from failure instead. They wanted to code for the happy path and have a separate mechanism get things back on track when the inevitable errors happen. The design they came up with is the supervisor Behaviour. It extracts error handling code from business logic into its own modules. Supervisor modules spawn supervisor processes that link to other processes and watch for failure, restarting those linked processes if they crash.
  • This is where we really fulfill the promise of fault tolerance. It’s one thing to restart a process if it crashes and then move on. It’s another thing entirely to restart it and restore the last known good state.
  • Phoenix Is Not Your Application
  • Channels scale incredibly well. In one test on a powerful machine, the Phoenix team was able to establish two million simultaneous channel connections. These weren’t just static connections. The team was able to broadcast messages to all two million clients within a few seconds.