[Ash] Scope
Multi-Tenancy 기반의 코드
Ash로 멀티테넌트 앱을 개발하다 보면 아래와 같은 코드를 자주 작성하게 된다.
# 매번 tenant 와 actor 를 반복해서 전달해야 함
Clubs.create_club!(params, actor: current_user)
Clubs.list_notices(tenant: current_club, actor: current_user)
Clubs.invite_member(params, tenant: current_club, actor: current_user)
Club 내부에서의 모든 action 들에 대한 권한 검증을 위해 현재 User 가 활동하는 Club 의 Context 와 User 정보를 매번 전달받게된다.
문제점:
같은
tenant와actor를 계속 반복실수로
tenant나actor를 빼먹을 가능성이 있음코드가 길어지고 패턴이 일관되지 않음
Ash Scope
Ash Scope는 여러 옵션을 하나로 묶어서 전달하는 역할을 한다:
# 한 번만 설정
scope = %MyApp.Scope{user: current_user, club: current_club}
# 깔끔하게 사용
Clubs.create_club!(params, scope: scope)
Clubs.list_notices(scope: scope)
Clubs.invite_member(params, scope: scope)
Ash Scope의 역할
1. 옵션 변환
Scope를 전달하면 Ash가 내부적으로 개별 옵션으로 변환한다:
# 이렇게 호출하면
Clubs.create_club!(params, scope: scope)
# Ash가 내부적으로 이렇게 변환
Clubs.create_club!(params, actor: scope.user, tenant: scope.club)
2. 구현 방법
Ash.Scope.ToOpts 프로토콜을 구현해서 Ash에게 변환 방법을 알려준다:
defmodule MyApp.Scope do
defstruct [:user, :club]
defimpl Ash.Scope.ToOpts do
def get_actor(%{user: user}), do: {:ok, user}
def get_tenant(%{club: club}), do: {:ok, club}
def get_context(_), do: {:ok, %{}}
def get_tracer(_), do: :error
def get_authorize?(_), do: :error
end
end
3. 자동 생성
플러그에서 현재 사용자와 클럽 정보를 자동으로 Scope로 만든다:
defmodule MyAppWeb.AssignScope do
def call(conn, _opts) do
scope = %MyApp.Scope{
user: conn.assigns.current_user,
club: get_current_club(conn)
}
assign(conn, :current_scope, scope)
end
end
사용 예시
LiveView에서
def handle_event("create", params, socket) do
scope = socket.assigns.current_scope
case Clubs.create_notice(params, scope: scope) do
{:ok, _} -> {:noreply, put_flash(socket, :info, "생성 완료")}
{:error, _} -> {:noreply, put_flash(socket, :error, "생성 실패")}
end
end
복잡한 비즈니스 로직에서
def create_event_with_notifications(params, scope: scope) do
with {:ok, event} <- Events.create(params, scope: scope),
{:ok, _} <- Notifications.send_to_members(event, scope: scope),
{:ok, _} <- Logs.record_activity(event, scope: scope) do
{:ok, event}
end
end
핵심 가치
Ash Scope는 단순히 코드 중복을 제거하는 도구다:
Before:
actor: user, tenant: club을 매번 반복After:
scope: scope하나로 통일
멀티테넌트 앱에서 반복되는 컨텍스트 정보를 깔끔하게 관리할 수 있게 해준다.