본문으로 건너뛰기
yceffort
PostsSeriesTagsAbout🧪 Research
EN

Tweaks

theme
accent palette
film grain
minimal mode
BACK TO INDEX
◆ ESSAY
--min
--year
KOoriginal

mailMail icongithubtwitter
yceffort
•
© 2026
•
https://yceffort.kr
BACK TO INDEX
◆ ESSAY

MobX를 공부하자 (2)

avatar
yceffort
2020-08-24 · 4분
4min
2020year
KOoriginal
reactmobx

MobX 실제로 적용해보기

import React, {Component} from 'react'

import {observable} from 'mobx'
import {observer} from 'mobx-react'

// @observer 데코레이터가 장착되어 있는 리액트 컴포넌트는
// @observable로 되어 있는 모든 것들을 rendering 하는 와중에 사용한다.
@observer
class Counter extends Component {
  // @observable 은 @obsever컴포넌트에게 변화를 감시해야 하는 값을 알려준다.
  @observable count = 0

  handleDec = () => {
    this.count--
  }

  handleInc = () => {
    this.count++
  }

  render() {
    return (
      <div>
        Counter: {this.count} <br />
        <button onClick={this.handleDec}>-</button>
        <button onClick={this.handleInc}>+</button>
      </div>
    )
  }
}

export default Counter

https://codesandbox.io/embed/mobx-9z6dh?fontsize=14&hidenavigation=1&theme=dark

위 state를 상위 컴포넌트에서 props로 던지는 방식으로 바꿔보았다.

import React, {Component} from 'react'
import ReactDOM from 'react-dom'

import {observable} from 'mobx'
import {observer} from 'mobx-react'

const appState = observable({
  count: 0,
})

appState.increment = function () {
  this.count++
}

appState.decrement = function () {
  this.count--
}

@observer
class Counter extends Component {
  render() {
    return (
      <div>
        Counter: {this.props.store.count} <br />
        <button onClick={this.handleInc}>+</button>
        <button onClick={this.handleDsc}>-</button>
      </div>
    )
  }

  handleInc = () => {
    this.props.store.increment()
  }

  handleDsc = () => {
    this.props.store.decrement()
  }
}

const rootElement = document.getElementById('root')
ReactDOM.render(
  <React.StrictMode>
    <Counter store={appState} />
  </React.StrictMode>,
  rootElement,
)

mobx devtools을 활용하면, state 값에 대한 디버그도 할 수 있다.

import {observable, computed} from 'mobx'
import React from 'react'
import ReactDOM from 'react-dom'
import {observer} from 'mobx-react'

const t = new (class Temperature {
  // 온도 단위와 온도를 추적
  @observable unit = 'C'
  @observable temperatureCelsius = 25

  @computed get temperatureKelvin() {
    console.log('calculating Kelvin')
    return this.temperatureCelsius * (9 / 5) + 32
  }

  @computed get temperatureFahrenheit() {
    console.log('calculating Fahrenheit')
    return this.temperatureCelsius + 273.15
  }

  // observable의 값이 바뀔 때마다, 자동으로 변경된 값을 리턴 (엑셀 처럼!)
  @computed get temperature() {
    console.log('calculating temperature')
    switch (this.unit) {
      case 'K':
        return this.temperatureKelvin + 'K'
      case 'F':
        return this.temperatureFahrenheit + 'F'
      case 'C':
        return this.temperatureCelsius + 'C'
      default:
        throw new Error('Unexpected unit.')
    }
  }
})()

const App = observer(({temperature}) => (
  <div>
    {temperature.temperature} <br />
    <button onClick={() => (temperature.unit = 'F')}>F</button>
    <button onClick={() => (temperature.unit = 'K')}>K</button>
    <button onClick={() => (temperature.unit = 'C')}>C</button>
  </div>
))

ReactDOM.render(<App temperature={t} />, document.getElementById('root'))

데코레이터를 사용하지 않고, 마찬가지로 observable만 사용해서 똑같이 구현할 수 있다.

import {observable} from 'mobx'
import React from 'react'
import ReactDOM from 'react-dom'
import {observer} from 'mobx-react'

const t = observable({
  unit: 'C',
  temperatureCelsius: 25,
  temperatureKelvin: function () {
    console.log('calculating Kelvin')
    return this.temperatureCelsius * (9 / 5) + 32
  },
  temperatureFahrenheit: function () {
    console.log('calculating Fahrenheit')
    return this.temperatureCelsius + 273.15
  },
  temperature: function () {
    console.log('calculating temperature')
    switch (this.unit) {
      case 'K':
        return this.temperatureKelvin() + 'K'
      case 'F':
        return this.temperatureFahrenheit() + 'F'
      case 'C':
        return this.temperatureCelsius + 'C'
      default:
        throw new Error('Unexpected unit.')
    }
  },
})

const App = observer(({temperature}) => (
  <div>
    {temperature.temperature()} <br />
    <button onClick={() => (temperature.unit = 'F')}>F</button>
    <button onClick={() => (temperature.unit = 'K')}>K</button>
    <button onClick={() => (temperature.unit = 'C')}>C</button>
  </div>
))

ReactDOM.render(<App temperature={t} />, document.getElementById('root'))

array도 observable이 가능하다. 다만, 이전 포스트에서 얘기 한 것처럼, 진짜 자바스크립트의 array와는 다른 측면이 있기 때문에 처리에 주의 해야 한다.

import { observable, computed, action, asMap } from "mobx";
import React from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";

class Temperature {
  id = Math.random();
  // 온도 단위와 온도를 추적
  @observable unit = "C";
  @observable temperatureCelsius = 25;

  @computed get temperatureKelvin() {
    console.log("calculating Kelvin");
    return this.temperatureCelsius * (9 / 5) + 32;
  }

  @computed get temperatureFahrenheit() {
    console.log("calculating Fahrenheit");
    return this.temperatureCelsius + 273.15;
  }

  // observable의 값이 바뀔 때마다, 자동으로 변경된 값을 리턴 (엑셀 처럼!)
  @computed get temperature() {
    console.log("calculating temperature");
    switch (this.unit) {
      case "K":
        return this.temperatureKelvin + "K";
      case "F":
        return this.temperatureFahrenheit + "F";
      case "C":
        return this.temperatureCelsius + "C";
      default:
        throw new Error("Unexpected unit.");
    }
  }

  // action을 정의할 수 있다.
  @action
  setUnit(newUnit) {
    this.unit = newUnit;
  }

  @action
  setCelsius(degrees) {
    this.temperatureCelsius = degrees;
  }

  @action("update temperature and unit")
  setTemperatureAndUnit(degrees, unit) {
    this.setCelsius(degrees);
    this.setUnit(unit);
  }
}

// 일반적인 array도 가능하다.
// const temps = observable([]);
// temps.push(new Temperature());
// temps.push(new Temperature());
// temps.push(new Temperature());

// array 선언
const temps = observable.map({
  Amsterdam: new Temperature(),
  Rome: new Temperature(),
  Seoul: new Temperature()
});

console.log(temps.entries());

const App = observer(({ temperatures }) => (
  <div>
   {* 진짜 javascript array와는 다르기 때문에 처리에 유의해야 한다. *}
   {* 참고: https://mobx.js.org/refguide/map.html *}
    {[...temperatures.entries()].map(([city, t]) => (
      <div key={t.id}>
        <div>
          ({city}) =&gt; {t.temperature}
        </div>
        <button onClick={() => t.setUnit("F")}>F</button>
        <button onClick={() => t.setUnit("K")}>K</button>
        <button onClick={() => t.setUnit("C")}>C</button>
      </div>
    ))}
  </div>
));

ReactDOM.render(<App temperatures={temps} />, document.getElementById("root"));

관련 글

  • #ai#essay#frontend

    프론트엔드는 어디서 왔고, 에이전트 이후 어디로 가는가

    층은 왜 쌓였고, 왜 서버로 돌아왔고, 에이전트 이후에도 스택이 남는 이유는 무엇인가. 그리고 스택이 이기는 것과 그 스택을 아는 사람의 가치는 왜 별개인가

    2026-07-22·34분
  • #react#react-server-components#memoization

    React cache() 딥다이브: 소스 코드로 읽는 요청 단위 메모이제이션

    React cache() 함수의 모든 이상한 규칙은 30여 줄짜리 구현에서 직접 따라 나온다. dispatcher, getCacheForType, WeakMap/Map 트리를 소스 레벨로 따라가며 요청 단위 메모이제이션의 동작을 끝까지 본다.

    2026-05-30·35분
  • ◆ 디렉티브 딥다이브
    #react#nextjs#frontend

    'use cache' 디렉티브 딥다이브: 캐시 경계의 끝까지

    "use cache" 한 줄이 만드는 빌드 타임 변환, 캐시 키 직렬화, ResumeDataCache, cacheHandler, 그리고 Cache Components까지

    2026-05-01·65분
  • ◆ 디렉티브 딥다이브
    #react#nextjs#frontend

    'use client' 디렉티브 딥다이브: 클라이언트 경계의 끝까지

    "use client" 한 줄이 만드는 모듈 경계, 빌드 타임 변환, Flight 직렬화, 그리고 성능까지

    2026-05-01·52분

새 글을 놓치고 싶지 않으시다면 RSS로 구독해 주세요.

RSS 구독 →

yceffort — 프론트엔드 엔지니어입니다. 발표·기술 자문·기고 문의는 이곳에서 받고 있습니다.

← Back to the blogIssue on GitHub →