[javascript] MobX와 코드 간결성

MobX는 자바스크립트 상태 관리 라이브러리로서, 코드를 더욱 간결하고 관리하기 쉽게 만들어줍니다. 이번 블로그 글에서는 MobX의 사용을 통해 코드 간결성을 어떻게 달성할 수 있는지 살펴보겠습니다.

1. Observable 상태

MobX는 관찰 가능한(Observable) 상태를 제공합니다. 이는 상태 변화를 추적하고, 자동으로 감지하여 관련된 코드만 업데이트하는 것을 의미합니다. 이를 통해 코드의 복잡성을 줄일 수 있습니다.

예를 들어, 아래의 코드는 MobX가 없는 상태 관리 코드입니다.

let count = 0;

const increaseCount = () => {
  count += 1;
  render();
};

const render = () => {
  document.getElementById('count').textContent = count;
};

document.getElementById('increase-btn').addEventListener('click', increaseCount);

render();

위 코드에서는 count 변수의 값이 변경될 때마다 render 함수를 호출하여 화면에 변경된 값을 업데이트합니다. 이런식으로 상태 변경과 그에 따른 액션을 수동으로 처리해야 하기 때문에 코드의 복잡성이 증가하게 됩니다.

하지만 MobX를 사용하면 코드를 훨씬 간결하게 만들 수 있습니다.

import { observable } from 'mobx';

class CounterStore {
  @observable count = 0;

  increaseCount() {
    this.count += 1;
  }
}

const counterStore = new CounterStore();

document.getElementById('increase-btn').addEventListener('click', counterStore.increaseCount);

autorun(() => {
  document.getElementById('count').textContent = counterStore.count;
});

MobX를 사용하면 @observable 데코레이터를 사용하여 상태를 관찰 가능하게 만들고, 상태 변경에 따라 자동으로 업데이트됩니다. 이를 통해 상태와 관련된 코드만 작성하면 되기 때문에 코드가 훨씬 간결해집니다.

2. Computed 값

MobX는 computed 값을 지원합니다. Computed 값은 기존의 상태를 기반으로 계산되는 값으로, 필요한 경우에만 다시 계산되기 때문에 성능을 향상시킬 수 있습니다.

아래 코드는 MobX를 사용하지 않고 계산된 값에 대한 코드를 작성한 예시입니다.

let width = 10;
let height = 5;

const area = () => {
  return width * height;
};

const render = () => {
  document.getElementById('area').textContent = area();
};

document.getElementById('increase-width-btn').addEventListener('click', () => {
  width += 1;
  render();
});

document.getElementById('increase-height-btn').addEventListener('click', () => {
  height += 1;
  render();
});

render();

위 코드에서는 area 함수를 호출하여 계산된 값을 가져옵니다. 하지만 MobX를 사용하면 이를 훨씬 간단하게 처리할 수 있습니다.

import { observable, computed } from 'mobx';

class Rectangle {
  @observable width = 10;
  @observable height = 5;

  @computed get area() {
    return this.width * this.height;
  }
}

const rectangle = new Rectangle();

document.getElementById('increase-width-btn').addEventListener('click', () => {
  rectangle.width += 1;
});

document.getElementById('increase-height-btn').addEventListener('click', () => {
  rectangle.height += 1;
});

autorun(() => {
  document.getElementById('area').textContent = rectangle.area;
});

MobX에서는 @computed 데코레이터를 사용하여 계산된 값을 정의할 수 있습니다. 계산된 값은 의존하는 상태가 변경될 때에만 재계산되기 때문에 성능에 이점을 줄 수 있습니다.

3. 액션

MobX는 상태를 변경하는 액션을 추적하고 관련 코드만 업데이트합니다. 이를 통해 코드의 간결성과 가독성을 높일 수 있습니다.

예를 들어, 아래 코드는 MobX를 사용하지 않고 상태를 변경하는 코드입니다.

class TodoStore {
  todos = [];

  addTodo = (title, description) => {
    const newTodo = {
      id: Date.now(),
      title,
      description,
      completed: false,
    };
    this.todos.push(newTodo);
    render();
  };

  completeTodo = (id) => {
    this.todos.forEach((todo) => {
      if (todo.id === id) {
        todo.completed = true;
      }
    });
    render();
  };
}

const todoStore = new TodoStore();

document.getElementById('add-todo-form').addEventListener('submit', (event) => {
  event.preventDefault();

  const title = document.getElementById('title-input').value;
  const description = document.getElementById('description-input').value;

  todoStore.addTodo(title, description);

  document.getElementById('title-input').value = '';
  document.getElementById('description-input').value = '';
});

document.getElementById('todos-list').addEventListener('click', (event) => {
  if (event.target.classList.contains('complete-btn')) {
    const id = event.target.dataset.id;
    todoStore.completeTodo(Number(id));
  }
});

const render = () => {
  const todosListElement = document.getElementById('todos-list');
  todosListElement.innerHTML = '';

  todoStore.todos.forEach((todo) => {
    const todoElement = document.createElement('li');
    todoElement.innerHTML = `
      <div>${todo.title}</div>
      <div>${todo.description}</div>
      <button class="complete-btn" data-id="${todo.id}">Complete</button>
    `;

    if (todo.completed) {
      todoElement.classList.add('completed');
    }

    todosListElement.appendChild(todoElement);
  });
};

위 코드에서는 addTodocompleteTodo 두 가지 액션이 존재합니다. 상태를 변경한 후에는 항상 render 함수를 호출하여 화면에 업데이트하는 복잡성이 있습니다.

하지만 MobX를 사용하면 상태 변경 액션을 훨씬 간결하게 처리할 수 있습니다.

import { observable, action } from 'mobx';

class TodoStore {
  @observable todos = [];

  @action addTodo = (title, description) => {
    const newTodo = {
      id: Date.now(),
      title,
      description,
      completed: false,
    };
    this.todos.push(newTodo);
  };

  @action completeTodo = (id) => {
    const todo = this.todos.find((todo) => todo.id === id);
    if (todo) {
      todo.completed = true;
    }
  };
}

const todoStore = new TodoStore();

document.getElementById('add-todo-form').addEventListener('submit', (event) => {
  event.preventDefault();

  const title = document.getElementById('title-input').value;
  const description = document.getElementById('description-input').value;

  todoStore.addTodo(title, description);

  document.getElementById('title-input').value = '';
  document.getElementById('description-input').value = '';
});

document.getElementById('todos-list').addEventListener('click', (event) => {
  if (event.target.classList.contains('complete-btn')) {
    const id = event.target.dataset.id;
    todoStore.completeTodo(Number(id));
  }
});

autorun(() => {
  const todosListElement = document.getElementById('todos-list');
  todosListElement.innerHTML = '';

  todoStore.todos.forEach((todo) => {
    const todoElement = document.createElement('li');
    todoElement.innerHTML = `
      <div>${todo.title}</div>
      <div>${todo.description}</div>
      <button class="complete-btn" data-id="${todo.id}">Complete</button>
    `;

    if (todo.completed) {
      todoElement.classList.add('completed');
    }

    todosListElement.appendChild(todoElement);
  });
});

위 코드에서는 @action 데코레이터를 사용하여 액션을 정의합니다. MobX가 액션을 추적하여 자동으로 업데이트를 처리하므로, 별도의 render 함수 호출이 필요하지 않습니다.

마무리

MobX를 사용하면 코드를 더욱 간결하고 관리하기 쉽게 만들 수 있습니다. Observable 상태, Computed 값, 그리고 액션을 통해 코드의 복잡성을 줄이고, 가독성과 유지보수성을 높일 수 있습니다. MobX는 모든 스크립트 프로젝트에서 코드 간결성을 추구하는 개발자들에게 매우 유용한 도구입니다. MobX의 다양한 기능과 사용법에 대해서는 공식 문서를 참조하시기 바랍니다.

참고 자료