사이드 이펙트 처리 방식
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
- "사이드 이펙트"의 정의 및 Redux에서의 역할
- Redux에서 사이드 이펙트를 관리하는 일반적인 도구
- 다양한 사용 사례별 도구 사용에 대한 권장사항
Redux와 사이드 이펙트
사이드 이펙트 개요
Redux 스토어 자체는 비동기 로직에 대해 아무것도 알지 못합니다. 동기적으로 액션을 디스패치하고, 루트 리듀서 함수를 호출하여 상태를 업데이트하며, UI에 변경 사항을 알리는 방법만 알고 있습니다. 모든 비동기 작업은 스토어 외부에서 발생해야 합니다.
Redux 리듀서는 절대 "사이드 이펙트"를 포함해서는 안 됩니다. "사이드 이펙트"란 함수에서 값을 반환하는 것 외부에서 관찰 가능한 상태나 동작의 변화를 의미합니다. 일반적인 사이드 이펙트 유형은 다음과 같습니다:
-
콘솔에 값 기록하기
-
파일 저장하기
-
비동기 타이머 설정하기
-
AJAX HTTP 요청 보내기
-
함수 외부에 존재하는 상태 수정하거나 함수 인자 변형하기
-
난수 또는 고유 ID 생성하기(예:
Math.random()또는Date.now())
그러나 실제 애플리케이션은 이런 작업들을 어딘가에서 반드시 수행해야 합니다. 그렇다면 리듀서에 사이드 이펙트를 넣을 수 없다면, 어디에 넣어야 할까요?
미들웨어와 사이드 이펙트
Redux 미들웨어는 사이드 이펙트가 있는 로직 작성을 가능하게 설계되었습니다.
Redux 미들웨어는 디스패치된 액션을 감지했을 때 어떤 작업이든 수행할 수 있습니다: 로깅, 액션 수정, 액션 지연, 비동기 호출 등. 또한 미들웨어가 실제 store.dispatch 함수 주위에 파이프라인을 형성하므로, 미들웨어가 해당 값을 가로채 리듀서에 도달하지 못하게 한다면 dispatch에 일반 액션 객체가 아닌 것을 전달할 수도 있습니다.
미들웨어는 dispatch와 getState에도 접근할 수 있습니다. 이는 미들웨어 내부에 비동기 로직을 작성하면서도 액션을 디스패치해 Redux 스토어와 상호작용할 수 있음을 의미합니다.
이 때문에 Redux의 사이드 이펙트와 비동기 로직은 일반적으로 미들웨어를 통해 구현됩니다.
사이드 이펙트 사용 사례
실제로 일반적인 Redux 앱에서 사이드 이펙트의 가장 흔한 사용 사례는 서버에서 데이터를 가져와 캐싱하는 것입니다.
Redux에 특화된 또 다른 사용 사례는 디스패치된 액션이나 상태 변경에 _반응_하여 추가 로직(예: 더 많은 액션 디스패치)을 실행하는 로직을 작성하는 것입니다.
권장사항
각 사용 사례에 가장 적합한 도구를 사용하는 것을 권장합니다(권장 이유 및 각 도구에 대한 자세한 내용은 아래 참조):
데이터 가져오기
- 데이터 가져오기 및 캐싱의 기본 접근 방식으로 RTK Query 사용
- 어떤 이유로 RTKQ가 완전히 맞지 않으면
createAsyncThunk사용 - 다른 방법이 모두 실패할 때만 직접 작성한 썽크(thunk)로 대체
- 데이터 가져오기에는 사가(saga)나 옵저버블(observable)을 사용하지 마세요!
액션/상태 변경에 반응, 비동기 워크플로
- 스토어 업데이트에 응답하고 장기 실행 비동기 워크플로를 작성하기 위한 기본으로 RTK 리스너 사용
- 리스너로 사용 사례를 충분히 해결할 수 없을 때만 사가/옵저버블 사용
상태 접근 로직
- 복잡한 동기 로직과 적당한 수준의 비동기 로직에는 썽크 사용,
getState접근 및 다중 액션 디스패치 포함
데이터 가져오기에 RTK Query를 사용하는 이유
React 문서의 'Effect에서 데이터 가져오기 대안' 섹션에 따르면, 서버 측 프레임워크에 내장된 데이터 가져오기 접근법이나 클라이언트 측 캐시를 사용해야 합니다. 데이터 가져오기 및 캐시 관리 코드를 직접 작성해서는 안 됩니다.
RTK Query는 Redux 기반 애플리케이션을 위한 완전한 데이터 가져오기 및 캐싱 레이어로 특별히 설계되었습니다. 모든 가져오기, 캐싱, 로딩 상태 로직을 관리하며, 직접 구현 시 흔히 누락하거나 처리하기 어려운 다양한 경계 사례를 해결하고 내장된 캐시 수명 주기 관리를 제공합니다. 또한 자동 생성된 React 훅을 통해 데이터를 쉽게 가져와 사용할 수 있습니다.
사가의 복잡성은 도움이 되지 않으며, 여전히 모든 캐싱 및 로딩 상태 관리 로직을 직접 작성해야 하므로 데이터 가져오기에는 사가 사용을 특별히 권장하지 않습니다.
반응형 로직에 리스너를 사용하는 이유
RTK 리스너 미들웨어는 의도적으로 사용하기 쉽게 설계되었습니다. 표준 async/await 구문을 사용하며, 가장 일반적인 반응형 사용 사례(액션 또는 상태 변경에 응답, 디바운싱, 지연)를 포함해 여러 고급 사례(자식 작업 실행)까지 처리합니다. 번들 크기가 작고(~3KB), Redux Toolkit에 포함되어 있으며 TypeScript와 완벽하게 호환됩니다.
다음과 같은 여러 이유로 대부분의 반응형 로직에 대해 사가(sagas)나 옵저버블(observables) 사용을 특히 권장하지 않습니다:
-
사가(sagas): 제너레이터 함수 구문과 사가 효과 동작을 이해해야 함, 추가 액션 디스패치로 인한 간접적 레이어 증가, TypeScript 지원 부족, 대부분의 Redux 사용 사례에 불필요한 과도한 복잡성
-
옵저버블(observables): RxJS API와 개념 모델 이해 필요, 디버깅 어려움, 번들 크기 상당히 증가
일반적인 사이드 이펙트 처리 방법
Redux로 사이드 이펙트를 관리하는 가장 저수준 기법은 특정 액션을 수신하고 로직을 실행하는 커스텀 미들웨어를 작성하는 것입니다. 하지만 실제로는 거의 사용되지 않습니다. 대신 대부분의 애플리케이션은 역사적으로 생태계에서 사용 가능한 일반적인 Redux 사이드 이펙트 미들웨어(썽크(thunks), 사가(sagas), 옵저버블(observables)) 중 하나를 사용해 왔습니다. 각각 고유한 사용 사례와 장단점이 있습니다.
최근 공식 Redux Toolkit 패키지는 사이드 이펙트 관리를 위한 두 가지 새로운 API를 추가했습니다: 반응형 로직 작성을 위한 "리스너" 미들웨어와 서버 상태 조회 및 캐싱을 위한 RTK Query입니다.
썽크(Thunks)
Redux "썽크" 미들웨어는 전통적으로 비동기 로직 작성에 가장 널리 사용된 미들웨어입니다.
썽크는 함수를 dispatch에 전달하는 방식으로 동작합니다. 썽크 미들웨어는 이 함수를 가로채 호출하며 theThunkFunction(dispatch, getState)를 전달합니다. 썽크 함수는 이제 모든 동기/비동기 로직을 실행하고 스토어와 상호작용할 수 있습니다.
썽크 사용 사례
썽크는 dispatch 및 getState 접근이 필요한 복잡한 동기 로직이나, "비동기 데이터 조회 후 결과와 함께 액션 디스패치" 같은 단일 요청과 같은 중간 수준 비동기 로직에 가장 적합합니다.
전통적으로 썽크를 기본 접근법으로 권장해 왔으며, Redux Toolkit은 특히 "요청 및 디스패치" 사용 사례를 위해 createAsyncThunk API를 포함합니다. 다른 사용 사례에서는 직접 썽크 함수를 작성할 수 있습니다.
썽크 장단점
-
👍: 함수만 작성하면 됨, 모든 로직 포함 가능
-
👎: 디스패치된 액션에 응답 불가, 명령형(imperative), 취소 불가
const thunkMiddleware =
({ dispatch, getState }) =>
next =>
action => {
if (typeof action === 'function') {
return action(dispatch, getState)
}
return next(action)
}
// Original "hand-written" thunk fetch request pattern
const fetchUserById = userId => {
return async (dispatch, getState) => {
// Dispatch "pending" action to help track loading state
dispatch(fetchUserStarted())
// Need to pull this out to have correct error handling
let lastAction
try {
const user = await userApi.getUserById(userId)
// Dispatch "fulfilled" action on success
lastAction = fetchUserSucceeded(user)
} catch (err) {
// Dispatch "rejected" action on failure
lastAction = fetchUserFailed(err.message)
}
dispatch(lastAction)
}
}
// Similar request with `createAsyncThunk`
const fetchUserById2 = createAsyncThunk('fetchUserById', async userId => {
const user = await userApi.getUserById(userId)
return user
})
사가(Sagas)
Redux-Saga 미들웨어는 전통적으로 썽크 다음으로 가장 일반적인 사이드 이펙트 도구였습니다. 백엔드 "사가" 패턴에서 영감을 받았으며, 장기 실행 워크플로가 시스템 전체에서 트리거된 이벤트에 응답할 수 있습니다.
개념적으로 사가는 Redux 애플리케이션 내부의 "백그라운드 스레드"로 생각할 수 있으며, 디스패치된 액션을 수신하고 추가 로직을 실행할 수 있는 기능을 가집니다.
사가는 제너레이터 함수를 사용해 작성됩니다. 사가 함수는 사이드 이펙트에 대한 _설명_을 반환하고 실행을 일시 중지하며, 사가 미들웨어는 사이드 이펙트를 실행하고 결과와 함께 사가 함수를 재개할 책임이 있습니다. redux-saga 라이브러리는 다음과 같은 다양한 효과 정의를 포함합니다:
-
call: 비동기 함수 실행 후 프로미스가 해결될 때 결과 반환 -
put: Redux 액션 디스패치 -
fork: 추가 작업을 수행할 수 있는 "자식 사가" 생성(추가 스레드와 유사) -
takeLatest: 지정된 Redux 액션 수신, 실행할 사가 함수 트리거, 재디스패치 시 이전 실행 사가 취소
사가 사용 사례
사가는 극도로 강력하며, "백그라운드 스레드" 유형 동작이나 디바운싱/취소가 필요한 고도로 복잡한 비동기 워크플로에 가장 적합합니다.
Saga 사용자들은 종종 사가 함수가 원하는 이펙트에 대한 _설명_만 반환한다는 점을 주요 장점으로 꼽으며, 이로 인해 테스트가 더 용이하다고 말합니다.
사가(Saga)의 장단점
-
👍: 이펙트 설명만 반환하므로 테스트 용이함 / 강력한 이펙트 모델 / 일시 중지 및 취소 기능
-
👎: 제너레이터 함수 복잡성 / 고유한 사가 이펙트 API / 사가 테스트는 종종 구현 결과만 검증하고 로직 수정시 테스트 재작성 필요 → 유용성 감소 / TypeScript와의 호환성 부족
import { call, put, takeEvery } from 'redux-saga/effects'
// "Worker" saga: will be fired on USER_FETCH_REQUESTED actions
function* fetchUser(action) {
yield put(fetchUserStarted())
try {
const user = yield call(userApi.getUserById, action.payload.userId)
yield put(fetchUserSucceeded(user))
} catch (err) {
yield put(fetchUserFailed(err.message))
}
}
// "Watcher" saga: starts fetchUser on each `USER_FETCH_REQUESTED` action
function* fetchUserWatcher() {
yield takeEvery('USER_FETCH_REQUESTED', fetchUser)
}
// Can use also use sagas for complex async workflows with "child tasks":
function* fetchAll() {
const task1 = yield fork(fetchResource, 'users')
const task2 = yield fork(fetchResource, 'comments')
yield delay(1000)
}
function* fetchResource(resource) {
const { data } = yield call(api.fetch, resource)
yield put(receiveData(data))
}
옵저버블(Observables)
Redux-Observable 미들웨어는 "epics"라 불리는 처리 파이프라인을 생성하기 위해 RxJS 옵저버블을 사용합니다.
RxJS는 프레임워크에 구애받지 않는 라이브러리이므로, 옵저버블 사용자들은 다양한 플랫폼에서 재사용 가능한 지식을 주요 장점으로 강조합니다. 또한 RxJS는 취소나 디바운싱 같은 타이밍 처리를 위한 선언적 파이프라인 구축이 가능합니다.
옵저버블 사용 사례
사가와 유사하게 옵저버블은 고도로 복잡한 비동기 워크플로에 적합하며, "백그라운드 스레드"형 동작이나 디바운싱/취소가 필요한 경우에 최적입니다.
옵저버블 장단점
-
👍: 매우 강력한 데이터 흐름 모델 / Redux와 독립적으로 RxJS 지식 활용 가능 / 선언적 문법
-
👎: RxJS API 복잡성 / 개념적 진입 장벽 / 디버깅 어려움 / 번들 크기 증가
// Typical AJAX example:
const fetchUserEpic = action$ =>
action$.pipe(
filter(fetchUser.match),
mergeMap(action =>
ajax
.getJSON(`https://api.github.com/users/${action.payload}`)
.pipe(map(response => fetchUserFulfilled(response)))
)
)
// Can write highly complex async pipelines, including delays,
// cancellation, debouncing, and error handling:
const fetchReposEpic = action$ =>
action$.pipe(
filter(fetchReposInput.match),
debounceTime(300),
switchMap(action =>
of(fetchReposStart()).pipe(
concat(
searchRepos(action.payload).pipe(
map(payload => fetchReposSuccess(payload.items)),
catchError(error => of(fetchReposError(error)))
)
)
)
)
)
리스너(Listeners)
Redux Toolkit은 "반응형" 로직 처리를 위한 createListenerMiddleware API를 포함합니다. 사가나 옵저버블보다 가볍게 설계된 대안으로, 동일한 사용 사례의 90%를 처리하면서 더 작은 번들 크기, 간단한 API, 향상된 TypeScript 지원을 제공합니다.
개념적으로 React의 useEffect 훅과 유사하지만, Redux 스토어 업데이트에 적용됩니다.
리스너 미들웨어는 액션 매칭을 통해 effect 콜백 실행 시점을 결정합니다. 썽크(thunk)와 마찬가지로 effect 콜백은 동기/비동기 처리가 가능하며 dispatch와 getState에 접근할 수 있습니다. 또한 다음과 같은 비동기 워크플로 구축을 위한 기본 기능이 포함된 listenerApi 객체를 받습니다:
-
condition(): 특정 액션 디스패치 또는 상태 변경 발생시까지 대기 -
cancelActiveListeners(): 실행 중인 이펙트 인스턴스 취소 -
fork(): 추가 작업 가능한 "자식 태스크" 생성
이러한 기본 기능 덕분에 리스너는 Redux-Saga의 거의 모든 이펙트 동작을 구현할 수 있습니다.
리스너 사용 사례
리스너는 경량 스토어 지속성, 액션 디스패치 시 추가 로직 트리거, 상태 변경 감시, 장기 실행 "백그라운드 스레드"형 복잡한 비동기 워크플로 등 다양한 작업에 활용 가능합니다.
또한 런타임 중 특수 add/removeListener 액션 디스패치로 리스너 항목을 동적으로 추가/제거할 수 있습니다. 이는 React의 useEffect 훅과 자연스럽게 연동되며, 컴포넌트 생명주기에 대응하는 추가 동작 구현에 유용합니다.
리스너 장단점
-
👍: Redux Toolkit 내장 / 친숙한
async/await문법 / 썽크와 유사함 / 가벼운 개념과 크기 / TypeScript와의 우수한 호환성 -
👎: 상대적으로 신규 기술로 검증 부족 / 사가/옵저버블에 비해 유연성 다소 낮음
// Create the middleware instance and methods
const listenerMiddleware = createListenerMiddleware()
// Add one or more listener entries that look for specific actions.
// They may contain any sync or async logic, similar to thunks.
listenerMiddleware.startListening({
actionCreator: todoAdded,
effect: async (action, listenerApi) => {
// Run whatever additional side-effect-y logic you want here
console.log('Todo added: ', action.payload.text)
// Can cancel other running instances
listenerApi.cancelActiveListeners()
// Run async logic
const data = await fetchData()
// Use the listener API methods to dispatch, get state,
// unsubscribe the listener, start child tasks, and more
listenerApi.dispatch(todoAdded('Buy pet food'))
}
})
listenerMiddleware.startListening({
// Can match against actions _or_ state changes/contents
predicate: (action, currentState, previousState) => {
return currentState.counter.value !== previousState.counter.value
},
// Listeners can have long-running async workflows
effect: async (action, listenerApi) => {
// Pause until action dispatched or state changed
if (await listenerApi.condition(matchSomeAction)) {
// Spawn "child tasks" that can do more work and return results
const task = listenerApi.fork(async forkApi => {
// Can pause execution
await forkApi.delay(5)
// Complete the child by returning a value
return 42
})
// Unwrap the child result in the listener
const result = await task.result
if (result.status === 'ok') {
console.log('Child succeeded: ', result.value)
}
}
}
})
RTK Query
Redux Toolkit은 RTK Query를 포함하며, 이는 Redux 앱을 위한 전용 데이터 가져오기 및 캐싱 솔루션입니다. 웹 애플리케이션에서 데이터 로딩을 단순화하고 수작업 데이터 페칭 및 캐싱 로직 작성을 불필요하게 합니다.
RTK Query는 여러 "엔드포인트"로 구성된 API 정의를 생성하는 데 의존합니다. 엔드포인트는 데이터를 가져오는 "쿼리" 또는 서버에 업데이트를 전송하는 "뮤테이션"이 될 수 있습니다. RTKQ는 내부적으로 데이터 가져오기와 캐싱을 관리하며, 각 캐시 항목의 사용량을 추적하고 더 이상 필요하지 않은 캐시 데이터를 제거합니다. 또한 서버 상태를 업데이트하는 뮤테이션에 따라 데이터를 자동으로 재요청하도록 트리거하는 고유한 "태그" 시스템을 자랑합니다.
Redux의 나머지 부분과 마찬가지로 RTK Query는 핵심적으로 UI 프레임워크에 종속되지 않으며 어떤 UI 프레임워크와도 함께 사용할 수 있습니다. 하지만 내장된 React 통합 기능도 제공되어 각 엔드포인트에 대한 React 훅을 자동으로 생성합니다. 이를 통해 React 컴포넌트에서 데이터를 가져오고 업데이트하기 위한 직관적이고 간단한 API를 제공합니다.
RTKQ는 기본적으로 fetch 기반 구현을 제공하며 REST API와 원활하게 작동합니다. 또한 GraphQL API와 함께 사용할 수 있을 정도로 유연하며, 임의의 비동기 함수와 작동하도록 구성할 수도 있어 Firebase, Supabase 같은 외부 SDK나 사용자 정의 비동기 로직과 통합이 가능합니다.
RTKQ는 또한 엔드포인트 "라이프사이클 메서드" 같은 강력한 기능을 갖추고 있어 캐시 항목이 추가되거나 제거될 때 로직을 실행할 수 있습니다. 이는 채팅방 초기 데이터를 가져온 후 추가 메시지를 위해 소켓을 구독하고, 해당 메시지로 캐시를 업데이트하는 시나리오 등에 활용될 수 있습니다.
RTK Query 사용 사례
RTK Query는 서버 상태의 데이터 가져오기 및 캐싱 사용 사례를 해결하기 위해 특별히 구축되었습니다.
RTK Query 장단점
-
👍: RTK에 내장되어 있으며, 데이터 가져오기 및 로딩 상태 관리를 위해 어떤 코드(썽크, 셀렉터, 이펙트, 리듀서)도 작성할 필요가 없음. TS와 완벽 호환되며 Redux 스토어의 나머지 부분과 통합됨. 내장된 React 훅 제공
-
👎: 의도적으로 "정규화된" 방식 대신 "문서"-스타일 캐시를 사용함. 일회성 번들 크기 증가 발생
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import type { Pokemon } from './types'
// Create an API definition using a base URL and expected endpoints
export const api = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: builder => ({
getPokemonByName: builder.query<Pokemon, string>({
query: name => `pokemon/${name}`
}),
getPosts: builder.query<Post[], void>({
query: () => '/posts'
}),
addNewPost: builder.mutation<void, Post>({
query: initialPost => ({
url: '/posts',
method: 'POST',
// Include the entire post object as the body of the request
body: initialPost
})
})
})
})
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = api
export default function App() {
// Using a query hook automatically fetches data and returns query values
const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
// render UI based on data and loading state
}
다른 접근법
커스텀 미들웨어
썽크, 사가, 옵저버블, 리스너가 모두 Redux 미들웨어 형태이고(또한 RTK Query는 자체 커스텀 미들웨어 포함), 이러한 도구들로 사용 사례를 충분히 처리할 수 없다면 항상 자신만의 커스텀 미들웨어를 작성할 수 있습니다.
앱 로직의 대부분을 관리하는 기법으로 커스텀 미들웨어 사용은 특별히 권장하지 않습니다! 일부 사용자가 수십 개의 커스텀 미들웨어를 만들어 각 앱 기능별로 할당한 사례가 있습니다. 이는 각 미들웨어가 dispatch 호출 시마다 실행되어야 하므로 상당한 오버헤드를 초래합니다. 대신 썽크나 리스너 같은 범용 미들웨어를 단일 인스턴스로 추가해 다양한 로직 덩어리를 처리하는 것이 더 나은 접근법입니다.
const delayedActionMiddleware = storeAPI => next => action => {
if (action.type === 'todos/todoAdded') {
setTimeout(() => {
// Delay this action by one second
next(action)
}, 1000)
return
}
return next(action)
}
웹소켓
많은 앱은 주로 서버로부터 스트리밍 업데이트를 수신하기 위해 웹소켓이나 다른 형태의 지속적 연결을 사용합니다.
일반적으로 Redux 앱에서 웹소켓 사용 대부분은 커스텀 미들웨어 내부에 위치해야 합니다. 그 이유는 다음과 같습니다:
-
미들웨어는 애플리케이션 수명 주기 동안 존재합니다
-
스토어 자체와 마찬가지로 전체 앱에서 사용할 단일 연결 인스턴스만 필요합니다
-
미들웨어는 디스패치된 모든 액션을 확인하고 직접 액션을 디스패치할 수 있습니다. 즉 디스패치된 액션을 가져와 웹소켓으로 전송되는 메시지로 변환하고, 웹소켓에서 메시지를 수신하면 새 액션을 디스패치할 수 있습니다.
-
웹소켓 연결 인스턴스는 직렬화할 수 없으므로 스토어 상태 자체에 포함되어서는 안 됨
애플리케이션 요구사항에 따라 미들웨어 초기화 과정의 일부로 소켓을 생성하거나, 초기화 액션 디스패치 시 미들웨어 내에서 주문형으로 소켓을 생성하거나, 별도 모듈 파일에 생성해 다른 곳에서 접근할 수 있게 할 수 있습니다.
웹소켓은 RTK Query 라이프사이클 콜백에서도 사용될 수 있으며, 메시지 수신 시 RTKQ 캐시에 업데이트를 적용하는 방식으로 응답할 수 있습니다.
XState
상태 머신은 시스템의 가능한 상태와 상태 간 전환을 정의하고, 전환 발생 시 사이드 이펙트를 트리거하는 데 매우 유용할 수 있습니다.
Redux 리듀서는 진정한 유한 상태 기계(FSM)로 작성될 수 있지만, RTK는 이를 지원하는 기능을 포함하지 않습니다. 실제로는 주로 디스패치된 액션에 따라 상태 업데이트 방식을 결정하는 부분적 상태 기머로 동작합니다. 리스너, 사가, 옵저버블은 "디스패치 후 사이드 이펙트 실행" 측면에 사용할 수 있으나, 특정 시점에만 사이드 이펙트가 실행되도록 보장하려면 추가 작업이 필요할 수 있습니다.
XState는 진정한 상태 기계를 정의하고 실행하기 위한 강력한 라이브러리로, 이벤트 기반 상태 전환 관리와 관련 사이드 이펙트 트리거를 포함합니다. 또한 그래픽 편집기로 상태 기계 정의를 생성한 후 실행 로직에 로드할 수 있는 연관 도구를 제공합니다.
현재 XState와 Redux 간의 공식 통합은 없지만, XState 머신을 Redux 리듀서로 사용하는 것이 가능합니다. XState 개발자는 XState를 Redux 사이드 이펙트 미들웨어로 사용하는 유용한 개념 증명(POC)을 작성했습니다:
추가 정보
-
발표 자료: Redux 비동기 로직의 진화
-
미들웨어와 사이드 이펙트 필요성 근거:
-
문서 및 튜토리얼:
-
아티클 및 비교 자료: