MobX는 React 애플리케이션의 상태 관리를 혁신적으로 단순화하는 강력한 라이브러리입니다. 그 핵심에는 '액션(actions)'이라는 개념이 자리 잡고 있습니다. MobX를 효과적으로 활용하고 예측 가능하며 유지보수가 용이한 애플리케이션을 구축하기 위해서는 액션에 대한 깊이 있는 이해가 필수적입니다. 이 블로그 포스트에서는 MobX 액션이 무엇인지, 왜 중요한지, 그리고 실제 코드 예시를 통해 어떻게 활용되는지 자세히 알아보겠습니다. 또한, MobX의 엄격 모드와 같은 고급 개념을 보완하여 실전 적용 팁도 추가하겠습니다.
MobX에서 '액션'이란 무엇인가요?
간단히 말해, MobX에서 액션은 '관찰 가능한 데이터를 수정하는 함수'입니다. 모든 상태 변경은 액션 내에서 이루어져야 한다는 원칙은 MobX의 견고성을 보장하는 핵심 메커니즘입니다. 액션의 주요 목적은 애플리케이션 상태의 변경 사항을 캡슐화하여, 이러한 변경 사항을 효율적으로 추적하고 관리할 수 있도록 하는 것입니다.
예를 들어, 비동기 API 호출이나 사용자 입력 처리처럼 복잡한 상태 업데이트를 액션으로 묶으면, 디버깅이 쉬워지고 상태 변경의 추적이 명확해집니다. 액션을 통해 모든 상태 수정이 예측 가능하고 특정 규칙을 따르도록 보장할 수 있으며, 이는 대규모 프로젝트에서 특히 빛을 발합니다.
액션의 주요 특징
MobX 액션은 다음과 같은 중요한 특징들을 가집니다. 이러한 특징들은 MobX가 단순한 상태 관리 도구가 아닌, 반응형 프로그래밍의 철학을 구현한 라이브러리임을 보여줍니다.
- 캡슐화: 액션은 관련된 작업을 함께 그룹화하여 상태 변경이 언제 발생하는지 명확하게 합니다. 이는 코드의 가독성을 높이고 유지보수를 용이하게 하는 데 크게 기여합니다. 예를 들어, 사용자 로그인과 관련된 여러 상태 변경(사용자 정보 저장, 로그인 상태 변경, 토큰 저장 등)을 하나의 로그인 액션으로 캡슐화할 수 있습니다. 이렇게 하면 코드가 모듈화되어 팀 협업 시 충돌을 최소화할 수 있습니다.
- 트랜잭션 관리: 여러 관찰 가능한 요소가 하나의 액션 내에서 업데이트될 때, MobX는 해당 업데이트를 일괄 처리하여 재렌더링을 최소화하고 성능을 최적화합니다. 이는 복잡한 상태 변경 시에도 애플리케이션의 반응성을 유지하는 데 큰 도움이 됩니다. UI가 여러 번 깜빡이는 것을 방지하고 부드러운 사용자 경험을 제공합니다. 특히, 대규모 리스트나 중첩된 객체 업데이트에서 이 기능이 유용합니다.
- 엄격 모드 준수: MobX의 엄격 모드(기본적으로 활성화됨)에서는 모든 관찰 가능한 요소의 수정이 액션 내에서 이루어져야 합니다. 이는 애플리케이션 전반에 걸쳐 상태 변경의 일관성을 유지하고, 디버깅을 용이하게 하는 데 결정적인 역할을 합니다. 엄격 모드는 예상치 못한 상태 변경을 방지하여 버그를 줄이는 데 도움을 줍니다. 만약 엄격 모드를 비활성화하면 성능이 약간 향상될 수 있지만, 개발 중에는 항상 활성화하는 것을 추천합니다.
MobX에서 액션 정의하기: 두 가지 방법
MobX에서 액션을 정의하는 방법은 크게 두 가지가 있습니다. 클래스 기반 스토어에서 주로 사용되며, React의 함수형 컴포넌트와도 잘 어우러집니다. 아래 예시들은 MobX 6.x 버전을 기준으로 하며, makeAutoObservable을 통해 자동으로 관찰 가능한 상태를 생성합니다.
1. @action 데코레이터 사용하기
클래스 기반 컴포넌트나 스토어에서 메서드를 액션으로 만들 때 가장 일반적으로 사용되는 방법입니다. 이 데코레이터는 메서드를 자동으로 액션으로 변환하여 상태 변경을 안전하게 처리합니다.
import { makeAutoObservable, action } from "mobx";
class TodoStore {
todos = [];
constructor() {
makeAutoObservable(this); // todos를 관찰 가능하게 만듭니다.
}
@action
addTodo(todo) {
this.todos.push(todo); // @action 데코레이터로 이 메서드가 상태를 수정할 수 있도록 MobX에 알립니다.
}
@action
removeTodo(index) {
this.todos.splice(index, 1); // 이 또한 액션으로 정의되어 안전하게 상태를 변경합니다.
}
}
const todoStore = new TodoStore();
위 예시에서 addTodo와 removeTodo 메서드는 @action 데코레이터가 붙어 있어, 이 메서드들이 todos 배열이라는 관찰 가능한 상태를 안전하게 변경할 수 있음을 MobX에게 알립니다. 데코레이터를 사용하면 코드가 간결해지며, TypeScript와도 잘 호환됩니다.
2. action 유틸리티 함수 사용하기
함수형 컴포넌트나 독립적인 함수에 액션을 적용할 때 유용합니다. 특히 클래스 메서드가 아닌 일반 함수에서 상태를 변경해야 할 때 유용합니다. 이 방법은 화살표 함수나 콜백을 래핑할 때 편리합니다.
import { makeAutoObservable, action } from "mobx";
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this); // count를 관찰 가능하게 만듭니다.
}
increment = action(() => {
this.count++; // action 함수로 래핑하여 상태 변경을 정의합니다.
});
decrement = action(() => {
this.count--; // 이 또한 action 함수로 래핑되어 있습니다.
});
}
const counterStore = new CounterStore();
여기서는 데코레이터 없이 action 함수를 사용하여 increment 및 decrement 메서드를 정의합니다. 이 방식은 함수 자체를 액션으로 정의할 때 유용하며, 비동기 액션(예: action(async () => {...}))에도 쉽게 적용할 수 있습니다.
실제 예제: MobX 액션을 사용한 ToDo 애플리케이션
MobX 액션이 실제 애플리케이션에서 어떻게 작동하는지 간단한 ToDo 애플리케이션 예시를 통해 살펴보겠습니다. 이 예제는 React와 MobX를 결합하여 상태 변경을 실시간으로 반영합니다. (MobX 6.x 및 React 18 기준)
import React, { useRef } from 'react';
import { observer } from 'mobx-react-lite';
import { makeAutoObservable, action } from 'mobx';
// 1. TodoStore: 애플리케이션의 상태와 액션을 정의합니다.
class TodoStore {
todos = []; // 관찰 가능한 상태
constructor() {
makeAutoObservable(this); // todos 배열을 관찰 가능하게 만듭니다.
}
// 액션: 새로운 할 일 항목을 추가합니다.
@action
addTodo(todo) {
if (todo.trim()) { // 빈 입력 방지
this.todos.push({ id: Date.now(), text: todo, completed: false });
}
}
// 액션: 할 일 항목을 제거합니다.
@action
removeTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
// 액션: 할 일 완료 상태 토글 (추가 기능으로 보완)
@action
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
}
const todoStore = new TodoStore(); // 스토어 인스턴스 생성
// 2. TodoList 컴포넌트: 스토어의 상태를 구독하고 UI를 렌더링합니다.
const TodoList = observer(() => {
const inputRef = useRef(null);
const handleAdd = () => {
const newTodo = inputRef.current.value;
todoStore.addTodo(newTodo);
inputRef.current.value = ''; // 입력 필드 초기화
};
return (
<div>
<h2>ToDo List</h2>
<div>
<input type="text" ref={inputRef} placeholder="새 할 일 입력..." />
<button onClick={handleAdd}>Add</button>
</div>
<ul>
{todoStore.todos.map((todo) => (
<li key={todo.id} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
<button onClick={() => todoStore.toggleTodo(todo.id)}>
{todo.completed ? 'Undo' : 'Complete'}
</button>
<button onClick={() => todoStore.removeTodo(todo.id)}>Remove</button>
</li>
))}
</ul>
</div>
);
});
// 3. App 컴포넌트: TodoList 컴포넌트를 포함하는 메인 애플리케이션입니다.
export default function App() {
return (
<div className="App">
<h1>My ToDo App with MobX</h1>
<TodoList />
</div>
);
}
예제 설명
- 상태 정의:
TodoStore내부에todos배열을 생성하여 애플리케이션의 핵심 상태를 정의합니다.makeAutoObservable(this)를 통해todos배열은 MobX에 의해 자동으로 관찰 가능하게 됩니다. 객체에 ID와 completed 필드를 추가하여 더 실전적인 구조로 보완했습니다. - 액션 메서드:
addTodo,removeTodo,toggleTodo메서드는todos배열을 조작합니다. 이들은 상태를 변경하는 유일한 경로를 제공하여 상태 변경의 예측 가능성을 높입니다.@action데코레이터를 추가하여 엄격 모드에서 안전하게 동작하도록 했습니다. 비동기 액션을 추가하려면runInAction을 사용해 트랜잭션을 관리할 수 있습니다. - 옵저버 컴포넌트:
observer로 감싸진TodoList컴포넌트는 스토어의 상태 변경을 감지합니다.todos배열에 항목이 추가되거나 제거되는 업데이트가 있을 때마다, MobX는 필요한 부분만 자동으로 재렌더링하여 효율적인 UI 업데이트를 보장합니다. useRef를 사용해 입력 처리를 개선했습니다.
이 예제를 실행하면 상태 변경이 즉시 UI에 반영되며, 여러 항목 업데이트 시에도 부드럽게 동작하는 것을 확인할 수 있습니다.
결론: MobX 액션의 중요성
액션은 React와 MobX를 사용하여 애플리케이션의 상태가 시간에 따라 어떻게 변하는지에 대한 명확성을 유지하는 데 중추적인 역할을 합니다. 액션은 코드를 체계적으로 유지하는 동시에 모든 변형이 정의된 경계 내에서 예측 가능하게 발생하도록 보장하여 애플리케이션의 유지보수성과 성능 최적화를 향상시킵니다.
'프로그래밍 > ReactJS' 카테고리의 다른 글
| ReactJS 개발, Jest로 더 견고하게! - 필수 테스트 개념과 실전 예시 (0) | 2025.10.18 |
|---|---|
| MobX 계산된 값: React 애플리케이션 성능 최적화의 핵심 (0) | 2025.10.18 |
| MobX 옵저버블: React 애플리케이션의 반응성을 극대화하는 핵! (0) | 2025.10.18 |
| React 상태 관리, MobX로 깔끔하게! 복잡함은 줄이고 반응성은 높이는 비결 (0) | 2025.10.18 |
| React 개발의 핵심: Redux와 Redux Saga로 상태 관리를 마스터하다 (0) | 2025.10.18 |