Skip to main content

Command Palette

Search for a command to run...

Ash StateMachine

Published
•2 min read•View as Markdown

TLDR;

Ash StateMachine 은 상태 변경이 가능한 케이스를 명시하고, 미리 선언된 상태 변경만 가능하도록 한다.


왜 쓸까?

예를 들면, “새로 생성 → 진행 중 → 완료” 처럼 정해진 workflow 가 있다면, 로직으로 if/else 를 사용하는 것보다 상태머신으로 선언해 두는 게 안전하고 읽기 쉽다.


예제: Task

요구사항:

  • 기본 상태는 :new

  • :start 액션이 :new → :in_progress

  • :finish 액션이 :in_progress → :done

  • 언제든 :cancel로 :canceled

1) state 및 transition 정의

defmodule MyApp.Todo.Task do
  use Ash.Resource,
    extensions: [AshStateMachine]

  attributes do
    # ...
  end

  state_machine do
    initial_states [:new, :in_progress, :done, :canceled]
    default_initial_state :new

    transitions do
      transition :start,  from: :new,          to: :in_progress
      transition :finish, from: :in_progress,  to: :done
      transition :cancel, from: [:new, :in_progress], to: :canceled
    end
  end
  • Resource 의 extension 에 추가한다.

  • state_machine 블록에서 기본 설정

  • transitions 블록에서 가능한 상태변경을 정의한다.

  • 개인적으로 상태변경에 이름을 부여할 수 있다는 점도 마음에 든다.

2) 액션에서 상태 변경

  actions do
    defaults [:create, :read, :update]

    update :start do
      change transition_state(:in_progress)
    end

    update :finish do
      change transition_state(:done)
    end

    update :cancel do
      change transition_state(:canceled)
    end
  end
end
  • 단순하다. transition_state 를 호출하고 변경을 원하는 상태만 명시하면 끝이다.

State 컬럼은 어떻게 관리하는가?

  • 확장을 켜면 기본으로 :state attribute 가 생성된다.

  • 이름을 바꾸려면 state_attribute(:alternative_state)를 사용하면 된다.


허용되지 않는 변경을 하는 경우?

  • Changeset Validation 에서 error 를 기록한다.

마무리

내 경우 상태값이 10개가 넘는 케이스가 있어서 처음 적용해 보았다.
하지만 상태관리를 복잡하게 하지 않는다고 해도 충분히 사용할 가치가 있다고 생각한다.
Ash 를 사용하는 이유와 비슷하게, 상태변경을 위한 구현을 하는게 아니라 선언적으로 상태관리를 할 수 있다는 점이 가장 큰 장점이라고 생각한다.


Reference

  • Ash StateMachine hex docs [link]

More from this blog

Ash Typescript

TL;DRElixir의 Ash Framework로 정의한 리소스를 TypeScript와 바로 연결해 주는 툴.백엔드 스펙을 따로 문서화하지 않아도 타입 안정성과 동기화를 자동으로 보장한다. 왜 사용하는가? Elixir로 백엔드를, TypeScript로 프런트엔드를 작성하다 보면 변경되는 API 스펙을 항상 동기화된 상태로 관리하기 쉽지 않다.결국 동기화를 위해서는 Graphql 을 사용하거나, 별도의 툴이 필요하다.AshTypescript ...

Sep 28, 20253 min read

Ash Reactor

TLDR; Reactor는 복잡한 워크플로우를 단계별로 정의하고 실행하는 라이브러리다. 각 단계가 독립적으로 돌아가며, 병렬 처리와 에러 핸들링도 쉽게 할 수 있다. 왜 쓸까? 프로젝트에서 "데이터 조회 → 가공 → 외부 API 호출 → 저장" 같은 복잡한 로직을 처리할 때가 있다.이런걸 그냥 함수로 쭉 연결하면 코드가 복잡해지고 에러 처리도 어려워진다. Reactor를 쓰면 각 단계를 명확히 분리하고 선언적으로 워크플로우를 관리할 수 있다. ...

Sep 21, 20254 min read

Ash Oban

최근 작업중에 이메일 발송을 처리해야 했다.after_action 에 이마일 발송을 고려했는데 최종적으로는 AshOban 을 사용했다. AshOban이 뭔가? Ash Framework와 Oban을 이어주는 라이브러리다. 백그라운드 job을 Ash 스타일로 쓸 수 있게 해준다. 멤버 초대 이메일 보내기 클럽에 새 멤버를 초대하면 이메일을 보내야 하는 상황이다. 이걸 어떻게 처리할까? Action Hook으로 하면: 사용자가 서버에서 이메일 발송...

Aug 31, 20252 min read

kkbz.hop

14 posts