Phoenix Presence

Tags
Published
Author
실시간 통신이 필요한 사이드 프로젝트를 구현하는데, 이 프로젝트는 특정 Room 에 유저가 들어오고 나가는 것을 트래킹 해야하고, 특정 시점에 Room 에 존재하는 전체 connection 정보를 조회해야 한다. 이런 경우 Phoenix Presence 를 사용하면 원하는 기능을 쉽게 구현할 수 있다.

Presence

Presence 는 Phoenix 프레임워크에서 제공하는 모듈이다. 특정 Topic 에 대해서 현재 topic 에 대한 연결된 connection 들을 조회하는 기능을 제공한다. 또 커넥션의 새로운 연결과 기존 커넥션이 끊어지는 경우 연결된 connection 들에 정보를 제공하는 기능도 제공한다.
단순히 topic 에 연결된 커넥션들을 트래킹하는 경우라면 Phoenix.Tracker 를 사용하고, 만약 각 커넥션에 현재 topic 에 대한 connection 의 diff 를 broadcast 하는 기능까지 필요한 경우 Phoenix.Presence 를 사용하면 된다. 나는 diff broadcasting 기능이 필요했기 때문에 Presence 를 사용하게 되었다.

Setting

세팅은 아주 간단하다.
먼저 Presence 모듈을 정의한다. use 를 사용해서 Phoenix 의 기능을 그대로 가져온다.
defmodule MyAppWeb.Presence do use Phoenix.Presence, otp_app: :my_app, pubsub_server: MyApp.PubSub end
이후 해당 모듈을 application.ex 의 children 에 포함시킨다. 이 때 기존 App 의 PubSub 이 미리 정의되어있어야 하고, PubSub 뒤 Endpoint 앞으로 위치하도록 추가하면 된다.
children = [ ... {Phoenix.PubSub, name: MyApp.PubSub}, MyAppWeb.Presence, MyAppWeb.Endpoint ]
이렇게만 하면 사용할 준비는 끝이다.

Track

먼저 channel 에 join 하는 경우 새로 join 한 connection 이 트래킹 되도록 등록하는 코드를 알아보자.
@impl true def join("room:" <> room_id, %{"nickname" => nickname}, socket) do with {:ok, validated_nickname} <- validate_nickname(nickname) do player_id = UUID.uuid4() socket = assign(socket, %{ room_id: room_id, player_id: player_id, nickname: validated_nickname }) send(self(), :after_join) {:ok, %{roomId: room_id, playerId: player_id, nickname: validated_nickname}, socket} else :error -> {:error, %{reason: "invalid nickname"}} end end
특정 채널의 Join 함수이다. 보통 Elixir 관행에 따라서 join 에서는 join 만 신경쓰도록 한다. 중간에 send(self(), :after_join) 코드가 있는데, 해당 코드는 현재 프로세스의 메세지큐에 메세지를 저장하도록 한다. 이후 handle_info(:after_join, params) 함수가 실행되는데 여기서 join 이후에 필요한 작업을 정의한다.
handle_info 코드는 아래와 같다.
@impl true def handle_info(:after_join, socket = %{assigns: %{room_id: room_id, player_id: player_id}}) do {:ok, _} = SqetchclubWeb.RoomTracker.track_room(socket, %{room_id: room_id, player_id: player_id}) {:noreply, socket} end
정상적으로 join 을 하는 경우 socket 에 저장한 room_id, player_id 정보를 track_room 함수로 전달한다.
track_room 함수는 아래의 RoomTracker module 에 정의되어 있다.
defmodule SqetchclubWeb.RoomTracker do use Phoenix.Presence, otp_app: :sqetchclub, pubsub_server: Sqetchclub.PubSub @topic_prefix "sqetch:room_tracker:" def track_room(socket, %{room_id: room_id, player_id: player_id}) do track(socket.channel_pid, @topic_prefix <> room_id, player_id, %{ player_id: player_id, player_joind_at: System.system_time(:millisecond) }) end def all_players(room_id) do topic = @topic_prefix <> room_id list(topic) end end
이후 web 으로 채널에 join 후 all_players 함수를 호출하면 아래와 같은 결과를 확인할 수 있다.
notion image
Tab 하나를 닫으면 아래와 같이 하나의 플레이어가 없어진 것을 확인할 수 있다.
notion image
 
끝!