Powered by AppSignal & Oban Pro

Deploy a chat app with Kino

demo/chat.livemd

Deploy a chat app with Kino

Mix.install([
  {:kino, "~> 0.11.0"}
])

Introduction

이 노트에서는 채팅 애플리케이션을 구축하고 배포할 것입니다. 그러기 위해서 라이브북의 컴패니언 라이브러리를 이용하겠습니다 [‘kino’] (https://github.com/livebook-dev/kino) .

간단히 말해서, 키노는 당신이 부분적으로 설치하는 도서관입니다 노트북을 대화식으로 만들 수 있습니다. 키노는 그리스어 접두사 “kino-“에서 유래했고, 그것은 다음을 의미합니다 “움직이는“ 도서관을 배우면서, 당신이 알게 될 것입니다 이것이 바로 우리의 노트북에 가져다 주는 것입니다.

키노는 마크다운, 애니메이션 프레임, 디스플레이 테이블, 입력 등을 관리합니다. 또한 빌딩 블록을 제공합니다 차트, 스마트 셀 등으로 라이브북을 확장할 수 있습니다. 노트북 애플리케이션을 구축하는 데는 크게 두 가지를 사용합니다 빌딩 블록: Kino.ControlKino.Frame.

위의 종속성으로 나열된 kino를 확인할 수 있으니 실행해 보겠습니다 셋업 셀과 시작합니다.

Kino.Control

가장 간단한 컨트롤은 Kino.Control.button/1입니다. 한번 시도해 보겠습니다:

click_me = Kino.Control.button("Click me!")

위의 셀을 실행하면 버튼이 렌더링됩니다. 클릭하시면 됩니다 하지만 아무 일도 일어나지 않을 겁니다 다행히도 우리는 버튼을 구독할 수 있습니다 이벤트:

Kino.Control.subscribe(click_me, :click_me)

가입을 했으니 버튼을 누를 때마다 :click_me 태그가 붙은 메시지를 받습니다. 모든 메시지를 인쇄합니다 받은 편지함에 저장됩니다:

Process.info(self(), :messages)

위의 셀을 실행한 다음 두 번 클릭합니다.

  • 상부 지시요각 클릭으로 새 메시지가 있습니다. 우리 편지함이 메시지를 소비할 수 있는 몇 가지 방법이 있습니다. 다음 예를 들어 봅시다.

Enumerating controls

모든 Kino 컨트롤을 열거할 수 있습니다. 이것은 우리가 그것들을 컬렉션, 이 경우 이벤트의 무한한 흐름으로 취급할 수 있음을 의미합니다. 다른 버튼을 정의해 보겠습니다.

click_me_again = Kino.Control.button("Click me again!")

이제 이러한 이벤트를 사용하겠습니다. 스트림은 무한하기 때문에 노트북을 차단하지 않기 위해 별도의 프로세스 내에서 사용합니다.

spawn(fn ->
  for event <- click_me_again do
    IO.inspect(event)
  end
end)

버튼을 제출하면 새로운 이벤트가 출력되어야 합니다. 이 노트북을 차단하지 않고 이벤트를 소비하는 이러한 패턴은 ‘Kino’가 Kino.animate/2Kino.listen/2와 같은 편리한 기능을 가지고 있다는 것이 너무 흔해서 계속 학습하죠.

Kino.Frame and animations

Kino.Frame을 사용하면 빈 프레임을 렌더링하고 진행하면서 업데이트할 수 있습니다. 이제 빈 프레임을 렌더링해보겠습니다:

frame = Kino.Frame.new()

이제 프레임 안에 1부터 100 사이의 임의의 숫자를 직접 렌더링해 보겠습니다:

Kino.Frame.render(frame, "Got: #{Enum.random(1..100)}")

위의 셀을 다시평가할 때마다 프레임이 업데이트되는 것을 주목하세요. 또한 Kino.Frame.append/2를 사용하여 프레임에 추가할 수도 있습니다:

Kino.Frame.append(frame, "Got: #{Enum.random(1..100)}")

여러 번 추가하면 항상 새로운 내용이 추가됩니다. 내용은 Kino.Frame.render/2 또는 Kino.Frame.clear/1를 호출하여 재설정할 수 있습니다.

프레임에 대한 중요한 점은 모든 사용자 간에 공유된다는 것입니다. 이 같은 노트북을 다른 탭에서 열고 위의 셀을 실행하면 모든 탭에 새로운 결과가 추가됩니다. 이는 Livebook 자체에서 협업 애플리케이션을 구축하는 데 프레임을 사용할 수 있다는 것을 의미합니다.

루프와 결합하여 내용을 동적으로 추가하거나 노트북을 애니메이션화하는 데 사용할 수 있습니다. 실제로 이러한 목적으로 사용할 수 있는 편리한 함수인 Kino.animate/2가 있습니다.

Kino.animate(100, fn i ->
  Kino.Markdown.new("**Iteration: `#{i}`**")
end)

위의 예제는 뒷면에서 새 프레임을 생성하고 100ms마다 새로운 Markdown 출력을 렌더링합니다. 동일한 접근 방식을 사용하여 일반 출력이나 이미지를 렌더링할 수도 있습니다!

또한 Kino.animate/3도 있으며, 특정 지점에서 애니메이션을 중지하거나 상태를 누적해야 하는 경우에 사용할 수 있습니다. 두 animate 함수 모두 열거 가능한 것을 받을 수 있으므로 컨트롤 이벤트를 기반으로 프레임을 애니메이션화할 수 있습니다.

button = Kino.Control.button("Click") |> Kino.render()

Kino.animate(button, 0, fn _event, counter ->
  new_counter = counter + 1
  md = Kino.Markdown.new("**Clicks: `#{new_counter}`**")
  {:cont, md, new_counter}
end)

animate를 사용하여 이벤트를 처리하는 장점 중 하나는 노트북 실행을 차단하지 않고 일반적으로 진행할 수 있다는 것입니다.

Putting it all together

제어 요소와 프레임에 대해 배웠으므로 이제 채팅 애플리케이션을 만들 준비가 되었습니다.

첫 번째 단계는 채팅 메시지를 렌더링할 프레임을 정의하는 것입니다.

frame = Kino.Frame.new()

이제 우리는 여러 입력을 한꺼번에 렌더링하고 제출하는 새로운 제어 요소인 ‘forms’을 사용할 것입니다.

inputs = [
  name: Kino.Input.text("Name"),
  message: Kino.Input.text("Message")
]

form = Kino.Control.form(inputs, submit: "Send", reset_on_submit: [:message])

이제 매번 폼이 제출될 때 메시지를 프레임에 추가하려고 합니다. 우리는 ‘Kino.animate/3’에 대해 배웠는데, 이 함수는 제어 이벤트를 받지만 항상 내용을 추가하려는 우리의 목적과는 맞지 않습니다. 우리는 내용을 직접 누적하고 항상 프레임에 모두 다시 렌더링할 수 있지만, 그런 접근 방식은 약간 낭비적으로 들릴 수 있습니다.

다행히도 Kino는 ‘listen’이라는 함수도 제공합니다. ‘listen’은 제어 및 열거 가능한 이벤트를 소비하지만 항상 프레임을 렌더링하려는 것을 가정하지 않으므로 더 많은 제어를 제공합니다. 한 번 시도해 보겠습니다:

Kino.listen(form, fn %{data: %{name: name, message: message}, origin: origin} ->
  if name != "" and message != "" do
    content = Kino.Markdown.new("**#{name}**: #{message}")
    Kino.Frame.append(frame, content)
  else
    content = Kino.Markdown.new("_ERROR! You need a name and message to submit..._")
    Kino.Frame.append(frame, content, to: origin)
  end
end)

위의 셀을 실행하면 채팅 앱이 완전히 작동해야 합니다. 위로 스크롤하고 폼을 통해 메시지를 제출하여 프레임에 나타나는 것을 확인할 수 있습니다.

구현적으로 ‘listen’ 호출은 폼 이벤트를 수신하며 각 입력의 값을 포함합니다. 이름과 메시지가 제공된 경우, 이를 프레임에 추가합니다.

‘append’ 함수는 두 가지 옵션을 받는데, 위의 예제에서 사용된 첫 번째 옵션은 ‘to: origin’입니다. 이것은 특정 메시지가 폼을 제출한 사용자에게만 보내진다는 의미이며 다른 모든 사용자에게는 전달되지 않습니다.

또 다른 자주 사용되는 옵션은 :temporary입니다. 기본적으로 모든 메시지는 프레임에 저장됩니다. 이것은 페이지를 다시로드하거나 늦게 참가하더라도 모든 이력을 볼 수 있음을 의미합니다. :temporary를 true로 설정하면 이렇게 되지 않습니다. 주의: :to 옵션으로 전송된 모든 메시지는 일시적인 것입니다.

또한 이 노트북을 다른 탭에서 열고 서로 다른 사용자가 어떻게 채팅할 수 있는지 시뮬레이션할 수 있습니다. 한 번 시도해 보세요!

Deploying

우리의 채팅 애플리케이션이 준비되었으므로 이제 배포할 준비가 되었습니다! 사이드바에 있는 아이콘을 클릭한 다음 “Configure”를 클릭하세요.

이제 배포에 대한 슬러그(예: “chat-app”)를 정의하고 비밀번호를 설정(또는 비밀번호 보호를 해제)한 후 “Deploy”를 클릭하세요. 이제 URL을 클릭하고 노트북 내에서 한 것처럼 채팅 앱과 상호 작용할 수 있습니다.

노트북을 배포할 때 Livebook는 노트북의 시작부터 끝까지 모든 코드를 실행합니다. 이로써 사용자가 상호 작용할 수 있도록 프레임 및 폼을 모두 설정합니다. 무언가 잘못되면 배포된 앱에서 Livebook 아이콘을 클릭하고 배포된 노트북 세션을 디버깅할 수 있습니다.

여기부터는 배포된 애플리케이션을 조정하십시오. 이전 섹션에서 사용되지 않는 출력을 제거하거나 새로운 기능을 추가하세요.

배포 축하드립니다!

Docker deployment

이제 첫 번째 노트북을 로컬로 애플리케이션으로 배포했으므로 생각할 수 있는 질문은: 실제로 이것을 프로덕션 환경으로 출시할 수 있을까요?

답은 “네”입니다!

사이드바에서 아이콘을 클릭하면 “Docker로 배포” 링크를 찾을 수 있습니다. 이 링크를 클릭하면 Docker를 통해 단일 노트북 또는 여러 항목이 들어 있는 폴더를 배포하는 방법에 대한 지침이 포함된 모달 창이 열립니다.

팀으로 노트북을 개발하고 배포하려면 Livebook Teams를 확인해보세요.

Where to go next

노트북을 사용하여 구축할 수 있는 다양한 종류의 애플리케이션이 있습니다. 예를 들어, 여기서 배운 내용을 기반으로 어떠한 종류의 폼 기반 애플리케이션도 개발할 수 있습니다. 구조는 항상 동일합니다.

  1. 사용자가 상호 작용할 입력 및 폼을 정의합니다.
  2. 폼 이벤트에 연결하여 데이터를 받아오고 유효성을 검사합니다.
  3. Kino.Frame에 직접 업데이트를 렌더링합니다.

프레임은 여기서 중요한 역할을 합니다. ‘to: origin’ 옵션 없이 프레임에 렌더링하면 업데이트가 모든 사용자에게 전송됩니다. ‘to: origin’ 옵션을 사용하면 변경 사항이 특정 사용자에게만 표시됩니다. 이것은 애플리케이션이 협업적인지 여부에 대한 완전한 제어를 제공합니다.

Livebook는 또한 멀티 세션 애플리케이션을 지원합니다 여러 사용자가 요청시 자체 Livebook 세션을 시작하는 멀티 세션 애플리케이션을 구축하는 데 Kino.InputKino.interrupt/2를 사용하는 것이 일반적입니다. 일반 노트북과 유사하게 단계별로 실행되며 이 가이드에서 수행한 것처럼 폼 컨트롤 및 이벤트 핸들러를 설정할 필요가 없습니다. 또한 멀티 세션 앱의 각 세션은 자체 Elixir 런타임을 가지며 격리를 제공하지만 세션당 더 높은 메모리 사용량을 야기합니다.

앱에 대해 더 자세히 알아보려면 다음 자료를 참고하세요.