JavaScript

[JavaScript] XState 기초 이해하기

hid1 2023. 10. 20. 18:43
 

Stately Studio + XState docs | Stately

Stately Studio and XState docs

stately.ai

 

1. 상태 관리 라이브러리

XState는 상태 관리 라이브러리 중 하나이다.

유한 상태 기계(Finite State Machine, FSM) 기반으로 상태 기계 개념에 기초하여 만들어졌다.

 

2. 상태 기계(State Machines)란

상태 기계는 애플리케이션 로직을 구성한다.

만약 개 상태 기계를 만들었다고 하다면, 개 상태 기계의 상태를 asleep, awake으로 구성할 수 있다.

즉, 개는 자고 있거나 깨어있다라는 두 가지 상태를 가진다. 이는 개는 잠을 자는 동시에 깨어 있을 수 없으며, 개가 잠을 안 자고 깨어 있는 것도 불가능하다. 이처럼 상태 기계는 한 번에 하나의 상태에만 존재할 수 있다. 우리가 정의한 두 가지의 유한한 상태 내에서 움직인다. 그렇기에 유한 상태 기계라 한다.

 

3. 상태 전환( Transitions )

개 상태 기계에서 현재 asleep 상태에 있다고 하자. awake 상태로 전환하려면 어떻게 해야할까? 답은 이벤트를 발생시키면 전환이 다음 상태로 이동한다. wakes up 이벤트를 발생하면 awake로 상태가 전환되고 awake 상태에서 falls asleep 이벤트를 발생시키면 asleep 상태로 전환된다.

 

 

4. 상태 기계 작성

import { createMachine } from "xstate";

export const machine = createMachine(
  {
    id: "dog",
    initial: "asleep",
    states: {
      asleep: {
        on: {
          "wakes up": {
            target: "awake",
          },
        },
      },
      awake: {
        on: {
          "falls asleep": {
            target: "asleep",
          },
        },
      },
    },
  }
)

개 상태 기계를 정의한 코드이다. createMachine 함수를 사용하여 상태 기계를 생성할 수 있다.

initial 프로퍼티로 초기 상태를 정의 가능하다.

states를 통해 상태를 정의, on 을 통해 이벤트를 정의하고 target을 통해 다음 상태 목적지를 정의한다.

 

 

4. 액터 (Actor)

우리가 createMachine 함수로 작성한 상태 기계는 일종의 클래스이고 이를 실제로 실행하기 위한 인스턴스가 필요하다. 상태 기계를 실행하면 액터가 된다. 

import { createMachine, createActor } from 'xstate';

const dogMachine = createMachine({
  // 생략
})

const dogActor = createActor(dogMachine);

dogActor.start()

dogActor.send({ type: 'wakes up' })
dogActor.send({ type: 'falls asleep' })

createActor 함수를 사용하여 액터 (actor) 를 만들 수 있으며  start 메소드를 통해 상태 기계를 실행하고,  send 메소드를 통해 이벤트를 발생할 수 있다.

 

5. 컨텍스트(Context)와 액션(Actions)

컨텍스트는 액터에 데이터를 저장하는 방법이다.

이 상태 기계와 관련된 데이터를 넣을 수 있으며 모든 상태에서 컨텍스트를 사용할 수 있다.

import { createMachine, assign } from 'xstate';

const feedbackMachine = createMachine({
  context: {
    feedback: 'Some feedback',
  },
  on: {
    'update': {
      actions: assign({
        feedback: ({ event }) => event.feedback,
      }),
    },
  },
});

const feedbackActor = createActor(feedbackMachine).start()

feedbackActor.send({
  type: 'update',
  feedback: 'Some other feedback',
})

 

 

액션은 전환이 일어날 때 실행되어야 할 작업을 정의할 수 있다. 즉 이벤트가 발생하면 액션이 실행하게 된다.

위의 예제는 feedback 메시지와 함께 update 이벤트를 보내면 actions가 실행이 되는데, event 객체를 받아 컨텍스트의 feedback을 업데이트 하는 예제이다.

이때 assign은 컨텍스트에 데이터를 할당할 수 있는 액션이다. 컨텍스트는 불변이라 직접 수정할 수 없으며 대신 assign을 통해 업데이트 할 수 있게 제공한다.

actions: [(context, event) => console.log('action')]

참고로 actions는 첫번째로 context, 두번째로 event를 받으며 여러 액션들을 배열로 받을 수 있고 따로 함수로 작성하여 정의할 수 있다.

 

 

6. 리액트와 함께 사용하기

import { useMachine } from '@xstate/react';
import { dogMachine } from './dogMachine';

const App = () => {
  const [state, send, actor] = useMachine(dogMachine);

  return (
    <div>
      <div>개 상태: {state.value}</div>
      <button onClick={() => send({ type: 'awake' })}>깨어나기</button>
    </div>
  );
};

@xstate/react 패키지를 사용하여 hooks를 통해 리액트에서 사용할 수 있도록 지원한다. 

리액트 이외에도 다양한 패키지를 통해 프레임워크를 지원한다.

 

 

 

 

 

나 자신도 아직  XState의 모든 내용에 대해 다 알지 못하지만 그래도 기초적인 내용으로 간결하게 작성해 보았다. 

이외에도 더 많은 기능과 다양한 로직으로 상태 기계를 정의하고 사용할 수 있다. 

 

 

 

반응형