React: ๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ๊ตฌํ˜„

์— ๋งŒ๋“  2015๋…„ 03์›” 13์ผ  ยท  136์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/react

์ด๊ฒƒ์€ ์ž ์žฌ์ ์œผ๋กœ props/state๋ฅผ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ์ „์—ญ ์ €์žฅ์†Œ/๋„คํŠธ์›Œํฌ/๋ฆฌ์†Œ์Šค์—์„œ ์ƒํƒœ ๋น„์ €์žฅ(์ž ์žฌ์ ์œผ๋กœ ๋ฉ”๋ชจํ™”๋จ) ๋ฐ์ดํ„ฐ์˜ ๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ๋ฅผ ์œ„ํ•œ ์ผ๊ธ‰ API์ž…๋‹ˆ๋‹ค.

type RecordOfObservables = { [key:string]: Observable<mixed> };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return <div>{myContent}</div>;
  }

}

๊ด€์ฐฐ()์€ componentWillMount/componentWillUpdate ์ดํ›„์— ์‹คํ–‰๋˜์ง€๋งŒ ๋ Œ๋”๋ง ์ „์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๋ ˆ์ฝ”๋“œ์˜ ๊ฐ ํ‚ค/๊ฐ’์— ๋Œ€ํ•ด. ๊ฐ’์—์„œ Observable์„ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.

subscription = observable.subscribe({ onNext: handleNext });

onNext๊ฐ€ ๊ตฌ๋…์—์„œ ๋™๊ธฐ์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋‹ค์Œ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

this.data[key] = nextValue;

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ดˆ๊ธฐ ๋ Œ๋”๋ง์— ๋Œ€ํ•ด ์ •์˜๋˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋‘ก๋‹ˆ๋‹ค. (์•„๋งˆ๋„ null๋กœ ์„ค์ •ํ• ๊นŒ์š”?)

๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ Œ๋”๋ง์ด ํ‰์†Œ์™€ ๊ฐ™์ด ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

onNext๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ์šฐ๋ฆฌ๋Š” ์ด ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ forcedUpdate๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ์ƒˆ๋กœ์šด "this.data[key]"๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์œ ์ผํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด๋ฉด ๊ด€์ฐฐ์ด ๋‹ค์‹œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(componentWillUpdate -> render -> componentDidUpdate).

props/state๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ(์˜ˆ: recieveProps ๋˜๋Š” setState์—์„œ ์—…๋ฐ์ดํŠธ) ๊ด€์ฐฐ()์ด ์žฌ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค(์กฐ์ • ์ค‘์—).

์ด ์‹œ์ ์—์„œ ์šฐ๋ฆฌ๋Š” ์ƒˆ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณตํ•˜๊ณ  ๋ชจ๋“  ์ƒˆ Observable์„ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด์ „ Observable์˜ ๊ตฌ๋…์„ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค.

subscription.dispose();

์ด ์ˆœ์„œ๋Š” ๋ฐ์ดํ„ฐ ๊ณต๊ธ‰์ž๊ฐ€ ์บ์‹œ์˜ ์ฐธ์กฐ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์•„๋ฌด๋„ ๋“ฃ์ง€ ์•Š๋Š” ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰์‹œ ๊ตฌ๋…์„ ์ทจ์†Œํ•˜๋ฉด ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ตฌ๋…ํ•˜๊ธฐ ์ „์— ์ฐธ์กฐ ํšŸ์ˆ˜๊ฐ€ 0์œผ๋กœ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋˜๋ฉด ๋ชจ๋“  ํ™œ์„ฑ ๊ตฌ๋…์—์„œ ์ž๋™์œผ๋กœ ๊ตฌ๋…์ด ์ทจ์†Œ๋ฉ๋‹ˆ๋‹ค.

์ƒˆ ๊ตฌ๋…์ด ์ฆ‰์‹œ onNext๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์œผ๋ฉด ์ด์ „ ๊ฐ’์„ ๊ณ„์† ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์˜ˆ์ œ์˜ this.props.url ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ณ  ์ƒˆ URL์„ ๊ตฌ๋…ํ•˜๋Š” ๊ฒฝ์šฐ myContent๋Š” ๋‹ค์Œ URL์ด ์™„์ „ํžˆ ๋กœ๋“œ๋  ๋•Œ๊นŒ์ง€ ์ด์ „ URL์˜ ๋‚ด์šฉ์„ ๊ณ„์† ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ <img /> ํƒœ๊ทธ์™€ ๋™์ผํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ํ˜ผ๋ž€์Šค๋Ÿฝ๊ณ  ๋ถˆ์ผ์น˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ ์ƒ๋‹นํžˆ ์ •์ƒ์ ์ธ ๊ธฐ๋ณธ๊ฐ’์ด๋ฉฐ ๋ฐ˜๋Œ€ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์Šคํ”ผ๋„ˆ๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฝ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ "null" ๊ฐ’์„ ์ฆ‰์‹œ ๋ณด๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ๋Œ€์•ˆ์€ Observable์ด ๊ฒฐ๊ณผ์— URL(๋˜๋Š” ID)๊ณผ ์ฝ˜ํ…์ธ ๋ฅผ ๋ชจ๋‘ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

Observable์˜ RxJS ๊ณ„์•ฝ์ด ๋” ์ผ๋ฐ˜์ ์ด๊ณ  ๋™๊ธฐ ์‹คํ–‰์„ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ @jhusain ์˜ ์ œ์•ˆ์ด ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋ฉด ๋Œ€์‹  ํ•ด๋‹น ๊ณ„์•ฝ์œผ๋กœ ์ „ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();

ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ์— ์‘๋‹ตํ•˜๋Š” ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ฅผ ๋” ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ : ์ด ๊ฐœ๋…์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜† ๋ฐ์ดํ„ฐ๊ฐ€ ์†Œํ’ˆ์ฒ˜๋Ÿผ "๋™์ž‘"์ฒ˜๋Ÿผ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์— ๋Œ€ํ•œ ๊ฐœ๋… ์ƒํƒœ๋ฅผ ๊ณผ๋ถ€ํ•˜ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๋‹ค์‹œ ๊ตฌ๋…ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋งŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ตœ์ ํ™”๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณต์› ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Component API Big Picture

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ API๋ฅผ ๊ฐ€์ง€๊ณ  ๋†€๊ณ  ์‹ถ๋‹ค๋ฉด observe ์— ๋Œ€ํ•ด ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ •๋ง ๋ฉ์ฒญํ•œ ํด๋ฆฌํ•„์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

์šฉ๋ฒ•:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

๋ชจ๋“  136 ๋Œ“๊ธ€

undefined ๋Š” onNext ๋ฅผ ํ†ตํ•ด ์˜ต์ €๋ฒ„๋ธ”์ด ์ฒซ ๋ฒˆ์งธ ๊ฐ’์„ ์ œ๊ณตํ•  ๋•Œ๊นŒ์ง€ data ์— ํ• ๋‹นํ•˜๋Š” ๊ฐ€์žฅ ์•ˆ์ „ํ•œ ๊ฐ’์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Relay์—์„œ null (๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Œ) ๋ฐ undefined (์•„์ง ๊ฐ€์ ธ์˜ค์ง€ ์•Š์Œ)์— ๋‹ค๋ฅธ ์˜๋ฏธ๋ฅผ ํ• ๋‹นํ•˜๋ฏ€๋กœ ์ด์ƒ์ ์ธ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ๊ฐ’์€ undefined ์ž…๋‹ˆ๋‹ค. ๋Œ€์•ˆ์€ ์˜ˆ๋ฅผ ๋“ค์–ด getInitialData ์™€ ๊ฐ™์€ ์ƒˆ๋กœ์šด ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด์ง€๋งŒ ์ด๊ฒƒ์ด ๋ถˆํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๊ณผ์ž‰์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๊ฝค ํฅ๋ฏธ๋กญ์ง€๋งŒ ์ •์  ํƒ€์ดํ•‘์˜ ๊ด€์ ์—์„œ ๋‚˜๋Š” ํ‚ค/๊ฐ’ ์‹œ์Šคํ…œ์— ๊ทธ๋‹ค์ง€ ๋งŒ์กฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ ์œ ํ˜•์€ ํ‘œํ˜„ํ•˜๊ธฐ๊ฐ€ ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
observe ๊ฐ€ ๋‹จ์ผ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  this.data ๋กœ ํ™•์ธ๋œ ๊ฐ’์„ ์„ค์ •/๋ณ‘ํ•ฉํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

class Foo {

  observe() {
    return (
      loadUser(this.props.userID)
        .map(user => { user })
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

๊ทธ๋ฆฌ๊ณ  ๋‹ค์ค‘ ๊ฐ€์ ธ์˜ค๊ธฐ์˜ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

class Foo {

  observe() {
    return (
     combineLatest(
      loadUser(this.props.userID),
      loadSomethingElse(this.props.somethingElseId),
      (user, somethingElse) => ({ user, somethingElse})
     )
  }

  render() {
    ..
  }

}

์ด๊ฒƒ์€ ์•„๋งˆ๋„ ์กฐ๊ธˆ ๋” ์žฅํ™ฉํ•˜์ง€๋งŒ ๋ฉ‹์ง„ ์ •์  ์œ ํ˜•์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface Comp<T> {
  observe(): Observable<T>;
  data: T;
}

๋˜ํ•œ props/state๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ observe ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋Š” ๋Œ€์‹  'props' 'state'์— ์˜ต์ €๋ฒ„๋ธ”๋กœ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

class Foo {

  observe(propsStream) {
    return (
      propsStream
        .flatMap(({ userID }) => loadUser(userId))
        .map(user => { user })
    );
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }
}

๊ทธ ์ด์œ ๋Š” (์—ฌ๋Ÿฌ) Observable์„ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ๋„๋ก RxJS๋ฅผ ์ดํ•ดํ•˜๊ณ  ๊ฒฐํ•ฉ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์š”๊ตฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๋‘ ๊ฐœ์˜ Observable์„ ๊ฒฐํ•ฉํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, ์ตœ์†Œํ•œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์˜ ๊ฒฝ์šฐ ๊ตฌ๋… API๋ฅผ ๊ตฌํ˜„ํ•˜์ง€๋งŒ Observable์˜ ํ”„๋กœํ† ํƒ€์ž…์— ๊ฒฐํ•ฉ์ž๋Š” ํฌํ•จํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•„์ˆ˜ ์‚ฌํ•ญ์€ ์•„๋‹ˆ์ง€๋งŒ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๊ฒฐํ•ฉ์ž๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ฐ„๋‹จํ•œ Flux ์Šคํ† ์–ด๋ฅผ ๊ตฌ๋…ํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌ๋…ํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค.

Flow๋Š” ์•„๋งˆ๋„ ์ œ์•ฝ ์กฐ๊ฑด์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ •์  ์œ ํ˜•์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ, ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์†์„ฑ์„ ์ž…๋ ฅํ•˜๊ณ  ๊ด€์ฐฐ ์œ ํ˜•์„ ์•”์‹œํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ถฉ๋ถ„ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

class Foo extends React.Component {
  data : { user : User, content : string };
  observe() /* implied as { user : Observable<User>, content : Observable<string> } */ {
  }
}

์ด ๋ณ€๊ฒฝ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒํƒœ๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ Observables์— ์˜ฌ์ธํ•˜๋Š” ๊ฒƒ์— ๊ด€ํ•œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ด์ „์— ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ ์ด๊ฒƒ ์œ„์— ๊ตฌํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฉฑ๋“ฑ์„ฑ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ช…์‹œ์ ์œผ๋กœ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์ƒํƒœ์— ๊ด€ํ•œ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ํ•„์š”์— ๋”ฐ๋ผ ๊ตฌ๋…์„ ์ทจ์†Œํ•˜๊ณ  ๋‹ค์‹œ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

cc @ericvicenti

์ ์–ด๋„ typescript์˜ ๊ฒฝ์šฐ data ์˜ ์œ ํ˜•์„ ๊ธฐ๋ฐ˜์œผ๋กœ observe ์˜ ๋ฐ˜ํ™˜ ์œ ํ˜•์„ ์ œํ•œํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ ์–ด๋„ https://github.com/ ๊ณผ ๊ฐ™์€ ๋•Œ๊นŒ์ง€๋Š” ์—†์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ React DnD์˜ ๋‹ค์Œ ๋ฒ„์ „์— ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์ง€๋งŒ ๋ถ„๋ช…ํžˆ ์ด๊ฒƒ์€ React 0.14๋ฅผ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.
ref ์ธ์Šคํ„ด์Šค์— this.data ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋‹น๋ถ„๊ฐ„ ์ด๊ฒƒ์„ "ํด๋ฆฌํ•„"ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๋„ˆ๋ฌด ๋ฏธ์นœ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•ฝ์†์„ ์ง€ํ‚ค๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ• ๊นŒ์š”? ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง ์ „์— ์ „์ฒด ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์•ฝ์† ํŠธ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ์ด๊ฒƒ์€ ์„œ๋ฒ„ ์ธก React์— ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์ผ๋ฅ˜ API๋กœ ๋งŒ๋“ค๋ฉด ์–ด๋–ค ์ด์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋ณธ์งˆ์ ์œผ๋กœ "๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ"๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์ผ๋ฅ˜ API๋กœ ๋งŒ๋“ค๋ฉด ์–ด๋–ค ์ด์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋ณธ์งˆ์ ์œผ๋กœ "๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ"๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

5๊ฐœ์˜ HOC๋กœ ๋ž˜ํ•‘ํ•˜์—ฌ 5๊ฐœ์˜ ๊ตฌ๋…์„ ์–ป๋Š” ๊ฒƒ์€ ์ดˆ๋ณด์ž์—๊ฒŒ ๋‹ค์†Œ ๋‹ค๋ฃจ๊ธฐ ์–ด๋ ต๊ณ  ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. componentWillReceiveProps ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ๋„ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‘˜ ๋‹ค ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์šฐ์„  ์šฐ๋ฆฌ์˜ ์ƒˆ๋กœ์šด ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋Œ€๊ตฐ์ฃผ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด https://github.com/chenglou/react-state-stream ์„ React์˜ ๋ฐ”๋‹๋ผ API์— ๋” ๊ฐ€๊น๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ HOC๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? Medium ๊ฒŒ์‹œ๋ฌผ ์˜ ์˜ˆ์—์„œ stores ๋ฅผ ๋ฐ˜๋ณตํ•˜๊ณ  ๊ฐ๊ฐ์„ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.

stores.forEach(store =>
  store.addChangeListener(this.handleStoresChanged)
);

@aaronshaf ํ™•์‹คํžˆ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋”ฐ๋ผ @sebmarkbage๊ฐ€ ๋งํ•˜๋Š” ๊ฒƒ์„ ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด์ œ ์ด๊ฒƒ์„ ๊ฐ€์ง€๊ณ  ๋†€๊ธฐ ์œ„ํ•ด ์ผ์ข…์˜ ํด๋ฆฌํ•„์„ ์ข‹์•„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์•„์ง ์•„์ด๋””์–ด๋ฅผ ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ํ–ฅํ›„ ์—…๋ฐ์ดํŠธ ์ฒ˜๋ฆฌ์™€ ๊ด€๋ จ๋œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋” ๋งŽ์€ ์‹œ๊ฐ„์„ ํ• ์•  ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ mixin์œผ๋กœ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

( @vjeux ๋Š” ๋‚ด๊ฐ€ ์ฐจ์ž„๋ฒจ์„

๋‚ด ์ž‘์—…์„ ํ™๋ณดํ•˜๋ ค๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ์ด ํ›„ํฌ๋Š” React Nexus ์˜ getNexusBindings ํ›„ํฌ์™€ ๋งค์šฐ ์œ ์‚ฌํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ(props์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ)๋ฅผ ํ†ตํ•ด ๊ตฌ์„ฑ ์š”์†Œ ์ˆ˜์ค€์—์„œ ๋ฐ์ดํ„ฐ deps๋ฅผ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

class UserDetails {
  getNexusBindings(props) {
    return {
      // binding to data in the datacenter
      posts: [this.getNexus().remote, `users/${this.props.userId}/posts`],
      // binding to data in the local flux
      mySession: [this.getNexus().local, `session`],
    }
  }
}

๋ฐ”์ธ๋”ฉ์€ componentDidMount ๋ฐ componentWillReceiveProps ๋™์•ˆ ์ ์šฉ/์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. ํ›„์ž์˜ ๊ฒฝ์šฐ ๋‹ค์Œ ๋ฐ”์ธ๋”ฉ์€ ์ด์ „ ๋ฐ”์ธ๋”ฉ๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ œ๊ฑฐ๋œ ๋ฐ”์ธ๋”ฉ์€ ๊ตฌ๋… ์ทจ์†Œ๋˜๊ณ  ์ถ”๊ฐ€๋œ ๋ฐ”์ธ๋”ฉ์€ ๊ตฌ๋…๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๊ฐ€์ ธ์˜ค๊ธฐ/์—…๋ฐ์ดํŠธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ Nexus Flux ๊ตฌํ˜„์— ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ์ปฌ ๋ฐ์ดํ„ฐ(๊ธฐ์กด ๋กœ์ปฌ ์ €์žฅ์†Œ) ๋˜๋Š” ์›๊ฒฉ ๋ฐ์ดํ„ฐ(GET์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๊ณ  Websockets/polyfill์„ ํ†ตํ•ด ํŒจ์น˜ ์ˆ˜์‹ )๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋‹ค๋ฅธ ์ฐฝ(postWindow ์‚ฌ์šฉ) ๋˜๋Š” WebWorker/ServiceWorker์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด์— ๋Œ€ํ•œ ์ง„์ •์œผ๋กœ ์œ ์šฉํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ Flux ์ถ”์ƒํ™”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑ ์š”์†Œ ์ˆ˜์ค€์—์„œ ๋ฐ์ดํ„ฐ deps๋ฅผ ๋™๊ธฐ์‹์œผ๋กœ ์„ค๋ช…ํ•˜๊ณ  ํ›„ํฌ๋Š” ์ข…์†์„ฑ์ด ์ž๋™์œผ๋กœ ๊ตฌ๋…๋˜๊ณ  ์—…๋ฐ์ดํŠธ ์‹œ ์ฃผ์ž…๋˜๊ณ  ๊ตฌ๋… ์ทจ์†Œ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์—๋Š” ๋ฉ‹์ง„ ๊ธฐ๋Šฅ๋„ ํ•จ๊ป˜ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. ์ •ํ™•ํžˆ ๋™์ผํ•œ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ ์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง ์‹œ๊ฐ„์— ๋ฐ์ดํ„ฐ ํ”„๋ฆฌํŽ˜์น˜๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ React Nexus๋Š” ๋ฃจํŠธ์—์„œ ์‹œ์ž‘ํ•˜์—ฌ ๊ฑฐ๊ธฐ์—์„œ ์žฌ๊ท€์ ์œผ๋กœ ๋ฐ”์ธ๋”ฉ์„ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์˜ค๊ณ  ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๊นŒ์ง€ ํ•˜์œ„ ํ•ญ๋ชฉ์„ ๊ณ„์† ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

@aaronshaf @gaearon ์ผ๋“ฑ์„ ์œผ๋กœ ๋งŒ๋“œ๋Š” ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1) props ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์žก์•„๋จน์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค๋ฅธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” data ์™€ ๊ฐ™์€ ์ด๋ฆ„์„ props ๊ฐ์ฒด์—์„œ ์š”๊ตฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ๋” ๋งŽ์€ ์ด๋ฆ„์„ ๊ณ„์† ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ด์ œ ์ด๋Ÿฌํ•œ ์ด๋ฆ„์„ ๊ณ ์œ ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์ž‘์„ฑ๋˜์—ˆ์„ ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ์„ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์ œ ์ด๋ฆ„ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

๊ฒŒ๋‹ค๊ฐ€, ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋Š” ๋ž˜ํ•‘๋œ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ณ„์•ฝ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐœ๋…์ ์œผ๋กœ ์•„์›ƒ๊ณผ ๋™์ผํ•œ ์†Œํ’ˆ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์†Œ๋น„์ž๊ฐ€ ์ˆ˜์‹ ํ•œ ๊ฒƒ๊ณผ ์™„์ „ํžˆ ๋‹ค๋ฅธ props ์„ธํŠธ๋ฅผ ์ œ๊ณตํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ณ  ๋””๋ฒ„๊ทธํ•˜๋Š” ๊ฒƒ์ด ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

2) ๋งˆ์ง€๋ง‰ ๊ฐ’์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด state ๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. data ๊ฐœ๋…์€ ์ˆœ์ „ํžˆ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์ด๋ผ๋Š” ์ ์—์„œ props ์™€ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšŒ์ˆ˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์–ธ์ œ๋“ ์ง€ ์ž์œ ๋กญ๊ฒŒ ๋ฒ„๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฌดํ•œ ์Šคํฌ๋กค์—์„œ ๋ณด์ด์ง€ ์•Š๋Š” ํ•˜์œ„ ํŠธ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@RickWong ์˜ˆ, Observable์˜ ํ•˜์œ„ ์ง‘ํ•ฉ์ด๊ธฐ ๋•Œ๋ฌธ์— Promise๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์˜๊ฒฌ์ด ์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๊ทธ๋ ‡๊ฒŒ ํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๊ทธ๊ฒƒ๋“ค์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ๋กœ Observable๋ณด๋‹ค ์—ด๋“ฑํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.

A) ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ž๋™์œผ๋กœ ์ทจ์†Œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์„ ์€ ๋Šฆ์€ ํ•ด๊ฒฐ์„ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋™์•ˆ Promise๋Š” ์ž ์žฌ์ ์œผ๋กœ ๊ณ ๊ฐ€์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์žฅ๊ธฐ ์‹คํ–‰ ํƒ€์ด๋จธ/๋„คํŠธ์›Œํฌ ์š”์ฒญ์˜ subscribe/cancel/subscribe/cancel...๊ณผ ๊ฐ™์€ ๋‚œ์žกํ•œ ์ƒํ™ฉ์— ๋น ์ง€๊ธฐ ์‰ฝ๊ณ  Promises๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฃจํŠธ์—์„œ ์ทจ์†Œ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ทธ๋ƒฅ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์†Œ์Šค๋ฅผ ์™„๋ฃŒํ•˜๊ฑฐ๋‚˜ ์‹œ๊ฐ„ ์ดˆ๊ณผํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” facebook.com๊ณผ ๊ฐ™์€ ๋Œ€ํ˜• ๋ฐ์Šคํฌํ†ฑ ํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ์ด๋‚˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ œํ•œ๋œ ํ™˜๊ฒฝ(์˜ˆ: ๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ)์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ์ค‘์š”ํ•œ ์•ฑ์˜ ์„ฑ๋Šฅ์— ํ•ด๋กœ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

B) ๋‹น์‹ ์€ ์˜ค์ง ํ•˜๋‚˜์˜ ๊ฐ€์น˜๋งŒ์„ ์–ป๋Š” ๊ฒƒ์— ์ž์‹ ์„ ๊ฐ€๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ณด๊ธฐ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ผ๊ด€์„ฑ์ด ์—†๋Š” ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ดœ์ฐฎ์„ ์ˆ˜ ์žˆ๋Š” ๋‹จ์ผ ์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง์˜ ๊ฒฝ์šฐ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ด์ƒ์ ์œผ๋กœ๋Š” ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ UI๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜๊ณ  ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ์‹์œผ๋กœ ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ Observable์€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž๋ฅผ ์ž ๊ทธ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ์šฐ์ˆ˜ํ•œ API๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.

@elierotenberg ์ฐธ์—ฌ ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์‹ค์ œ๋กœ ๋งค์šฐ ์œ ์‚ฌํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ์ข…๋ฅ˜์˜ ํ˜œํƒ. ๋‚ด ์ œ์•ˆ์— ์ œํ•œ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ฆ‰, React Nexus์— ๋ˆ„๋ฝ๋œ ๊ฒƒ์ด ์žˆ๋Š”๋ฐ ์ด ์œ„์— ๋นŒ๋“œํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ค‘์š”ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ์Šค์Šค๋กœ๋ฅผ ์ž ๊ทธ์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. :)

์„œ๋ฒ„ ๋ Œ๋”๋ง ๊ด€์ ์—์„œ Observable/Promise๊ฐ€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋กœ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ตœ์ข… renderToString์„ ์—ฐ๊ธฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์•„์ง ํŽ˜์ด์ง€์— ์–ด๋–ค ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•œ ์ฑ„ React ์™ธ๋ถ€์—์„œ ๋ชจ๋“  ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ์œ„์น˜์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” react-nexus๊ฐ€ ๋ Œ๋” ํŠธ๋ฆฌ๋ฅผ ๊ณ„์† ๋‚ด๋ ค๊ฐ€๊ธฐ ์ „์— ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๋น„๋™๊ธฐ ๋กœ๋”ฉ์„ ํ—ˆ์šฉํ•œ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

์˜ˆ, react-nexus๋Š” ๋‹ค์Œ์„ ๋ช…์‹œ์ ์œผ๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.
1) getNexusBindings ๋กœ ์„ ์–ธ ๋ฐ”์ธ๋”ฉ
2) ๊ตฌ๋…/์—…๋ฐ์ดํŠธ๋ฅผ applyNexusBindings ๋ฐ”์ธ๋”ฉ
3) ํ”„๋ฆฌํŽ˜์นญ์„ prefetchNexusBindings ๋กœ ๋ฐ”์ธ๋”ฉ(๋น„๋™๊ธฐ์‹์ด๋ฉฐ "์ดˆ๊ธฐ"(์ด๊ฒƒ์ด ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์ด ๋ฌด์—‡์ด๋“ ) ๊ฐ’์ด ์ค€๋น„๋˜๋ฉด ํ•ด๊ฒฐ๋จ)

ReactNexus.prefetchApp(ReactElement) ๋Š” Promise(String html, Object serializableData) ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ›„ํฌ๋Š” React ํŠธ๋ฆฌ์˜ ๊ตฌ์„ฑ์„ ๋ชจ๋ฐฉํ•˜๊ณ ( instantiateReactComponent ) ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ๊ตฌ์„ฑ/ํ”„๋ฆฌํŽ˜์น˜/๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ๊ฐ€ '์ค€๋น„'๋˜๋ฉด ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์ค€๋น„๋˜์—ˆ์Œ์„ ์•Œ๋ฉด์„œ ๋งˆ์นจ๋‚ด React.renderToString ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค(๋ชจ๋“ˆ๋กœ ์˜ค๋ฅ˜). ํ•ด๊ฒฐ๋˜๋ฉด ์ด Promise์˜ ๊ฐ’์„ ์„œ๋ฒ„ ์‘๋‹ต์— ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์—์„œ ์ผ๋ฐ˜ React.render() ์ˆ˜๋ช… ์ฃผ๊ธฐ๋Š” ํ‰์†Œ์™€ ๊ฐ™์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ API๋ฅผ ๊ฐ€์ง€๊ณ  ๋†€๊ณ  ์‹ถ๋‹ค๋ฉด observe ์— ๋Œ€ํ•ด ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ •๋ง ๋ฉ์ฒญํ•œ ํด๋ฆฌํ•„์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

์šฉ๋ฒ•:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Observable์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ตฌํ˜„์„ ์ œ์™ธํ•˜๊ณ  ๊ตฌ์ฒด์ ์ด๊ณ  ํ•ฉ์˜๋œ ๊ฒƒ์ž…๋‹ˆ๊นŒ? ๊ณ„์•ฝ์ด๋ž€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋ฒ ์ด์ปจ์ด๋‚˜ Rxjs๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ ๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๊นŒ? ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์ด๋“œ๋กœ๋”ฉํ•˜๊ธฐ ์œ„ํ•œ ์ผ๋ฅ˜ API๋งŒํผ ํ›Œ๋ฅญํ•˜์ง€๋งŒ, ์ผ๋ฐ˜ js๋ฅผ ํ–ฅํ•œ React์˜ ๊พธ์ค€ํ•œ ์›€์ง์ž„์„ ๊ฐ์•ˆํ•  ๋•Œ React๊ฐ€ ์ง€์ •๋˜์ง€ ์•Š์€/๋งค์šฐ ์ดˆ๊ธฐ ์ง€์ • ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” API๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ์ด์ƒํ•ด ๋ณด์ž…๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ๊ฒƒ์ด ์šฐ๋ฆฌ๋ฅผ ํŠน์ • ์‚ฌ์šฉ์ž ํ† ์ง€ ๊ตฌํ˜„์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๊นŒ?

์ œ์ณ๋‘๊ณ  ์ŠคํŠธ๋ฆผ์ด ์•„๋‹Œ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๊ฒฝ์ฃผ์—๋Š” ๋ง์ด ์—†์ง€๋งŒ ์†”์งํžˆ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์›น ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•œ ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์œผ๋ฉฐ ๋ฌผ๋ก  ๋…ธ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณ ๋ คํ•ด์•ผ ํ•  ๋˜ ๋‹ค๋ฅธ ๋‘ ๊ฐ€์ง€: https://github.com/cujojs/most ๋ฐ https://github.com/caolan/highland

@jquense Observable์„ ECMAScript 7(+)์— ์ถ”๊ฐ€ํ•˜๋Š” ์ œ์•ˆ์— ๋Œ€ํ•œ ํ™œ์„ฑ ์ž‘์—…์ด ์žˆ์œผ๋ฏ€๋กœ ์ด์ƒ์ ์œผ๋กœ๋Š” ์ผ๋ฐ˜ JS๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. https://github.com/jhusain/asyncgenerator (ํ˜„์žฌ ๊ตฌ์‹์ž…๋‹ˆ๋‹ค.)

์šฐ๋ฆฌ๋Š” RxJS์— ์˜์กดํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. API๋Š” RxJS๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์Šค์Šค๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. RxJS๋Š” ํ™œ์„ฑ ECMAScript ์ œ์•ˆ์— ๊ฐ€์žฅ ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

most.js๋„ ๊ฐ€๋Šฅํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Bacon.js์˜ API๋Š” ๊ฐ’์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด Bacon.Event ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— Bacon์— ์˜์กดํ•˜์ง€ ์•Š๊ณ ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Stream API๋Š” ๋„ˆ๋ฌด ๋†’์€ ์ˆ˜์ค€์ด๋ฉฐ ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ๋ฉ€๋ฆฌ ๋–จ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

"await before render" ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ œ ๋ง์€ ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ๋ชจ๋“  Observable์„ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์—†์ง€๋งŒ ์„œ๋ฒ„์—์„œ๋Š” ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ์˜ render()๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ๋ถ€๋ถ„์ด ์•„๋‹Œ ์ „์ฒด .

[parent] await observe(). full render(). -> [foreach child] await observe(). full render().

๋ชจ๋“  ํƒ์ƒ‰์—์„œ ์ด๊ฒƒ์ด ์„œ๋ฒ„ ์ธก React์—์„œ ๋ˆ„๋ฝ๋œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ์ž„์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ํ† ๋ก ์— ์ด์–ด ๋‹ค์Œ ๊ฒŒ์‹œ๋ฌผ์—์„œ React Nexus๊ฐ€ ํ•˜๋Š” ์ผ์„ ์š”์•ฝํ•˜๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

React Nexus๋กœ ์ œ๋Œ€๋กœ ๋œ Ismorphic ์•ฑ

ํ•ต์‹ฌ ํ”„๋ฆฌํŽ˜์นญ ๋ฃจํ‹ด์˜ ๋‹ค์ด์–ด๊ทธ๋žจ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

React Nexus

์šฐ๋ฆฌ๋Š” RxJS์— ์˜์กดํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. API๋Š” RxJS๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์Šค์Šค๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. RxJS๋Š” ํ™œ์„ฑ ECMAScript ์ œ์•ˆ์— ๊ฐ€์žฅ ๊ฐ€๊น์Šต๋‹ˆ๋‹ค.

:+1: ์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ๋ฌด์—‡์„ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•˜๋Š” ํ•œ ์Šค์Šค๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๊ทน๋„๋กœ ์–ด๋ ค์šด ์•ฝ์†์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋ฉด์„œ ๊ฐ€์žฅ ํฐ ๊ด€์‹ฌ์‚ฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ƒํƒœ๊ณ„์˜ ํŠน์ • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ์•”์‹œ์  ์š”๊ตฌ ์‚ฌํ•ญ์œผ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค. ์ ‘์„ ์ ์œผ๋กœ... Promise ์„ธ๊ณ„์˜ ์ข‹์€ ์  ์ค‘ ํ•˜๋‚˜๋Š” A+ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์ด๋ฏ€๋กœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ „์ฒด์— ๊ฑธ์ณ์„œ๋ผ๋„ .then ์˜ ๊ณตํ†ต ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ณด์ฆ์ด ์ตœ์†Œํ•œ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ‘œ์ค€ํ™”.

๋‹น์‹ ์ด ๋ฌด์—‡์„ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋‹น์‹  ์ž์‹ ์˜ ๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๊ทน๋„๋กœ ์–ด๋ ค์šด ์•ฝ์†์— ๋Œ€ํ•ด ์ƒ๊ฐํ•˜๋ฉด์„œ ์ด๊ฒƒ์ด ์ €์—๊ฒŒ ํฐ ๊ด€์‹ฌ์‚ฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์ƒํƒœ๊ณ„์˜ ํŠน์ • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ์•”์‹œ์  ์š”๊ตฌ ์‚ฌํ•ญ์œผ๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค.

์™„์ „ํžˆ ๋™์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๋ง™๊ฒŒ๋„ ์˜ต์ €๋ฒ„๋ธ”์€ ์ •๋ง ๊ฐ„๋‹จํ•œ ๊ณ„์•ฝ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  then ์™€ ๊ฐ™์€ ๋‚ด์žฅ ๋ฉ”์„œ๋“œ๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๋ฉด์—์„œ๋Š” ํ”„๋ผ๋ฏธ์Šค๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

์œ„์›ํšŒ์—์„œ next ํ˜ธ์ถœ์ด Promises์™€ ๊ฐ™์€ ๋ฏธ์„ธ ์ž‘์—…์„ ์˜ˆ์•ฝํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•˜๋ฉด ๋” ๋ณต์žกํ•ด์ง€๊ณ  ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” onNext๊ฐ€ RxJS์—์„œ ๋™๊ธฐ์‹์ด๋ผ๋Š” ์‚ฌ์‹ค์— ๊ธฐ๋ฐ˜ํ•œ ๋งŽ์€ ํŒจํ„ด์„ ๊ท€์ฐฎ๊ฒŒ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ Flux ์ €์žฅ์†Œ ํŒจํ„ด์€ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ‚ค๋ณ„๋กœ Map of Observable์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ชจ๋“  ์‚ฌ๋žŒ์ด ๊ตฌ๋…์„ ์ทจ์†Œํ•  ๋•Œ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด MyStore.get(this.props.someID) ์™€ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ญ์ƒ ๋™์ผํ•œ Observable์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด MyStore.get(this.props.someID)๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ํ•ญ์ƒ ๋™์ผํ•œ Observable์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

this.props.key (gone I know)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ด๋Ÿฌํ•œ ๊ณ ์œ  ์‹๋ณ„์ž๋ฅผ ์ด๋ฏธ <... key={child.id} .. /> ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด MyStore.get(this.props.someID)๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ํ•ญ์ƒ ๋™์ผํ•œ Observable์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋‚ด๊ฐ€ React Nexus์—๋„ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. Store#observe๋Š” ๋ฉ”๋ชจํ™”๋œ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ด€์ฐฐ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ตฌ๋…์ž๊ฐ€ ์ ์–ด๋„ ํ•œ ํ‹ฑ ๋™์•ˆ ์‚ฌ๋ผ์ง€๋ฉด ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค(์‹ค์ œ "๊ตฌ๋… ์ทจ์†Œ" ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ์™€ ๊ฐ™์€ ๊ด€๋ จ ๋ฐฑ์—”๋“œ๋ณ„ ์ •๋ฆฌ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ํฌํ•จ).

@sebmarkbage @gaearon v0.14 ์—์„œ ์„œ๋ฒ„ ์ž‘์—…์„ ์–ด๋–ป๊ฒŒ ๊ด€์ฐฐํ• ๊นŒ์š”?
react-nexus๊ฐ€ ํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋ฌธ์ž์—ด๋กœ ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ๋ชจ๋“  ๊ด€์ฐฐ์ž๊ฐ€ ํ•ด๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์ ์ ˆํ•˜๊ฒŒ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ(๊ทธ๋Ÿฌ๋‚˜ ๋ฐ˜์‘ํ•˜๋„๋ก ๋‚ด์žฅ๋˜์–ด ์žˆ์Œ)?

IMO ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋งํ•  "์ค€๋น„"๋˜๊ธฐ ์ „์— ์ฒซ ๋ฒˆ์งธ ๊ด€์ฐฐ ๊ฐ’์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@gaearon : IMO ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋งํ•  "์ค€๋น„"๋˜๊ธฐ ์ „์— ์ฒซ ๋ฒˆ์งธ ๊ด€์ฐฐ ๊ฐ’์„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์˜ˆ, :+1: ๋น„๋™๊ธฐ ๋ Œ๋”๋ง์˜ ๊ฒฝ์šฐ. ๊ทธ ๋™์•ˆ @andreypopp ์˜ react-async ๊ฐ€ ๋Œ€์•ˆ์ด์ง€๋งŒ React๋ฅผ "ํ•ดํ‚น"ํ•˜๋ ค๋ฉด fibers ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. React๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„๋™๊ธฐ ๋ Œ๋”๋ง์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ๋ Œ๋”๋ง์€ ์ง€์›ํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์ด ๋ฌธ์ œ์˜ ์ผ๋ถ€๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์—˜

๋ถˆํ–‰ํžˆ๋„ 0.14์—์„œ๋Š” ์•„๋งˆ ๊ทธ๊ฒƒ์„ ๋งŒ๋“ค์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณ ๋ คํ•˜๊ณ  ๋ฆฌํŒฉํ† ๋งํ•ด์•ผ ํ•  ๋‹ค์–‘ํ•œ ๋””์ž์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡๊ฒŒ ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋‚ด๋ถ€ ์•„ํ‚คํ…์ฒ˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ž‘์„ฑํ•˜๊ณ  ์„ค๋ช…ํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์ž‘์„ฑํ•˜์‹ญ์‹œ์˜ค.

@gaearon re: react-streaming-state ์™€ ๊ฐ™์€ ์ƒ๊ฐ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค . ์‚ฌ์ด๋“œ ๋กœ๋”ฉ ์ด์™ธ์˜ ๋ชจ๋“  ์ž ์žฌ์ ์ธ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์„ ๊ณ ๋ คํ•  ๋•Œ data ๋ณด๋‹ค ๋” ๋‚˜์€ ์ด๋ฆ„์ด ์žˆ์„๊นŒ์š”? ์˜ˆ๋ฅผ ๋“ค์–ด observed ๋Š” ๋ฉ”์„œ๋“œ์™€ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ์—ฐ๊ด€๋ฉ๋‹ˆ๋‹ค.

Bikeshedding์œผ๋กœ ํƒˆ์„ ํ•˜๋ ค๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ ์ด๊ฒƒ์„ ๋ฒ„๋ฆฌ๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

React์—์„œ Observable์„ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์ดํ•ดํ•  ๋•Œ React๋ฅผ ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

react-async ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•˜๋Š” ๋™์•ˆ ๋น„์Šทํ•œ ์•„์ด๋””์–ด๋ฅผ ์‹คํ—˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. README ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ฃผ๋ชฉํ• ๋งŒํ•œ ์ฐจ์ด์ ์€ React๊ฐ€ key prop ๋ฐ stateful ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ํ”„๋กœ์„ธ์Šค๋ฅผ ์กฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด ๋ช…์‹œ์  ๊ด€์ฐฐ ๊ฐ€๋Šฅ/ํ”„๋กœ์„ธ์Šค ID๋ฅผ ๋„์ž…ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ช…๋ช…๋œ ํ”„๋กœ์„ธ์Šค์˜ id ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด React Async๋Š” ์ด์ „ ํ”„๋กœ์„ธ์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ค‘์ง€ํ•˜๊ณ  ์ƒˆ ์ธ์Šคํ„ด์Šค๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

API๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import React from 'react';
import Async from 'react-async';

function defineFetchProcess(url) {
  return {
    id: url,
    start() {
      return fetch(url)
    }
  }
}

function MyComponentProcesses(props) {
  return {
    user: defineFetchProcess(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentProcesses)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }
}

ํ”„๋กœ์„ธ์Šค API๋Š” ์ด์ œ ๊ตฌ๋ฌธ์ ์œผ๋กœ๋‚˜ ์ด๋ฆ„์ ์œผ๋กœ ES6 Promises API๋ฅผ ๋”ฐ๋ฅด์ง€๋งŒ ์˜๋ฏธ์ƒ์œผ๋กœ๋Š” process.then(onNext, onError) ๊ฐ€ ๋ผ์ด๋ธŒ ํ”„๋กœ์„ธ์Šค๋‹น ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Promise๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š”(?) ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์ˆ˜์šฉํ•˜๋„๋ก ๋งŒ๋“ค์–ด์กŒ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์†”์งํžˆ ์ง€๊ธˆ์€ ํ˜ผ๋ž€์„ ๋ง‰๊ธฐ ์œ„ํ•ด ๋ฐ”๊ฟ”์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

๋‚ด๊ฐ€ ํ‹€๋ ธ์„ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž ์˜์—ญ์—์„œ ์ œ์•ˆ๋œ(์ด ๋ฌธ์ œ์—์„œ) API๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉํ•ดํ•˜๋Š” ์œ ์ผํ•œ ๊ฒƒ์€ componentWillUpdate ์™€ ๊ฐ™์ด ๋ Œ๋”๋ง ์ง์ „์— ์‹คํ–‰๋˜์ง€๋งŒ ์ƒˆ๋กœ์šด props ์‹คํ–‰๋˜๋Š” ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  state ์ด๋ฏธ ์ธ์Šคํ„ด์Šค์— ์„ค์น˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์•„์ง ๋…ผ์˜๋˜์ง€ ์•Š์€ ํ•œ ๊ฐ€์ง€๋Š” onError ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ์ž…๋‹ˆ๋‹ค. Observable์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ํ•ด๋‹น ์ •๋ณด๋ฅผ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์–ด๋–ป๊ฒŒ๋“  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. React๋Š” ์‹ค์ œ subscribe(callbacks) ํ˜ธ์ถœ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ฝœ๋ฐฑ ๊ฐ์ฒด์— ์ฃผ์ž…ํ•˜๋ ค๋ฉด ํ‘œ์ค€ํ™”๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ตœ๋Œ€ํ•œ์˜ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” this.data ์™€ ์œ ์‚ฌํ•œ ์ตœ์ƒ์œ„ ์†์„ฑ์— ์˜ค๋ฅ˜๋ฅผ ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—„์ฒญ๋‚˜๊ฒŒ ๋ฌด๊ฑฐ์›Œ ๋ณด์ด๋ฉฐ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋„ค์ž„ ์ŠคํŽ˜์ด์Šค๋ฅผ ๋” ๋งŽ์ด ๋จน์Šต๋‹ˆ๋‹ค.
๋‘ ๋ฒˆ์งธ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž์‹ ์˜ onError ์ฝœ๋ฐฑ์„ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ธฐ๋Šฅ์œผ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์˜ต์ €๋ฒ„๋ธ”์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ •์˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ์›ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

onObserveError(key, error) {
  // do something with the error
  this.state.errors[key] = error;
  this.setState({ errors: this.state.errors });
}

์ด๊ฒƒ์€ Parse+React์˜ ๋‹ค์Œ ๋ฐ˜๋ณต์—์„œ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” API๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฒด ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ์ธ { name => error } ์ง€๋„์— ์˜ค๋ฅ˜๊ฐ€ ์ถ”๊ฐ€๋˜๊ณ  ๊ตฌ์„ฑ ์š”์†Œ์—๋Š” ๋น„์–ด ์žˆ์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ง€๋„์˜ ๋ณต์ œ๋ณธ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ตœ์ƒ์œ„ ๊ณต๊ฐœ ๋ฉ”์„œ๋“œ์ธ queryErrors() ๋ฐ null ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด (๋‹จ์ˆœํ•œ if (this.queryErrors()) ํ—ˆ์šฉ.

๋ฌผ๋ก  ์ƒˆ๋กœ์šด ์˜ˆ์•ฝ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ๋„ ๊นŒ๋‹ค๋กœ์šด ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์•„๋‹Œ ์ฒ™ ํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ Œ๋”๋งํ•  ๋•Œ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์˜ค๋ฅ˜๋ฅผ ์•”์‹œ์  ๋˜๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

@andrewimm ์•„์ด๋””์–ด๋Š” ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋  ๋•Œ๊นŒ์ง€ ๊ณ„์ธต ๊ตฌ์กฐ ์œ„๋กœ ์˜ค๋ฅ˜๋ฅผ ๋ฒ„๋ธ”๋งํ•˜๋Š” ์ผ๋ฐ˜ ์˜ค๋ฅ˜ ์ „ํŒŒ ์‹œ์Šคํ…œ์— ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. https://github.com/facebook/react/issues/2928

์ด๊ฒƒ์€ ๋˜ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๋˜์ง€๊ณ  ์ •์ƒ์ ์œผ๋กœ ๋ณต๊ตฌํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. render() ๋ฉ”์„œ๋“œ๊ฐ€ throwํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ›จ์”ฌ ๋” ๋งŽ์€ ์ž‘์—…์ด ํ•„์š”ํ•˜๊ณ  ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ์ข€ ๊ฑธ๋ฆฌ์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๊ฒƒ์ด ์•„์ด๋””์–ด์˜€์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ ์ ˆํ•œ ๋ฐ˜์‘ ์™ธ๋ถ€์— ๋‘์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•˜๊ณ , 1๊ฐœ ๋˜๋Š” 2๊ฐœ์˜ ํ•ต์‹ฌ ํ†ตํ•ฉ ์ง€์ ์€ react-async ๋ฐ react-nexus์™€ ๊ฐ™์€ ํ”„๋กœ์ ํŠธ์™€ ์กฐ์ •๋˜์–ด์•ผ ์ ์ ˆํ•œ React ์œ„์—์„œ ๊น”๋”ํ•˜๊ฒŒ ์ˆ˜ํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค....

๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ œ์•ˆ๋œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด
์ด๊ฒƒ์„ ํ”„๋ ˆ์ž„์›Œํฌ ์ž์ฒด์— ๊ตฝ๊ธฐ

2015๋…„ 4์›” 21์ผ ํ™”์š”์ผ ์˜คํ›„ 11:38 Rodolfo Hansen [email protected]
์ผ๋‹ค:

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ ์ ˆํ•œ ๋ฐ˜์‘ ์™ธ๋ถ€์— ๋‚จ๊ฒจ ๋‘์–ด์•ผํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•˜๊ณ  1 ๋˜๋Š” 2 ํ‚ค
ํ†ตํ•ฉ ์ง€์ ์€ react-async ๋ฐ
react-nexus๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ์ ˆํ•œ React ์œ„์—์„œ ๊น”๋”ํ•˜๊ฒŒ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค....

โ€”
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ๋‹ต์žฅํ•˜๊ฑฐ๋‚˜ GitHub์—์„œ ํ™•์ธํ•˜์„ธ์š”.
https://github.com/facebook/react/issues/3398#issuecomment -95048028.

์ฃผ๋ง ๋™์•ˆ ์ €๋Š” Flexy ๋ผ๋Š” ๋˜ ๋‹ค๋ฅธ Flux ๊ตฌํ˜„์„ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์ƒ์ ์˜ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์˜ต์ €๋ฒ„๋ธ”์ด๋‚˜ ๋‹ค๋ฅธ Reactive ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์—†์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์˜ต์ €๋ฒ„๋ธ” API๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” .getObservable ๋ฉ”์„œ๋“œ๋ฅผ ๋…ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ API๋Š” ์‹ค์ œ Observable๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์„ ๋งŒํผ ์‰ฝ๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ฆ‰, ์ฝ”๋“œ๋ฅผ ๊ฐ€ํ˜นํ•˜๊ฒŒ ํŒ๋‹จํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ์ฃผ๋ง ๋™์•ˆ ์ˆ˜ํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • ์žฌ๋ฏธ์žˆ๋Š”
  • ์ดํ•ด
  • js-csp๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ
  • ๊ด€์ฐฐ API ์‚ฌ์šฉ

์ฐธ๊ณ ๋กœ ์ด์™€ ๊ฐ™์€ ์‹œ์Šคํ…œ๊ณผ Flux๋Š” ์‹ค์ œ๋กœ ์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง์„ ๋œ ๊ณ ํ†ต์Šค๋Ÿฝ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. React-Nexus์™€ ์œ ์‚ฌํ•œ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šคํ† ์–ด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  React ์•ฑ์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ƒ์ ๊ณผ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๋” ์ด์ƒ ์ž‘์—…์ด ์‹คํ–‰๋˜์ง€ ์•Š์„ ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋‹ค์‹œ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์ƒ์ ์— ์žˆ์Œ).

๋‚˜๋Š” ์ด๊ฒƒ์ด ์ƒํƒœ ๋น„์ €์žฅ ๋ฐ์ดํ„ฐ ๊ตฌ๋…์˜ ์ƒˆ๋กœ์šด ์˜๋ฏธ๋ฅผ ์–ป๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ์ž‘์€ ํ†ตํ•ฉ ์ง€์ ์ด๋ผ๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์–ด๋–ค ํ†ตํ•ฉ ํฌ์ธํŠธ๋ฅผ ์ œ์•ˆํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ํ›จ์”ฌ ๋” ๋ณต์žกํ•œ ๋ฌธ์ œ์ด๊ณ  ์ž์ฒด ์Šค๋ ˆ๋“œ๊ฐ€ ํ•„์š”ํ•œ ๋น„๋™๊ธฐ ๋ Œ๋”๋ง์„ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ด ํ›„ํฌ๋Š” ๊ด€๊ณ„์—†์ด ๋น„๋™๊ธฐ ๋ Œ๋”๋ง๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ์€ ์ด๋ฏธ ๊ตฌ์„ฑ ์š”์†Œ๋ณ„๋กœ React๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ „์—ญ ์ฃผ์ž…์€ ํ™˜๊ฒฝ ์ „๋ฐ˜์—์„œ ๊ตฌ์„ฑ ์š”์†Œ ์žฌ์‚ฌ์šฉ์„ ์ค‘๋‹จํ•˜๋ฏ€๋กœ React๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ถ•๋œ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ์ปจํ…์ŠคํŠธ์—ฌ์•ผ ํ•˜๋ฏ€๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ „์—ญ ์ฃผ์ž…์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์–ด๋–ค ํ›„ํฌ๋ฅผ ๋†“์น˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์•ผ,

์†”์งํžˆ ๋งํ•ด์„œ, ์ƒˆ๋กœ์šด ํ›„ํฌ๊ฐ€ ๊ตฌํ˜„์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธด ํ–ˆ์ง€๋งŒ react-async ๋ฐ react-nexus ์‹œ์—ฐํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ํ›„ํฌ ์—†์ด๋„ ์ธก๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋“œ๋ฅผ ํ™•์‹คํžˆ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ํƒ‘์žฌ๋œ React ๊ณ„์ธต ๊ตฌ์กฐ ์™ธ๋ถ€์—์„œ React ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋…ธ์ถœํ•˜๊ณ  ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. react-nexus ์—์„œ ๋‚ด๋ถ€ instanciateReactComponent ํ•˜๊ณ  componentWillMount , componentWillUnmount ๋“ฑ์„ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉฐ ์ด ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ทจ์•ฝํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค( instanciateReactComponents ๋Š” ๋‹ค์Œ ๋ฒ„์ „์˜ React?์—์„œ ๋ณ€๊ฒฝ๋˜๋Š” ๋‚ด๋ถ€ ๋ถˆ๋ณ€์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.).

์ƒํƒœ ๋น„์ €์žฅ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ _is_ ์ƒํƒœ๋กœ ๊ฐ€์ ธ์˜ค๊ณ  ๋”ฐ๋ผ์„œ ์ผ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ์— ๋ณด๋ฅ˜/์™„๋ฃŒ/์‹คํŒจ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ๊ด€๋ จ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์•ฑ์—์„œ react-nexus ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฐ€์ ธ์˜ค๊ธฐ ์ƒํƒœ๋ฅผ ์ž์‹ ๊ตฌ์„ฑ ์š”์†Œ์— ์†Œํ’ˆ์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ๋Š” "์ƒํƒœ ๋น„์ €์žฅ"(๋ฐ”๋žŒ์งํ•จ)์ด๊ณ  ์™ธ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ๋Š” "์ƒํƒœ ์ €์žฅ"(์˜ˆ: ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ๋˜๋Š” ์ž๋ฆฌ ํ‘œ์‹œ์ž๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐ์—๋„ ๋ฐ”๋žŒ์งํ•จ)์ž…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์–ด๋–ค ํ†ตํ•ฉ ํฌ์ธํŠธ๋ฅผ ์ œ์•ˆํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

@ANDREYPOPP ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์งˆ๋ฌธ์„ ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ Œ๋”๋งํ•˜๊ธฐ ์ „์— ์‚ฌ์šฉ์ž ์˜์—ญ์—์„œ ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ์œ ์ผํ•œ ๊ฒƒ์ด ์•„๋‹Œ๊ฐ€์š”? ์ตœ์†Œํ•œ์˜ API ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๋‚˜๋จธ์ง€๋Š” ์ž…๋ ฅ ์ŠคํŠธ๋ฆผ/์ด๋ฏธํ„ฐ/๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ํ•ญ๋ชฉ์— ๋”ฐ๋ผ data ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ forceUpdate๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ์„ค์ •ํ•˜๊ณ  ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋‹ค๋ฅธ ํŠน๋ณ„ํ•œ ๊ฒƒ์„ ๋†“์น˜๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด (์™„์ „ํžˆ ๊ฐ€๋Šฅ)?

๋‚˜๋Š” ์—ฌ๊ธฐ์„œ ๋” ํฐ ๋…ผ์˜์— ๋น ์ ธ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ @sebmarkbage ์— ๋‹ตํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํ›„ํฌ ์ค‘ ํ•˜๋‚˜๋Š” ์‹ค์ œ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ๋งŒ ํ•„์š”ํ•œ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฐ์ดํ„ฐ๋กœ ์„ค์ •๋˜๊ธฐ _์ „์—_ ์˜ต์ €๋ฒ„๋ธ”์— ์˜ํ•ด ํ‘ธ์‹œ๋œ ๊ฐ’์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ํ›„ํฌ. ์‹ค์ œ ์˜ต์ €๋ฒ„๋ธ”์„ ์‚ฌ์šฉํ•˜๋ฉด .map ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

์ƒํ™ฉ์ด ์ข€ ๋” ๊ฐœ๋ฐฉ์ ์ด์—ˆ๋‹ค๋ฉด Observable ๊ด€๋ จ ๋™์ž‘์„ ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋Œ€์ฒดํ•  ํ›…์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ ์ด๋ฒคํŠธ ์ด๋ฏธํ„ฐ ๋˜๋Š” CSP ์ฑ„๋„์„ ๋Œ€์‹  โ€‹โ€‹์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ๋ช‡ ๋Œ“๊ธ€์ด ๊ฐ€์žฅ ์ž‘์€ ํ™•์žฅ ์ง€์ ์ด ์‹ค์ œ๋กœ "์—ฐ๊ฒฐ" ๋ฐ "์—ฐ๊ฒฐ ํ•ด์ œ" ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ตฌ์„ฑ ์š”์†Œ์— ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ์—ฐ๊ฒฐํ•˜๊ณ  ํ•ด๋‹น ๊ตฌ๋…์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๊ทธ๋Ÿฐ ๋‹ค์Œ Observable์€ (์ฝ”์–ด ๋‚ด๋ถ€ ๋˜๋Š” ์™ธ๋ถ€) ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒ๋‹นํžˆ ์‚ฌ์†Œํ•˜๊ฒŒ ๊ตฌ์ถ•๋  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋Ÿฌํ•œ ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ง€์ ์„ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๋” ํฐ ๋งค๋ ฅ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

ํ•ฉ๋ฆฌ์ ์ธ ์š”์•ฝ์ธ๊ฐ€์š”?

๋‚˜๋Š” ๋˜ํ•œ ์ด๊ฒƒ์ด React ์ž์ฒด์—์„œ ํ•ด๊ฒฐ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ์•„์ง ํ™•์‹ ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” React๊ฐ€ React ์œ„์— ์ด ๊ธฐ๋Šฅ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ํ›„ํฌ๋ฅผ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒฝํ–ฅ์ด ํ›จ์”ฌ ๋” ํฝ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ž ์‹œ ๋™์•ˆ observe() ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ ์ƒ๊ฐ:

this.props , this.state , this.context ๋ฐ ํ˜„์žฌ this.data ๊ฐ€ ๋ชจ๋‘ render() ์ž ์žฌ์ ์ธ ์ƒˆ ๋ฐ์ดํ„ฐ ์†Œ์Šค์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‚˜์—๊ฒŒ ๊ณผ๋„ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ƒํƒœ์—์„œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์ƒํƒœ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ์•„์ด๋””์–ด์ž…๋‹ˆ๊นŒ? ์ด๊ฒƒ์€ ๊ตญ๊ฐ€์™€ ๊ด€๋ จ๋œ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๊ณ  ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ƒˆ๋กœ์šด ์ž…๋ ฅ์„ ๋„์ž…ํ•˜๋Š” ๋น„์šฉ์ด ์ด๋“๋ณด๋‹ค ํฌ์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. this.state ๊ฐ€ ๊ตฌ์„ฑ ์š”์†Œ ์ƒํƒœ์—๋งŒ ์ง‘์ค‘๋˜๋„๋ก ํ•˜๋ ค๋ฉด this.data ์˜ ํ•„๋“œ๋ฅผ this.props ๋˜๋Š” this.context ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

this.data ์ด๋ฆ„์ด ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. props๋Š” ๋ฐ์ดํ„ฐ, state๋Š” ๋ฐ์ดํ„ฐ, ๋ชจ๋“  ์ง€์—ญ ๋ณ€์ˆ˜๋Š” ๋ฐ์ดํ„ฐ์ž…๋‹ˆ๋‹ค. ์ด๋ฆ„์€ ์˜๋ฏธ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ๊ธฐ์กด ์˜๋ฏธ๋ฅผ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” this.observed ๋˜๋Š” ์‹ค์ œ๋กœ ์˜๋ฏธ๊ฐ€ ์žˆ๋Š” ๋‹ค๋ฅธ ์ด๋ฆ„์„ ํ›จ์”ฌ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ @matthewwithanm ์˜ ๋Œ“๊ธ€์— +1:

data ๋ณด๋‹ค ๋” ๋‚˜์€ ์ด๋ฆ„์ด ์žˆ์„๊นŒ์š”? ์˜ˆ๋ฅผ ๋“ค์–ด observed ๋Š” ๋ฉ”์„œ๋“œ์™€ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ์—ฐ๊ด€๋ฉ๋‹ˆ๋‹ค.

observe() ๊ฐ€ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋ฉด ๋งˆ์šดํŠธ ํ•ด์ œ๊ฐ€ ์ ˆ๋Œ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์ •๋ฆฌํ•˜๋Š” ์ผ์ข…์˜ ํ›„ํฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

props ๋˜๋Š” state ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค observe() ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋ฉด(๊ทธ๋ฆฌ๊ณ  context ?) observe ๋Š” ์„ฑ๋Šฅ์— ์ตœ์ ํ™”๋˜์–ด์•ผ ํ•˜๋ฉฐ ์ด์ƒ์ ์œผ๋กœ๋Š” ์‹ค์ˆ˜๋กœ ๋น„์‹ธ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋œจ๊ฑฐ์šด ๊ธธ์˜ ์ผ๋ถ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” React Nexus์—์„œ ์ด๊ฒƒ์„ ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ฐ”์ธ๋”ฉ์€ ์ด์ „ ๋ฐ”์ธ๋”ฉ๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ œ๊ฑฐ๋œ ๋ฐ”์ธ๋”ฉ์€ ๊ตฌ๋… ์ทจ์†Œ๋˜๊ณ  ์ถ”๊ฐ€๋œ ๋ฐ”์ธ๋”ฉ์€ ๊ตฌ๋…๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ƒํƒœ๊ฐ€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์„ ๋ฏฟ๊ฒŒ ๋˜์—ˆ๊ณ , ๋‚ด ์ž์‹ ์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ทธ๊ฒƒ์„ ๋œ ์‚ฌ์šฉ ํ•˜๊ณ  ๋Œ€์‹  ํ–ˆ์Šต๋‹ˆ๋‹ค . ์ด๊ฒƒ์ด @fisherwebdev๊ฐ€ ์ œ๊ธฐํ•œ ์šฐ๋ ค( observe ๊ฐ€ state ์— ์˜์กดํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ์ƒ๊ฐ์ด๋ผ๊ณ  ํ™•์‹ ํ•˜์ง€ ๋ชปํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. State๋Š” props์— ์˜์กดํ•˜๊ณ  ๊ด€์ฐฐ์€ state _and_ props์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ๋ณต์žกํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ์ฐจ๋ผ๋ฆฌ observe(props) ๊ฐ–๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ ๊ด€์ฐฐ์ด state ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ซ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„ ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. @gaearon ์ด ์ง€์ ํ–ˆ๋“ฏ์ด ์ƒํƒœ๋ฅผ ํ•œ ๋‹จ๊ณ„ ์œ„์˜ ์ˆ˜์ค€์œผ๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ๊ฒƒ์€ ์ถฉ๋ถ„ํžˆ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  ๋‚˜๋ฉด ๋” ๊น”๋”ํ•ด์ง‘๋‹ˆ๋‹ค. observe ๊ฐ€ state ์— ์ž ์žฌ์ ์œผ๋กœ ์˜์กดํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋…ผ๋ฆฌ๊ฐ€ ํ›จ์”ฌ ๋” ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค. props ์—๋งŒ ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ componentDidMount / componentWillReceiveProps ์— ํฌํฌ ์—†๋Š” ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ค‘์š” ๊ฒฝ๋กœ์˜ ์ฝ”๋“œ๊ฐ€ ์ ์œผ๋ฉด ๋ Œ๋”๋ง ์ฃผ๊ธฐ๊ฐ€ ๋” ๋‹จ์ˆœํ•ด์ง€๊ณ  ๊ตฌ๋…์„ ๋‹ค์‹œ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ์˜๋„ํ•˜์ง€ ์•Š์€ ์—…๋ฐ์ดํŠธ ์ˆ˜๋„ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

๊ด€์ฐฐ(์†Œํ’ˆ)์— ๋Œ€ํ•ด +1

์šฐ๋ฆฌ๊ฐ€ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์ ์„์ˆ˜๋ก ๋” ๋‚˜์€ IMO๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ์„œ React๊ฐ€ ๊ฐ€๋Šฅํ•œ ํ•œ ์œ ์—ฐํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ด€์ฐฐ์ด ์ƒํƒœ์— ์˜์กดํ•˜๋Š” ๊ฒƒ์€ ์ข‹์€ ์ƒ๊ฐ์ด ์•„๋‹ˆ๋ผ๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ, ๋ณต์žกํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ, ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ ์ƒํƒœ์— ์˜์กดํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ React ์‚ฌ์šฉ์ž์—๊ฒŒ ํ—ˆ์šฉ๋˜์–ด์•ผ ํ•˜๋Š” ์„ ํƒ์ž…๋‹ˆ๋‹ค.
ํ˜„์žฌ API๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ (์˜ˆ๋ฅผ ๋“ค์–ด ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฒƒ ์ด์ƒ๊ณผ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ์œ ์—ฐ์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ€๋Šฅํ•œ ํ›„ํฌ ์ œ์™ธ), ๊ด€์ฐฐ ๋ฉ”์„œ๋“œ์—์„œ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋˜์ง€ ์•Š์Œ์„ ์„ค๋ช…ํ•˜๋Š” ๋ฌธ์„œ๋ฅผ ์ฆ๋ช…ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์ด ์˜ณ์€ ์ผ์„ ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๊ณ  ์šฐ๋ฆฌ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉํ–ฅ์œผ๋กœ ์ธ๋„๋˜๊ธฐ๋ฅผ ์›ํ•œ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค. ์ˆ˜๋ฝํ•˜์ง€ ์•Š๋Š” ์ƒํƒœ๋ฅผ ๊ด€์ฐฐํ•˜๋ฉด ๋ฌธ์„œ์—์„œ "์ด๊ฒƒ์€ ๋ฐ˜ํŒจํ„ด์ž…๋‹ˆ๋‹ค"์™€ ๊ฐ™์€ ๊ฒƒ์„ ์‹ค์ˆ˜๋กœ ์ฐพ๋Š” ๊ฒƒ๋ณด๋‹ค ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘: ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ flatMap ์ฐพ๊ณ  ์žˆ์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋ฉ”์‹œ์ง€ ๋ฐฐ์—ด์„ ํ‰ํ‰ํ•˜๊ฒŒ ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ˜ผ๋ž€์Šค๋Ÿฌ์› ์ง€๋งŒ ๋” ๋†’์€ ์ˆ˜์ค€์—์„œ ์ž‘๋™ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค(๋ฉ”์‹œ์ง€ ๊ด€์ฐฐ ๊ฐ€๋Šฅ).

์ œ์•ˆ๋œ API๋Š” ํ•œ ๋ฐ์ดํ„ฐ ํ•„๋“œ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ํ•„๋“œ์˜ ๊ฒฐ๊ณผ์— ์ข…์†๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๊นŒ? ์ฆ‰, ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ๋ฅผ ๋งคํ•‘ํ•˜๊ณ  ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

observe(props, context) {
  if (!props.params.threadID) {
    return {};
  }

  const observeThread = ThreadStore.observeGetByID(
    {id: props.params.threadID}
  );
  return {
    thread: observeThread,
    messages: observeThread.map(thread => {
      return MessageStore.observeGetByIDs({ids: thread.messageIDs});
    })
  };
}

๋‚˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ต์ €๋ฒ„๋ธ”์— ์ต์ˆ™ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ์™„์ „ํžˆ ํ‹€๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. promise-land์—์„œ ์ด๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. then ์—์„œ promise๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ›„์† then ์ด ํ•ด๋‹น promise๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

this.state ์˜์กดํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค๋Š” ์˜๊ฒฌ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์บก์Šํ™”๋œ ์ƒํƒœ๋Š” ํ™•์‹คํžˆ React๋ฅผ ํ›จ์”ฌ ๋” ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค์ง€๋งŒ ๊ทธ๊ฒŒ ์ „๋ถ€์ž…๋‹ˆ๋‹ค. ์บก์Šํ™”๋œ ์ƒํƒœ๊ฐ€ ์—†์œผ๋ฉด ๋ฉ”๋ชจํ™”๋œ ์ฆ‰์‹œ ๋ชจ๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. "Stores"์— ์˜ฌ์ธํ•˜๋ฉด ๋„ค, ์ƒํƒœ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ React์—์„œ ๊ทธ๋ ‡๊ฒŒ ํ•˜๋„๋ก ๊ทœ์ •ํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

์ถ”๊ฐ€ ๋ž˜ํผ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ํŒจํ„ด์ด ์žˆ์ง€๋งŒ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ์—์„œ๋Š” ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ƒ์  ์™ธ์— ๊ด€์ฐฐ๋œ ๋ฐ์ดํ„ฐ๋Š” ๊ฐ„์ ‘์ ์ด๋”๋ผ๋„ ํ•ญ์ƒ ์ƒํƒœ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๊ด€์ฐฐ์— ์˜์กดํ•˜๋Š” ๊ฒƒ์ด ๋‚˜์œ ์Šต๊ด€์€ ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ

observe() {
  return { items: Items.getPagedItems({ pageIndex: this.state.currentPage }) };
}

observe ๊ฐ€ state ์— ์˜์กดํ•˜์ง€ ์•Š๋”๋ผ๋„ ์—ฌ์ „ํžˆ props ๋ฐ context ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. context ์— ์˜์กดํ•˜์ง€ ์•Š๋”๋ผ๋„ $# observe ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์˜ ์†Œํ’ˆ์„ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด context ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ„์ ‘ ์ฐธ์กฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

observe ๋Š” props๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ Œ๋” ํŒจ์Šค๊ฐ€ ๋‹ค์šด๋  ๋•Œ๋งˆ๋‹ค ์žฌํ‰๊ฐ€๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฒฐ๊ณผ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ํ™•์‹คํžˆ ๋น„๊ตํ•˜๊ณ  ๋™์ผํ•œ ๊ด€์ฐฐ ํ•ญ๋ชฉ์ด ๋ฐ˜ํ™˜๋˜๋Š” ๊ฒฝ์šฐ ๊ตฌ๋… ์ทจ์†Œ/์žฌ๊ตฌ๋…์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐœ๋ณ„ ์†์„ฑ(shouldComponentUpdate ์ œ์™ธ)์— ๋Œ€ํ•ด diff๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ Map ๋ฅผ ์ „์› ๊ธฐ๋Šฅ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๊ณ ์œ ํ•œ ์บ์‹œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด ํŠธ๋ฆฌ์˜ ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ์š”์†Œ์— ๋™์ผํ•œ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋™์ผํ•œ ์‚ฌ์šฉ์ž๋ฅผ ๋กœ๋“œํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ตฌ์„ฑ ์š”์†Œ. ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋„ Observable์„ ๋‹ค์‹œ ๋งŒ๋“ค๊ณ  ๊ฒฐ๊ตญ์—๋Š” ํ•˜๋‹จ ์บ์‹œ์— ๋„๋‹ฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ React ์กฐ์ •์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋Š๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด observe ํ›„ํฌ๋Š” ์ƒํƒœ๊ฐ€ Observable์—์„œ ์บก์ฒ˜๋œ๋‹ค๋Š” ์ ์—์„œ React๋ฅผ ์™„์ „ํžˆ ๋ฐ˜์‘์ ์œผ๋กœ ๋งŒ๋“ค๋„๋ก ์„ค๊ณ„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ฃผ์š” ์„ค๊ณ„ ๋ชฉํ‘œ๋Š” ํด๋กœ์ €์™€ ๊ฒฐํ•ฉ๊ธฐ์—์„œ ์ƒํƒœ๋ฅผ ํŠธ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ณ  ๋Œ€์‹  ๋™๊ฒฐ ๋ฐ ๋ถ€ํ™œ์ด ๊ฐ€๋Šฅํ•˜๊ณ  ์ž‘์—…์ž ๊ฐ„์— ์ž ์žฌ์ ์œผ๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊นจ๋—ํ•˜๊ณ  ๋ณ„๋„์˜ ์ƒํƒœ ํŠธ๋ฆฌ๋ฅผ ๊ฐ–๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜์˜ ๋งˆ์ง€๋ง‰ ์š”์ ์— ์ด๋ฅด๊ฒŒ ํ•˜๋Š” ๊ฒƒ์€...

์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์„ ํ•ต์‹ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ _ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค_. ์›๋ž˜ "๊ณต๊ฐœ" ์ธํ„ฐํŽ˜์ด์Šค๋Š” mountComponent/receiveComponent์˜€์œผ๋ฉฐ ๊ทธ ์œ„์— ์ „์ฒด ๋ณตํ•ฉ ๊ตฌ์„ฑ ์š”์†Œ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด์ œ ๋” ๋†’์€ ์ถ”์ƒํ™” ๋ง‰๋Œ€์— ์˜ํ•ด ํ™œ์„ฑํ™”๋˜๋Š” ๋‹ค๋ฅธ ๊ฒƒ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”์ƒํ™” ๋ง‰๋Œ€๋ฅผ ๋†’์ด๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ๊ฐ•๋ ฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ์€ ๋งŽ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ „์ฒด ์ตœ์ ํ™”์™€ ๊ฐ™์€.

React์˜ ์ฃผ์š” ๋ชฉ์ ์€ ์ƒํƒœ๊ณ„์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ถ”์ƒํ™”๊ฐ€ ๊ณต์กดํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ ์š”์†Œ ๊ฐ„์— ๊ณ„์•ฝ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์—ญํ• ์˜ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ๊ณตํ†ต ๊ฐœ๋…์— ๋Œ€ํ•œ ์ถ”์ƒํ™” ์ˆ˜์ค€์„ ๋†’์—ฌ ์ƒˆ๋กœ์šด ๊ต์ฐจ ๊ตฌ์„ฑ ์š”์†Œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•˜์œ„ ํŠธ๋ฆฌ์˜ ๋ชจ๋“  ์ƒํƒœ๋ฅผ ์ €์žฅํ•œ ๋‹ค์Œ ํ•˜์œ„ ํŠธ๋ฆฌ๋ฅผ ๋ถ€ํ™œ์‹œํ‚ต๋‹ˆ๋‹ค. ๋˜๋Š” ์„œ๋ฒ„์—์„œ ์ž๋™ ๋งˆ์šดํŠธ ํ•ด์ œ๋ฅผ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„์—์„œ ์กฐ์ •์˜ ํƒ€์ด๋ฐ ์ธก๋ฉด์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ๊ฒƒ์„ ๋ง›์žˆ๊ณ  ๊ท ์ผํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํฌํ•จ๋œ ์ผ๋ถ€ ๋ฐฐํ„ฐ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ดํฌ๋กœ ๋ชจ๋“ˆํ™”(์˜ˆ: ์ƒˆ๋กœ์šด ๋ผ์ดํ”„ ์‚ฌ์ดํด ํ›„ํฌ ์ถ”๊ฐ€)๊ฐ€ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋นŒ๋“œํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์—„๋ฐ€ํžˆ ๋งํ•˜๋ฉด ์ˆœ์ˆ˜ํ•œ ์Šน๋ฆฌ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์ ์„ ๊นจ๋‹ซ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์ถ”์ƒํ™”์— ๋Œ€ํ•ด ๋” ์ด์ƒ ์ถ”๋ก ํ•  ์ˆ˜ ์—†์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด์™€ ๊ฐ™์€ ๊ฒƒ์ด "์ฒ ํ•™/์„ค๊ณ„ ๋ชฉํ‘œ/๋น„๋ชฉํ‘œ"๋กœ ๋ฌธ์„œ์— ์žˆ์—ˆ์œผ๋ฉด ํ•ฉ๋‹ˆ๋‹ค.

React์˜ ์ฃผ์š” ๋ชฉ์ ์€ ์ƒํƒœ๊ณ„์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ถ”์ƒํ™”๊ฐ€ ๊ณต์กดํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑ ์š”์†Œ ๊ฐ„์— ๊ณ„์•ฝ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ ์—ญํ• ์˜ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ๊ณตํ†ต ๊ฐœ๋…์— ๋Œ€ํ•œ ์ถ”์ƒํ™” ์ˆ˜์ค€์„ ๋†’์—ฌ ์ƒˆ๋กœ์šด ๊ต์ฐจ ๊ตฌ์„ฑ ์š”์†Œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฑฐ ๋„ˆ๋ฌด ์ข‹์•„. ๋‚˜๋Š” ์ด๊ฒƒ์ด ์ผ์ข…์˜ ๋ฌธ์„œ์— ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ด๋ผ๋Š” @gaearon์˜ ๋ง์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ํ™•์‹คํžˆ ์ด๊ฒƒ์„ ํ•ต์‹ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.... ๊ทธ๋Ÿฌ๋‚˜, ๋” ๋†’์€ ์ถ”์ƒํ™” ๋ง‰๋Œ€์— ์˜ํ•ด ํ™œ์„ฑํ™”๋˜๋Š” ๋‹ค๋ฅธ ๊ฒƒ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”์ƒํ™” ๋ง‰๋Œ€๋ฅผ ๋†’์ด๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋” ๊ฐ•๋ ฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ์€ ๋งŽ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ „์ฒด ์ตœ์ ํ™”์™€ ๊ฐ™์€.

๋‚˜๋Š” (์ ์–ด๋„ ๋‚˜๋ฅผ ์œ„ํ•ด) ๊ณผ๋ฌตํ•จ์€ ๋‹ค๋ฅธ API๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ž‘๋™ํ•˜๊ธฐ ์œ„ํ•ด ๋น„์–ธ์–ด(์•„์ง ์ •์˜๋˜๊ณ  ์žˆ๋Š”) ๊ตฌ์กฐ์— ์˜์กดํ•˜๋Š” API๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์™„์ „ํžˆ ์ž˜ ๋  ์ˆ˜ ์žˆ์ง€๋งŒ Promise ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์„œ๋กœ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” Promise ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋ถˆํ•„์š”ํ•œ ๋ž˜ํ•‘ ๋ฐ ๋ฐฉ์–ด ์ž‘์—…์ด ๋ฐœ์ƒํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ์ตœ์ ํ™” ๊ธฐํšŒ๊ฐ€ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. . ๋˜๋Š” ๋” ๋‚˜์œ ๊ฒƒ์€ ๊ฒฐ์ฝ” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๊นจ์ง„ ๊ตฌํ˜„์œผ๋กœ jQuery์ฒ˜๋Ÿผ ๊ฐ‡ํžˆ๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@jquense ์ „์ ์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ž˜์ „์— ์ด ํ›„ํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. (์›๋ž˜ ์‹คํ—˜: https://github.com/reactjs/react-page/commit/082a049d2a13b14199a13394dfb1cb8362c0768a )

2๋…„ ์ „๋งŒ ํ•ด๋„ ์•„์ง ํ‘œ์ค€ํ™”๊นŒ์ง€๋Š” ๋ฉ€์—ˆ๋‹ค๋Š” ๊ฒƒ์ด ๋ง์„ค์—ฌ์กŒ๋‹ค. ์ฝ”์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์— ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์„ ์›ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ Observable๊ณผ ๊ฐ™์€ ๊ฒƒ์— ๋Œ€ํ•œ ํ•„์š”์„ฑ์— ๋™์˜ํ•˜๊ณ  ํ‘œ์ค€ํ™”๊ฐ€ ์ž…๋ง›์— ๋งž๋Š” API๊ฐ€ ์ œ์•ˆ๋˜๋Š” ์ง€์ ์— ๋„๋‹ฌํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์•ฝ๊ฐ„ ์กฐ์ •ํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•˜์ง€๋งŒ ๋†’์€ ์ˆ˜์ค€์˜ ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์ž‘๋™ํ•˜๋Š” ํ•œ ๊ต์ฒด ๊ฐ€๋Šฅํ•˜๊ณ  ๊ฒฐ๊ตญ ์ˆ˜๋ ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋ผ๋ฏธ์Šค์—์„œ ์ผ์–ด๋‚œ ์ผ์€ Observable์ด ๊ฒช์ง€ ์•Š๋Š” ํŠน์ • ์˜์—ญ์—์„œ API ๋ฐ ๋””๋ฒ„๊น… ์Šคํ† ๋ฆฌ๊ฐ€ ์‹ฌ๊ฐํ•˜๊ฒŒ ๋ถ€์กฑํ–ˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. Promises๊ฐ€ ์ตœ์†Œํ•œ์˜ ๋ถˆ์™„์ „ ์†”๋ฃจ์…˜์„ ํ‘œ์ค€ํ™”ํ•ด์•ผ ํ•˜๋Š” ๋ณด๋‹ค ์™„๋ฒฝํ•œ ์Šคํ† ๋ฆฌ์ž…๋‹ˆ๋‹ค.

์˜๊ฒฌ์˜ ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ๊ด€์ฐฐํ•œ Observables(์ €ํ•ญํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค)๋Š” Zalgo ์ž ์žฌ๋ ฅ์ž…๋‹ˆ๋‹ค. Observable์ด ๊ตฌ๋…์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ๋™๊ธฐ์ ์œผ๋กœ ๊ฐ’์„ ํ‘ธ์‹œํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ ๊ทธ๊ฒƒ์— ๋ฐ˜๋Œ€ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ํ•œ React์˜ Observable ์‚ฌ์šฉ์€ ์ด๊ฒƒ์— ์˜์กดํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋…ผํ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ผ๋ฐ˜์ ์œผ๋กœ ์†Œ๋น„์ž๊ฐ€ ํ•ญ์ƒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๊ณ  observeOn ์™€ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ ํ•ญ์ƒ ๋น„๋™๊ธฐ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Zalgo๊ฐ€ Observable์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์นจ๋‚ด ์ด์— ๋Œ€ํ•œ ํ•ฉ์˜๊ฐ€ ์ด๋ฃจ์–ด์ง€๊ฒŒ ๋˜์–ด ๊ธฐ์ฉ๋‹ˆ๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ Observable๋ณด๋‹ค ์ฑ„๋„์„ ์„ ํ˜ธํ•˜์ง€๋งŒ Observable์ด ์–ธ์–ด์— ์ถ”๊ฐ€๋œ๋‹ค๋ฉด ๋” ์ด์ƒ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ๊ธฐ๋ณธ API๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ๋น„ Observable๊ณผ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก API๋ฅผ ์ถฉ๋ถ„ํžˆ ์—ด์–ด ๋‘๋„๋ก ํ•ฉ์‹œ๋‹ค.

๋‚˜๋Š” Zalgo๋„ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Observable์„ ์‚ฌ์šฉํ•˜๋ฉด ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฒฝ์šฐ ๋น„๋™๊ธฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ๊ธฐ๋ณธ ์Šค์ผ€์ค„๋Ÿฌ๋Š” ๋น„๋™๊ธฐ์ด๋ฏ€๋กœ ํ•„์š”์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@sebmarkbage ๋‚˜๋Š” ๋‹น์‹ ์ด ๋‚ด ์šฐ๋ ค์˜ ๋Œ€๋ถ€๋ถ„์„ ํ•ด๊ฒฐํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ ์ด์ œ ์ด๊ฒƒ์„ ํ”„๋ ˆ์ž„์›Œํฌ์— ์ถ”๊ฐ€ํ•˜๋Š” ์ด์ ์„ ๋ด…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ this.data ์— ๋Œ€ํ•ด ์–ธ๊ธ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? -- (1) ํ•ด๋‹น ํ•„๋“œ๋ฅผ props/context/state๋กœ ์ ‘์„ ์ˆ˜/์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๊นŒ, ์•„๋‹ˆ๋ฉด (2) ์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์•ฝ๊ฐ„ ์ž์ „๊ฑฐ ํƒ€๊ธฐ -yํ•˜์ง€๋งŒ ์–ด์จŒ๋“  ๊ณ„์† ... Zalgo ๋ฌธ์ œ๋Š” API ๊ธฐ๋Œ€ ์ธก๋ฉด์—์„œ ์ค‘์š”ํ•œ์ง€ ์—ฌ๋ถ€์— ๊ด€ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ƒํ˜ธ ์šด์šฉ์„ฑ ๋ฐ ๊ตฌํ˜„ ์šฉ์ด์„ฑ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. Promise ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ธ๊ณ„๋ฅผ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ Promise๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋งค์šฐ ๋ฐฉ์–ด์ ์ด์–ด์•ผ ํ•˜๋Š” ์„ฑ๊ฐ€์‹  ์œ„์น˜์— ๋†“์ด๊ฒŒ ํ•œ Zalgo์— ๋Œ€ํ•œ ์ดˆ๊ธฐ ๋™์˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. (์•„๋ž˜์—์„œ ๋ฐ˜๋ณต๋˜๋Š” ๋‚ด ์œ„์˜ ์š”์ )

...ํ”„๋กœ๋ฏธ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(์‚ฌ์–‘ ๋ถˆ๋งŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํฌํ•จ)๊ฐ€ ์„œ๋กœ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ๊ณณ์—์„œ๋Š” ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•ด๊ฒฐ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋ถˆํ•„์š”ํ•œ ๋ž˜ํ•‘ ๋ฐ ๋ฐฉ์–ด ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ดˆ๊ธฐ ์•ฝ์†์ด ๋ชจ๋‘ ๋น„๋™๊ธฐ ํ•ด๊ฒฐ์„ ์ค€์ˆ˜ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ง€์ •๋˜๋”๋ผ๋„ thenables ๊ฐ€ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ์ˆ˜ ์—†์–ด ์ž ์žฌ์  ์ตœ์ ํ™”๊ฐ€ ์ค‘๋‹จ๋˜๋Š” ์œ„์น˜์— ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ React๊ฐ€ ์‚ฌ์šฉํ•  Observable ๊ตฌํ˜„์„ ์ œ๊ณตํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ฉฐ(๋ˆ„๊ฐ€ ๊ทธ๊ฒƒ์„ ์›ํ•˜๊ฒ ์Šต๋‹ˆ๊นŒ?) ์—ฌ๊ธฐ์—์„œ ํŠนํžˆ ๊ด€๋ จ์ด ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” Observable์ด ์ œ๊ณตํ•˜๋Š” ๋งค์šฐ ์‰ฌ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ „์ ์œผ๋กœ ์˜์กดํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ธฐ๊นŒ์ง€๋Š” ๋ช‡ ๋…„์ด ๊ฑธ๋ฆด ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ƒํ˜ธ์ž‘์šฉ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. @gaearon ์˜ ์š”์ ์— ๋”ํ•˜์—ฌ React๊ฐ€ ๋™๊ธฐํ™” ํ˜ธ์ถœ์— ์˜์กดํ•˜๊ณ  ํ•ญ์ƒ ๋น„๋™๊ธฐ์‹์œผ๋กœ ์ง€์ •๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ jquery์™€ ๊ฐ™์€ ์œ„์น˜๊ฐ€ ๋ถˆ๋Ÿ‰ ๊ตฌํ˜„์— ๊ฐ‡ํžˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์™„์ „ํžˆ ๋™์˜ ํ•ด. ์˜ค๋ž˜์ „์— ์ด ํ›„ํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. 2๋…„ ์ „๋งŒ ํ•ด๋„ ์•„์ง ํ‘œ์ค€ํ™”๊นŒ์ง€๋Š” ๋ฉ€์—ˆ๋‹ค๋Š” ๊ฒƒ์ด ๋ง์„ค์—ฌ์กŒ๋‹ค. ์ฝ”์–ด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ „์— ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ์„ ์›ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €๋„ ์ฐธ์„ํ•˜๊ณ  ์ƒ๊ฐํ•˜๋‹ˆ ๊ธฐ์˜๊ณ  ํ™•์‹คํžˆ ์œ„๋กœ๊ฐ€ โ€‹โ€‹๋ฉ๋‹ˆ๋‹ค. :) ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜์ ์œผ๋กœ ๋‚˜๋Š” Promise์˜ ์กฐ๊ธฐ ์ฑ„ํƒ์ด ๋‚ด๊ฐ€ ์—ฌ๊ธฐ์„œ ๋…ผ์˜ํ•˜๋Š” ๋‹จ์ ์˜ ๊ฐ€์น˜๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฏ€๋กœ ๋‚ด ์šฐ๋ ค๋ฅผ ์‹ซ์–ดํ•˜๊ฑฐ๋‚˜ ์Šน์ธํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์œผ๋กœ ๋ฐ›์•„๋“ค์ด์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๋‚˜๋Š” ์ด์— ๋Œ€ํ•œ ์ผ๋ฅ˜ API์˜ ์ „๋ง์— ๊ฝค ํฅ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜๋Š” ๋˜ํ•œ Observable์ด ์—ฌ๊ธฐ์—์„œ ์–ผ๋งˆ๋‚˜ ์ข‹์€/๊ฐ€์žฅ ํ•ฉ๋ฆฌ์ ์ธ ์„ ํƒ์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"Observable์˜ RxJS ๊ณ„์•ฝ์ด ๋” ์ผ๋ฐ˜์ ์ด๊ณ  ๋™๊ธฐ ์‹คํ–‰์„ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ @jhusain ์˜ ์ œ์•ˆ์ด ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋ฉด ๋Œ€์‹  ํ•ด๋‹น ๊ณ„์•ฝ์œผ๋กœ ์ „ํ™˜ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค."

์ปจํ…์ŠคํŠธ๋ฅผ ์กฐ๊ธˆ ๋” ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋น„์ฐจ๋‹จ ์—ญ์••์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•œ ํ‘œ์ค€์„ ์ œ๊ณตํ•˜๋Š” Reactive Streams ์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ(http://www.reactive-streams.org/)๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ๋„คํŠธ์›Œํฌ ํ”„๋กœํ† ์ฝœ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ(JVM ๋ฐ JavaScript)์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ๋…ธ๋ ฅ์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ฃผ์š” ๊ตฌํ˜„์€ fe Akka Streams ๋˜๋Š” RxJava์ž…๋‹ˆ๋‹ค. RxJ๊ฐ€ ์ด๋ฏธ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค, ๊ตฌ๋…์ž์˜ ํ˜„์žฌ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ค€์ˆ˜ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.onSubscribe(Subscription s), onNext(T t), onCompleted(), onError(Throwable t)์ž…๋‹ˆ๋‹ค.

@jhusain ์˜ ์ œ์•ˆ์ด ๋ฌด์—‡์ธ์ง€ ๋” ๋ฐํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

React๊ฐ€ ์ด ์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ๋ฅผ ์—„๊ฒฉํžˆ ์ค€์ˆ˜ํ•ด์•ผ ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ RxJ(์ค€์ˆ˜ํ•  ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •)๋ฅผ ์ค‘๊ฐ„์— ๋„ฃ๊ณ  React ์ธํ„ฐํŽ˜์ด์Šค์— ์ ์‘ํ•˜๊ณ  RxJ์— ๋Œ€ํ•œ ์—ญ์••๊ณผ ๊ฐ™์€ ๊ณ ๊ธ‰ ๊ฐœ๋…์„ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋งŽ์ด ์ ์‘ํ•˜์ง€ ์•Š์•„๋„ ๋จ).

์ด ์ด๋‹ˆ์…”ํ‹ฐ๋ธŒ์— ๋Œ€ํ•œ ์ž…์žฅ์ด๋‚˜ ๋ชฉํ‘œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@vladap ๋‚˜๋Š” ์ด๊ฒƒ์ด @jhusain์˜ ์–ธ๊ธ‰๋œ ์ œ์•ˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@jhusain ์„ ์ฝ์—ˆ์œผ๋ฉฐ ํ–ฅํ›„ ์ด ์‚ฌ์–‘์œผ๋กœ ์ด๋™ํ•  ๋™๊ธฐ๊ฐ€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠน๋ณ„ํ•œ ์žฅ์ ์ด ์žˆ๋‚˜์š”?

Reactive-streams ์‚ฌ์–‘์€ ๋” ํฐ ์ง€์›์„ ์ œ๊ณตํ•˜๋ฉฐ ์ด๋ฏธ ๋ฒ„์ „ 1.0์— ์žˆ์Šต๋‹ˆ๋‹ค. RxJava๋Š” ์ด๋ฏธ ์ด ์‚ฌ์–‘์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋Š” RxJ๊ฐ€ ๋”ฐ๋ฅผ ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋Ÿฌ๋‚˜ ํ™•์ธํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค).

์ด ๋ธ”๋กœ๊ทธ ๋Š” Akka ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์™€ ํ•จ๊ป˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์š”์•ฝํ•ฉ๋‹ˆ๋‹ค.

๋ฐฑ์—”๋“œ์™€ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ช‡ ๊ฐ€์ง€ ์ด์ ์ด ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฃผ๋กœ ๋‘˜ ๋‹ค์—์„œ ์ž‘์—…ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ์™€ ํ”„๋ก ํŠธ์—”๋“œ ๊ทธ๋ฃน ๊ฐ„์˜ ํ˜‘๋ ฅ์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹ค๋ฅธ ํ•œํŽธ์œผ๋กœ๋Š” websocket ๋˜๋Š” sse๊ฐ€ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์œ„ํ•œ ์‹ค์ œ ํ†ตํ•ฉ ์ง€์ ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ www.reactive-streams.org ์—์„œ ๊ตฌํ˜„์ž ๋ชฉ๋ก์„ ์ฐพ์„ ์ˆ˜ ์—†์ง€๋งŒ ๋งˆ์ง€๋ง‰์œผ๋กœ ํ™•์ธํ•œ ๊ฒƒ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

Bjรถrn Antonsson โ€“ Typesafe Inc.
๊ฐœ๋นˆ ๋น„์–ด๋งŒ โ€“ Oracle Inc.
์กด ๋ธŒ๋ฆฌ์Šค๋นˆ โ€“ Pivotal Software Inc.
์กฐ์ง€ ์บ ๋ฒจ โ€“ Netflix, Inc
๋ฒค ํฌ๋ฆฌ์Šคํ…์Šจ โ€“ Netflix, Inc
๋งˆํ‹ฐ์•„์Šค ๋„์—๋‹ˆ์ธ  โ€“ spray.io
๋งˆ๋ฆฌ์šฐ์Šค ์—๋ฆญ์„ผ โ€“ Twitter Inc.
ํŒ€ ํญ์Šค โ€“ Red Hat Inc.
Viktor Klang โ€“ Typesafe Inc.
Roland Kuhn ๋ฐ•์‚ฌ โ€“ Typesafe Inc.
Doug Lea โ€“ SUNY Oswego
์Šคํ…ŒํŒ ๋ง๋””๋‹ˆ โ€“ Pivotal Software Inc.
Norman Maurer โ€“ Red Hat Inc.
Erik Meijer โ€“ Applied Duality Inc.
ํ† ๋“œ ๋ชฝ๊ณ ๋ฉ”๋ฆฌ - Kaazing Corp.
ํŒจํŠธ๋ฆญ ๋…ธ๋“œ์›” โ€“ Typesafe Inc.
์š”ํ•˜๋„ค์Šค ๋ฃจ๋Œํ”„ โ€“ spray.io
Endre Varga โ€“ Typesafe Inc.

์–ด์ฉŒ๋ฉด ๋‚ด๊ฐ€ ๋„ˆ๋ฌด ๋ฉ€๋ฆฌ ๊ฐˆ ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋” ํฐ ๋งฅ๋ฝ์ด ๋ฏธ๋ž˜์˜ ๊ฒฐ์ •์— ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

@vladap ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๊ณ  github ๋ฌธ์ œ์—์„œ ๋ณธ ๊ฒƒ์—์„œ @jhusain ์€ ์ด๋ฏธ ๊ทธ๋“ค๊ณผ ํ•จ๊ป˜ ์ž‘์—…ํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ๊ทธ๋ ‡๊ฒŒ ๋งŽ์€ ๋ฌธ์ œ๊ฐ€ ์—†์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
์ธํ„ฐํŽ˜์ด์Šค ๊ด€์ ์—์„œ ๋‹ค๋ฅธ github ๋ฌธ์ œ ๋ฐ ๊ธฐํƒ€ ์‚ฌ์–‘ ๋ฌธ์„œ์—์„œ๋„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์—์„œ ๊ด€์ฐฐ์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒ์„ฑ๊ธฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์‹คํžˆ ์กด์ค‘ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

{
  next(value),
  throw(e),
  return(v)
}

๋ฐ˜์‘์— ๋Œ€ํ•ด ์•ˆ์ „ํ•ด์•ผ ํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์กด์ค‘ํ•˜๋Š” ๋‹จ์ผ '๊ตฌ๋…' ๋ฐฉ๋ฒ•์œผ๋กœ ๋งค์šฐ ๊ธฐ๋ณธ์ ์ธ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ๊ตฌํ˜„ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์ด๋ฆ„์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๊ทธ๋Ÿฐ ์˜๋ฏธ์—์„œ๋Š” ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์‚ฌ์–‘์—์„œ์™€ ๊ฐ™์€ ์ด๋ฆ„์„ ์„ ํ˜ธํ•  ๊ฒƒ์ด์ง€๋งŒ ๊ฒฐ๊ตญ์—๋Š” ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์ด ๋™์ผํ•˜๊ฒŒ ์ˆ˜ํ–‰๋˜๋Š” ํ•œ ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

onSubscribe()์— ํ•ด๋‹นํ•˜๋Š” ๋ˆ„๋ฝ์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์–ธ๊ธ‰ํ•œ ๋ธ”๋กœ๊ทธ์—์„œ ์ €์ž๋Š” ๊ทธ๊ฒƒ์ด ๋ฐฐ์••์„ ์ œ์–ดํ•˜๋Š” โ€‹โ€‹ํ•ต์‹ฌ์ด๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์œผ๋กœ๋ถ€ํ„ฐ ๋‚˜๋Š” React๊ฐ€ ๋ฐฐ์•• ์ œ์–ด์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ฑฐ๋‚˜ ๊ทธ๊ฒƒ์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ์ „๋žต์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋ณต์žกํ•œ ์ผ์ด๋ฏ€๋กœ React ๊ด€์‹ฌ์‚ฌ๊ฐ€ ์•„๋‹˜์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

์ „๋žต์ด ์„ ์„ ๋”ฐ๋ผ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์•ฑ์ด ๋ณต์žกํ•˜๊ณ  ์—ญ์•• ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ RxJS์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ ์‚ฌ์ด์— ๋ฌด์–ธ๊ฐ€๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ fe websocket์— ์ง์ ‘ ์—ฐ๊ฒฐํ•˜๋ฉด ์•ฑ์ด ๊ฐ„๋‹จํ•˜๊ณ  ์—…๋ฐ์ดํŠธ๊ฐ€ ๋Š๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์—ญ์•• ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

ํ–ฅํ›„ ECMAScript์— ๋Œ€ํ•ด ์ œ์•ˆ๋œ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์–ด๋””์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์ด๋ฏธ ์žˆ๋Š” ๊ฒฝ์šฐ.

ํ˜„์žฌ ์ œ์•ˆ์€ ์—ฌ๊ธฐ์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

https://github.com/jhusain/asyncgenerator

JH

2015๋…„ 5์›” 7์ผ ์˜ค์ „ 2์‹œ 32๋ถ„์— vladap [email protected] ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค.

ํ–ฅํ›„ ECMAScript์— ๋Œ€ํ•ด ์ œ์•ˆ๋œ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์–ด๋””์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์ด๋ฏธ ์žˆ๋Š” ๊ฒฝ์šฐ.

โ€”
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ํšŒ์‹ ํ•˜๊ฑฐ๋‚˜ GitHub์—์„œ ํ™•์ธํ•˜์„ธ์š”.

Reactive Streams Proposal(RSP)์€ ์—ญ์••์„ ์ฒ˜๋ฆฌํ•˜๋Š” Observable์„ ๋„์ž…ํ•˜๊ธฐ ๋•Œ๋ฌธ์— TC-39 ์ œ์•ˆ๋ณด๋‹ค ๋” ๋‚˜์•„๊ฐ‘๋‹ˆ๋‹ค. RSP Observable์€ ์—ญ์••์„ ์กด์ค‘ํ•˜๋ฉด์„œ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ŠคํŠธ๋ฆผ์„ ํšจ์œจ์ ์œผ๋กœ ์ „์†กํ•˜๋„๋ก ์ตœ์ ํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ถ€๋ถ„์ ์œผ๋กœ RxJava์—์„œ ์ˆ˜ํ–‰๋œ ์ž‘์—…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ, ์ด๋Š” ๋งค์šฐ ์ธ์ƒ์ ์ธ ์—”์ง€๋‹ˆ์–ด๋ง ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค(์ „์ฒด ๊ณต๊ฐœ: Netflix์˜ ๋™๋ฃŒ Ben Christensen์ด ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค).

๋ณด๋‹ค ์›์‹œ์ ์ธ Observable ์œ ํ˜•์„ ํ‘œ์ค€ํ™”ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•œ ์ฃผ๋œ ์ด์œ ๋Š” ์ฃผ์˜์ž…๋‹ˆ๋‹ค. ๋” ์›์‹œ์ ์ธ Observable์€ ES2015 Iterable ๊ณ„์•ฝ์˜ ์ด์ค‘์œผ๋กœ, ์œ ํ˜•์ด ์ ์–ด๋„ ES2015์—์„œ ์ด๋ฏธ ํ‘œ์ค€ํ™”๋œ ์œ ํ˜•๋งŒํผ ์œ ์—ฐํ•˜๋‹ค๋Š” ๊ท€์ค‘ํ•œ ๋ณด์žฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ JS์—๋Š” ๋ฐฐ์••์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ Observable์— ๋Œ€ํ•œ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์—์„œ DOM์€ ํ‘ธ์‹œ ์ŠคํŠธ๋ฆผ์˜ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‹ฑํฌ์ด๋ฉฐ ๋ฒ„ํผ์ฒ˜๋Ÿผ ํšจ๊ณผ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. RSP ์œ ํ˜•์ด ๋” ๋ณต์žกํ•˜๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ์šฐ๋ฆฌ์˜ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋” ์›์‹œ์ ์ธ ์œ ํ˜•์„ ๋จผ์ € ํ‘œ์ค€ํ™”ํ•œ ๋‹ค์Œ ๋‚˜์ค‘์— ๋” ๊ณ ๊ธ‰ ์œ ํ˜•์„ ๊ตฌํ˜„ํ•  ์—ฌ์ง€๋ฅผ ๋‚จ๊ฒจ๋‘๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ƒ์ ์œผ๋กœ๋Š” ์‚ฌ์šฉ์ž ์˜์—ญ์—์„œ ๊ฒ€์ฆ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ RxJS๋Š” ํ˜„์žฌ RSP Observable์„ ๊ตฌํ˜„ํ•  ๊ณ„ํš์ด ์—†์Šต๋‹ˆ๋‹ค.

JH

2015๋…„ 5์›” 7์ผ ์˜ค์ „ 2์‹œ 30๋ถ„์— vladap [email protected] ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ผ์Šต๋‹ˆ๋‹ค.

๊ฐ™์€ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ๋‹ค๋ฅธ ์ด๋ฆ„์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๊ทธ๋Ÿฐ ์˜๋ฏธ์—์„œ๋Š” ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์‚ฌ์–‘์—์„œ์™€ ๊ฐ™์€ ์ด๋ฆ„์„ ์„ ํ˜ธํ•  ๊ฒƒ์ด์ง€๋งŒ ๊ฒฐ๊ตญ์—๋Š” ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์ด ๋™์ผํ•˜๊ฒŒ ์ˆ˜ํ–‰๋˜๋Š” ํ•œ ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

onSubscribe()์— ํ•ด๋‹นํ•˜๋Š” ๋ˆ„๋ฝ์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์–ธ๊ธ‰ํ•œ ๋ธ”๋กœ๊ทธ์—์„œ ์ €์ž๋Š” ๊ทธ๊ฒƒ์ด ๋ฐฐ์••์„ ์ œ์–ดํ•˜๋Š” โ€‹โ€‹ํ•ต์‹ฌ์ด๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์œผ๋กœ๋ถ€ํ„ฐ ๋‚˜๋Š” React๊ฐ€ ๋ฐฐ์•• ์ œ์–ด์— ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ฑฐ๋‚˜ ๊ทธ๊ฒƒ์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ์ „๋žต์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋ณต์žกํ•œ ์ผ์ด๋ฏ€๋กœ React ๊ด€์‹ฌ์‚ฌ๊ฐ€ ์•„๋‹˜์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

์ „๋žต์ด ์„ ์„ ๋”ฐ๋ผ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์•ฑ์ด ๋ณต์žกํ•˜๊ณ  ์—ญ์•• ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ RxJS์™€ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ ์‚ฌ์ด์— ๋ฌด์–ธ๊ฐ€๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ fe websocket์— ์ง์ ‘ ์—ฐ๊ฒฐํ•˜๋ฉด ์•ฑ์ด ๊ฐ„๋‹จํ•˜๊ณ  ์—…๋ฐ์ดํŠธ๊ฐ€ ๋Š๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์—ญ์•• ๋ฌธ์ œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

โ€”
์ด ์ด๋ฉ”์ผ์— ์ง์ ‘ ํšŒ์‹ ํ•˜๊ฑฐ๋‚˜ GitHub์—์„œ ํ™•์ธํ•˜์„ธ์š”.

๊ท€์ค‘ํ•œ ์„ธ๋ถ€์‚ฌํ•ญ์„ ์œ„ํ•œ ๋งŽ์€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ๋งŽ์€ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@gaearon ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ES6 ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ParseReact๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— mixin์˜ ๊ด€์ฐฐ API๋ฅผ ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋‹ค์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

https://gist.github.com/amccloud/d60aa92797b932f72649 (์•„๋ž˜ ์‚ฌ์šฉ๋ฒ•)

  • ๋‚˜๋Š” ๊ด€์ฐฐ์ด ๊ตฌ์„ฑ ์š”์†Œ์— ์ •์˜๋˜๊ฑฐ๋‚˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์ „๋‹ฌ๋˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. (๋‚˜๋Š” ๋‘˜์„ ํ•ฉ์น  ์ˆ˜๋„ ์žˆ๋‹ค)
  • ์˜ต์ €๋ฒ„๋ธ”์„ ์†Œํ’ˆ์œผ๋กœ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค(this.data ๋˜๋Š” this.props.data ์—†์Œ).

@aaronshaf @gaearon ์ผ๋“ฑ์„ ์œผ๋กœ ๋งŒ๋“œ๋Š” ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1) props ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์žก์•„๋จน์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค๋ฅธ ์šฉ๋„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” props ๊ฐ์ฒด์˜ ๋ฐ์ดํ„ฐ์™€ ๊ฐ™์€ ์ด๋ฆ„์„ ์š”๊ตฌํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ๋” ๋งŽ์€ ์ด๋ฆ„์„ ๊ณ„์† ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ด์ œ ์ด๋Ÿฌํ•œ ์ด๋ฆ„์„ ๊ณ ์œ ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์ž‘์„ฑ๋˜์—ˆ์„ ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ์„ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด์ œ ์ด๋ฆ„ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

๊ฒŒ๋‹ค๊ฐ€, ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋Š” ๋ž˜ํ•‘๋œ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ณ„์•ฝ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐœ๋…์ ์œผ๋กœ ์•„์›ƒ๊ณผ ๋™์ผํ•œ ์†Œํ’ˆ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์†Œ๋น„์ž๊ฐ€ ์ˆ˜์‹ ํ•œ ๊ฒƒ๊ณผ ์™„์ „ํžˆ ๋‹ค๋ฅธ props ์„ธํŠธ๋ฅผ ์ œ๊ณตํ•  ๋•Œ ์‚ฌ์šฉํ•˜๊ณ  ๋””๋ฒ„๊ทธํ•˜๋Š” ๊ฒƒ์ด ํ˜ผ๋ž€์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค.

HOC ๋Œ€์‹  ์ผ๋ฐ˜ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import Observe from 'react/addons/Observe';

class Foo {
  render() {
    return (
      <Observe
        render={this.renderData}
        resources={{
          myContent: xhr(this.props.url)
        }} />
    );
  }

  renderData({ myContent }) {
    if (myContent === null) return <div>Loading...</div>;
    return <div>{myContent}</div>;
  }
}

render ์†Œํ’ˆ์œผ๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๋ฅผ ์ œ์–ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฆ„์ด ์ถฉ๋Œํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— ์†Œ์œ ์ž์˜ ์ƒํƒœ๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋‚ด๊ฐ€ ๋†“์น˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

Observe ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ props์—์„œ Observables๋ฅผ ๊ฐ€์ ธ์˜จ ๊ฒฝ์šฐ ์ด๊ฒƒ์€ ํ›จ์”ฌ ๋œ ์žฅํ™ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

render() {
  return (
    <Observe myContent={Observable.fetch(this.props.url)}
             render={this.renderData} />
  );
}

renderData({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

์ด๊ฒƒ์— ๋Œ€ํ•œ ์ข‹์€ ์ ์€ render ์— ์ „ํ˜€ ๋“ค์–ด๊ฐ€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— shouldComponentUpdate ๊ฐ€ false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์žฌ๊ตฌ๋…์„ ํ”ผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ Observe ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋ž˜ํ•‘ํ•˜๋Š” render ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@observe(function (props, state, context) {
  myContent: Observable.fetch(props.url)
})
render({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ๋…ผ๋ฆฌ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ๋Œ€์‹  render ๋ฅผ ์ˆœ์ˆ˜ํ•œ ๋ Œ๋”๋ง ํ•จ์ˆ˜๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.
์ œ ์ƒ๊ฐ์—๋Š” ์ดˆ๊ธฐ ์ œ์•ˆ์ด ์ข‹์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ƒํƒœ๊ฐ€ rx-react ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•˜๋ฉฐ ๋งค์šฐ ์ผ๊ด€์„ฑ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋Š” ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ๋…ผ๋ฆฌ์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋ฅผ ๊ดด๋กญํžˆ๋Š” ์œ ์ผํ•œ ๊ฒƒ์€ ํ•˜๋‚˜์˜ ์˜ต์ €๋ฒ„๋ธ” ๋Œ€์‹  ์˜ต์ €๋ฒ„๋ธ”์˜ ๋งต์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ต์ €๋ฒ„๋ธ” ๊ตฌ์„ฑ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ณตํ•˜์ง€ ์•Š์ง€๋งŒ ์ด๊ฒƒ์€ ์‚ฌ์†Œํ•œ ๋ฌธ์ œ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ ๋…ผ๋ฆฌ๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ผ๋ถ€ ์ž…๋ ฅ์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. <Observe /> ๊ตฌ์„ฑ ์š”์†Œ๋งŒ ๋ Œ๋”๋งํ•˜๋Š” ์œ„์˜ ๋ฒ„์ „์œผ๋กœ ์„คํƒ•์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์ƒํƒœ ์ €์žฅ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ฒƒ์€ ๋“œ๋ฌธ ์ผ์ด ์•„๋‹ˆ๋ฏ€๋กœ render ๊ฐ€ ์ง€๊ธˆ๋ณด๋‹ค ๋œ ์ˆœ์ˆ˜ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” #3858์˜ ์ตœ์„ ๊ณผ ์ด ์ œ์•ˆ์„ ๊ฒฐํ•ฉํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ๋‹ค.

๋ชจ๋“  HOC ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ ์ด์ ์€ ๋ช…์‹œ์ ์ด์ง€๋งŒ @sebmarkbage์— ์˜ํ•ด ๋‹จ์ ์ด ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค . props ์ด๋ฆ„์€ ์–ด๋Š ์‹œ์ ์—์„œ ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ œ์•ˆ์—์„œ ์ด์ ์€ ๋ช…์‹œ์ ์ด์ง€๋งŒ ๋ถ€์ •์ ์ธ ์ธก๋ฉด์€ ๋” ๋ณต์žกํ•œ ์ˆ˜๋ช… ์ฃผ๊ธฐ์™€ ๋” ํฐ ํ•ต์‹ฌ ๊ตฌ์„ฑ ์š”์†Œ API ํ‘œ๋ฉด์ž…๋‹ˆ๋‹ค.

#3858์—์„œ ์ด์ ์€ "memoized render" ์ข…์†์„ฑ์„ ๋ Œ๋”๋ง ์ž์ฒด์™€ ํ•จ๊ป˜ ๋ฐฐ์น˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์˜๋ฏธ๊ฐ€ ์žˆ์Œ). ๊ทธ๋Ÿฌ๋‚˜ ์ €๋Š” "look sync but is async" ์— ๋Œ€ํ•ด ์šฐ๋ คํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. this ์— ๋„ˆ๋ฌด ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ ๋ถˆ๋ณ€ ๋ชจ๋ธ๋กœ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค . ๋˜ํ•œ ์ˆ˜๋™์œผ๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ถ”์ ํ•˜๊ณ  ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ React์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ(๋˜๋Š” React์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋„๋ก ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒƒ)์— ๋Œ€ํ•ด ์‰ฝ๊ฒŒ ์ถ”๋ก ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— React-was-eas-to-reason-about ๋ฐฉ์‹์œผ๋กœ ์ €๋ฅผ ์ž˜๋ชป ๋ฌธ์ง€๋ฆ…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์„ฑ๋Šฅ์„ ๋ฐœํœ˜ํ•˜๊ณ  ์ƒ์šฉ๊ตฌ๋ฅผ ์ค„์ด๋Š” ๊ฒƒ ๋ชจ๋‘๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค๋Š” ์••๋ ฅ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ์ œ์•ˆ์—์„œ ๋‚˜๋Š” ์ฝ”๋กœ์ผ€์ด์…˜๊ณผ ๋ช…์‹œ์„ฑ์„ ์œ ์ง€ํ•˜๊ณ  ์žˆ์ง€๋งŒ:

  • <Observe /> (๋˜๋Š” <Observe /> ๋กœ render๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” observe() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ)๋Š” ์• ๋“œ์˜จ์ผ ๋ฟ์ด๋ฉฐ React ์ฝ”์–ด์— _any_ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ œ์•ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋ˆ„๊ตฌ๋‚˜ ์ž์‹ ์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋Œ€ํ•œ ์ž์ฒด ๊ด€์ฐฐ ๋…ผ๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ๊ฒฝ์šฐ ํ•˜๋‚˜์˜ ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ์ž์‹ ๋งŒ์˜ <Observe /> ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ  ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตฌ์„ฑ ์š”์†Œ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์†Œํ’ˆ ์ถฉ๋Œ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ์šฐ๋ฆฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋„๊ตฌ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•จ์œผ๋กœ์จ ๋„๊ทธํ‘ธ๋”ฉ์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ƒ์šฉ๊ตฌ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด ํ•ต์‹ฌ ๊ฐœ๋…์„ ๋„์ž…ํ•˜๋Š” ๋Œ€์‹  ์ƒ์šฉ๊ตฌ ์ถ•์†Œ ๋„๊ตฌ(๋ฐ์ฝ”๋ ˆ์ดํ„ฐ)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ ํ›Œ๋ฅญํ•œ ํ† ๋ก ๊ณผ ์ž‘์—…, ๋งŽ์€ ์กด๊ฒฝ์„ ํ‘œํ•ฉ๋‹ˆ๋‹ค. :)

๋‚˜๋Š” ์ด๊ฒƒ์ด ๊ทธ๊ฒƒ์„ ํ•ต์‹ฌ์œผ๋กœ ๋งŒ๋“ค ๊ฐ€์น˜๊ฐ€ ์—†๋‹ค๋Š” ๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์€ ์‚ฌ๋žŒ๋“ค์ด ๋” ์™„์ „ํžˆ ์ปค๋ฐ‹ํ•˜๊ธฐ ์ „์— ์ˆ˜๋ ดํ•˜๊ณ  ํ‘œ์ค€ํ™”ํ•˜๋ ค๊ณ  ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ์ถฉ๋ถ„ํ•œ ๊ฒฌ์ธ๋ ฅ์„ ์ด ์ œ์•ˆ์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋‚˜๋Š” ์ด ์ œ์•ˆ์ด ๋ฏธ๋‹ˆ๋ฉ€๋ฆฌ์ฆ˜์„ ์œ„ํ•ด https://github.com/facebook/react/issues/3858 ๋ฐ https://github.com/facebook/react/pull/3920 ๋ณด๋‹ค ๋‚ซ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๊ทธ๋ž˜์„œ ์†Œ๊ธˆ ์•Œ๊ฐฑ์ด) - @elierotenberg ์˜ ๋ฉ‹์ง„ ์ž‘์—…๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ ์ด ์•ฑ์ด React์—์„œ 100%๊ฐ€ ์•„๋‹ˆ๋ฉฐ ์ƒํ˜ธ

CoffeeScript์™€ ๋ฏน์Šค์ธ์— ๋Œ€๋น„ํ•˜๊ฑฐ๋‚˜ ์›ํ•˜๋Š” ๊ฒฝ์šฐ ์ด๊ฒƒ์ด ES6์ฒ˜๋Ÿผ ๋ณด์ผ ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋ˆˆ์„ ๊ฐ€๋Š˜๊ฒŒ ๋œจ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. :)

_ = require 'lodash'

module.exports = DeclareNeedsMixin = 
  componentDidMount: ->
    <strong i="12">@needsConsumerId</strong> = _.uniqueId @constructor.displayName
    <strong i="13">@sinkNeeds</strong> <strong i="14">@props</strong>, <strong i="15">@state</strong>

  componentWillUpdate: (nextProps, nextState) ->
    <strong i="16">@sinkNeeds</strong> nextProps, nextState

  componentWillUnmount: ->
    @props.flux.declareNeeds <strong i="17">@needsConsumerId</strong>, []

  sinkNeeds: (props, state) ->
    if not @declareNeeds?
      return console.warn 'Missing method required for DeclareNeedsMixin: `declareNeeds`', @

    needs = <strong i="18">@declareNeeds</strong> props, state
    props.flux.declareNeeds <strong i="19">@needsConsumerId</strong>, needs

  # Intended to be overridden by the host class.
  # Returns a set of facts, stored as an array.
  # Yes, immutable data is awesome, that's not the point here though. :)
  # Facts are serializable data, just values.
  # declareNeeds: (props, state) ->
  #   []

๊ทธ๋ฆฌ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

module.exports = EmailThreads = React.createClass
  displayName: 'EmailThreads'
  mixins: [DeclareNeedsMixin]

  propTypes:
    flux: PropTypes.flux.isRequired

  declareNeeds: (props, state) ->
    [Needs.GmailData.myThreads({ messages: 20 })]

  ...

๋”ฐ๋ผ์„œ declareNeeds ๋Š” ์ด ๊ตฌ์„ฑ ์š”์†Œ์— ํ•„์š”ํ•œ ์„ค๋ช…์— ๋Œ€ํ•œ props ๋ฐ state์˜ ๋‹จ๋ฐฉํ–ฅ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ๊ตฌํ˜„์—์„œ @props.flux.declareNeeds ์˜ ์ˆ˜์‹ ์ธก์€ ์ตœ์ƒ์œ„ ๊ตฌ์„ฑ ์š”์†Œ์— ์„ค์ •๋˜์–ด ์ด๋Ÿฌํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ProcessSink ๊ฐœ์ฒด๋กœ ํก์ˆ˜ํ•ฉ๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ๋Œ€๋กœ ์ผ๊ด„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋™์ผํ•œ flux ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ์—์„œ needs ์ค‘๋ณต์„ ์ œ๊ฑฐํ•œ ๋‹ค์Œ ์ด๋Ÿฌํ•œ ์š”๊ตฌ ์‚ฌํ•ญ(์˜ˆ: ์†Œ์ผ“ ์—ฐ๊ฒฐ ๋˜๋Š” HTTP ์š”์ฒญ ๋งŒ๋“ค๊ธฐ)์„ ์ถฉ์กฑํ•˜๊ธฐ ์œ„ํ•ด ๋ถ€์ž‘์šฉ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋” ์ด์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์—†์„ ๋•Œ ์†Œ์ผ“ ์—ฐ๊ฒฐ๊ณผ ๊ฐ™์€ ์ƒํƒœ ์ €์žฅ ํ•ญ๋ชฉ์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ฐธ์กฐ ์นด์šดํŒ…์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋Š” ์†Œ์ผ“ ์ด๋ฒคํŠธ ๋ฐ ์š”์ฒญ๊ณผ ๊ฐ™์€ ์ƒํƒœ ์ €์žฅ ๋น„ํŠธ์—์„œ ๋””์ŠคํŒจ์ฒ˜๋กœ(๊ทธ๋Ÿฐ ๋‹ค์Œ ์ €์žฅ์†Œ ๋˜๋Š” ์–ด๋””๋“ ์ง€) ํ•„์š”๋ฅผ ์ถฉ์กฑ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋‹ค์‹œ ํ๋ฆ…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋™๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์•„์ง ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์„ ๋•Œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ Œ๋”๋ง์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์—์„œ ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์†”๋ฃจ์…˜์„ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ ํ˜„์žฌ API๊ฐ€ ๊ทธ๋Ÿฐ ์ข…๋ฅ˜์˜ ์‹คํ—˜์„ ์ •๋ง ์ž˜ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋˜ ๋‹ค๋ฅธ ๋ชฉ์†Œ๋ฆฌ๋กœ ์ด๊ฒƒ์„ ๊ณต์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹ ๊ฐ„์˜ ์ƒํ˜ธ ์šด์šฉ์„ฑ์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”์–ด๊ฐ€ ์ทจํ•  ์ˆ˜ ์žˆ๋Š” ์ตœ์†Œ ๋‹จ๊ณ„์˜ ๊ด€์ ์—์„œ @elierotenberg ๊ฐ€ ์ด๋ฅผ ๋ชป ๋ฐ•์•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์–ด์จŒ๋“  ํƒ‘์žฌ๋œ React ๊ณ„์ธต ๊ตฌ์กฐ ์™ธ๋ถ€์—์„œ React ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ๋…ธ์ถœํ•˜๊ณ  ์ง€์›ํ•˜๋Š” ๊ฒƒ์ด ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ƒํƒœ ๋น„์ €์žฅ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ๋น„๋™๊ธฐ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ์ƒํƒœ ์ €์žฅ์ด๋ฏ€๋กœ ์ผ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ์— ๋ณด๋ฅ˜/์™„๋ฃŒ/์‹คํŒจ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

@elierotenberg ์™€ @andrewimm ์€ ์ผ๋ฅ˜ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ํ›Œ๋ฅญํ•œ ์ง€์ ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. @sebmarkbage ์ตœ์†Œํ•œ์˜ interop ์ง€์ ์— ๋Œ€ํ•œ ๋ณธ๋Šฅ์ด onError ๋ฐ onCompleted ๊ฐ’์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋˜‘๊ฐ™์ด ๊ฐ„๋‹จํ•œ ์ด์•ผ๊ธฐ๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. this.observed ์ค‘ ํ•˜๋‚˜์˜ ๋งˆ์ง€๋ง‰ ๊ฐ’์„ ๋ณด์œ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค next/error/completed like { next: "foo" } . ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ณ„์•ฝ์„ ์ผ๊ธ‰ ๊ธฐ๋Šฅ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๊ณ , ๋‚˜๋Š” ์ด ์ œ์•ˆ์ด ์‚ญ๊ฐ๋˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ์•ฝ๊ฐ„ ํšŒ์˜์ ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ์ธํ„ฐ๋„ท์ด๊ณ  ์—ฌ๊ธฐ์—์„œ ์ฒ˜์Œ์œผ๋กœ ์†Œ๋ฆฌ๋ฅผ ๋‚ธ ๊ฒƒ ์ค‘ ํ•˜๋‚˜์ด๊ธฐ ๋•Œ๋ฌธ์— React ๋ฌธ์ œ ํ”ผ๋“œ๋Š” ํ›Œ๋ฅญํ•œ ์ž‘์—…๊ณผ ์•„์ด๋””์–ด์— ๋Œ€ํ•œ ์ตœ๊ณ ์˜ ์ฝ๊ธฐ์ด์ž ๋งŒ๋Šฅ ์†Œ์Šค์ž…๋‹ˆ๋‹ค. :+1:

๋‚˜์—๊ฒŒ ๋ฐ”์ธ๋”ฉ๊ณผ ๊ฐ™์€ ๋ƒ„์ƒˆ๊ฐ€ ๋‚œ๋‹ค.

์ด๊ฒƒ์ด ํ˜„์žฌ ์ œ์•ˆ/๊ตฌํ˜„๊ณผ ์–ด๋–ค ๊ด€๋ จ์ด ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๊ตฌ์„ฑ ๋ฐ ๊ณ ์ฐจ ์กฐ์ž‘์„ ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ์ด ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ ์ข…์†์„ฑ ์ถ”์ ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋ฐ˜์‘์„ฑ ๋ฐ์ดํ„ฐ ์†Œ์Šค(ํ”Œ๋Ÿญ์Šค, ํ”Œ๋Ÿญ์Šค ์œ ์„  ๋˜๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ์ด์™ธ์˜ ๋ชจ๋“  ๊ฒƒ).

react-nexus@^3.4.0 ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ์˜ˆ๋ฅผ ์‚ดํŽด๋ณด์„ธ์š”.

// the result from this query...
@component({
  users: ['remote://users', {}]
})
// is injected here...
@component(({ users }) =>
  users.mapEntries(([userId, user]) =>
    [`user:${userId}`, [`remote://users/${userId}/profile`, {}]]
  ).toObject()
))
class Users extends React.Component {
  // ... this component will receive all the users,
  // and their updates.
}

๋Œ€์ฒด๋กœ ์ปดํฌ๋„ŒํŠธ API์— ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์ด ์žˆ์–ด์•ผ ํ•˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๊ณ ์ฐจ์› ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ฉ”์„œ๋“œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์˜ค์—ผ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ํ‘œํ˜„ํ•˜๋Š” ์•„์ฃผ ์ข‹์€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ @sebmarkbage ๊ฐ€ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ๋Œ€์‹  props ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ ์˜ค์—ผ๋  ์œ„ํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์†Œํ’ˆ ๋ณ€ํ™˜๊ธฐ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ( react-transform-props )๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์†Œํ’ˆ์„ ๋‚ด๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ์ •๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ์ผ๋ฐ˜์ ์ด๊ณ  ์ด๋ฆ„ ์ถฉ๋Œ์˜ ์œ„ํ—˜์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
๊ธฐํ˜ธ๊ฐ€ ์†์„ฑ ํ‚ค์ž„์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? propTypes ๊ฒ€์‚ฌ๊ฐ€ Symbol -keyed ์†Œํ’ˆ์„ ์ง€์›ํ•ฉ๋‹ˆ๊นŒ? JSX๋Š” ๊ณ„์‚ฐ๋œ ์ธ๋ผ์ธ ์†Œํ’ˆ ํ‚ค๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๊นŒ(ํ•ญ์ƒ ๊ณ„์‚ฐ๋œ ์†์„ฑ + ๊ฐœ์ฒด ํ™•์‚ฐ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ)?

์ด๊ฒƒ์ด ์ฃผ์ œ์—์„œ ์กฐ๊ธˆ ๋ฒ—์–ด๋‚˜๋ฉด ์ฃ„์†กํ•˜์ง€๋งŒ ๊ตฌ์„ฑ ์š”์†Œ ์ˆ˜์ค€์—์„œ ๋ฐ์ดํ„ฐ deps๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ์ ์ ˆํ•œ ์ถ”์ƒํ™”/API๋ฅผ ์—ฌ์ „ํžˆ ์ฐพ์•„์•ผ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋‚ด 2์„ผํŠธ

๋‚˜๋Š” ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€ํ•˜๋Š” ๊ฐ€์น˜์— ๋Œ€ํ•œ 'children as a function' ํŒจํ„ด์œผ๋กœ ๋งŽ์€ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. @elierotenberg๊ฐ€ ์ฒ˜์Œ์œผ๋กœ ์ƒ๊ฐํ•ด

<Springs to={{x: 20, y: 30}} tension={30}>
  {val => <div style={{left: val.x, top: val.y}}>moving pictures</div>}
</Springs>

์†Œํ’ˆ ์ถฉ๋Œ์ด ์—†๊ณ  ์†Œ์œ ์ž ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์—ฌ๋Ÿฌ ์Šคํ”„๋ง์„ ์ค‘์ฒฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ฐ˜์‘์€ ๋ชจ๋“  ํ•˜๋“œ ๋น„ํŠธ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ '๊ด€์ธก ๊ฐ€๋Šฅ ํ•ญ๋ชฉ'(ํ•˜!)์€ onError , onComplete ๋ฐ ๊ธฐํƒ€ ์†Œํ’ˆ(graphql ์ฟผ๋ฆฌ?)๋„ ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

'ํ”Œ๋Ÿญ์Šค'์— ๋Œ€ํ•œ ๋Œ€๋žต์ ์ธ ์Šค์ผ€์น˜ ์‹œ๋„

<Store 
  initial={0}
  reduce={(state, action) => action.type === 'click'? state+1 : state} 
  action={{/* assume this comes as a prop from a 'Dispatcher' up somewhere */}}> 
    {state => <div onClick={() => dispatch({type: 'click'})}> clicked {state} times</div>}
</Store>

์šฐ๋ฆฌ๋Š” Asana์—์„œ _render callbacks_์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ์ด ํŒจํ„ด์„ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ๊ถ๊ทน์ ์œผ๋กœ ์ด ํŒจํ„ด์—์„œ ๋ฉ€์–ด์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์žฅ์ 

  • ์†Œํ’ˆ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • ์‰ฌ์šด์˜ ์ €์ž๋กœ ๋ชจ๋‘๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” StoreComponent ์™€์˜ ์‚ฌ์šฉ์ž StoreComponent

๋‹จ์ 

  • shouldComponentUpdate ๋Š” ๋ Œ๋” ์ฝœ๋ฐฑ์ด ์ƒํƒœ ์ด์ƒ์œผ๋กœ ๋‹ซํž ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. A -> Store -> B ์˜ ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ์ƒํ•ด๋ณด์‹ญ์‹œ์˜ค. A ์—๋Š” B ๋Œ€ํ•œ props๋กœ ๋ Œ๋”๋ง ์ฝœ๋ฐฑ ์ค‘์— ์•ก์„ธ์Šค๋˜๋Š” ์ƒํƒœ์˜ ์นด์šดํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์นด์šดํ„ฐ๋กœ ์ธํ•ด A ์—…๋ฐ์ดํŠธ๋˜๊ณ  Store ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์œผ๋ฉด B ์—๋Š” ์˜ค๋ž˜๋œ ๋ฒ„์ „์˜ ์นด์šดํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์šฐ๋ฆฌ๋Š” ํ•ญ์ƒ ์Šคํ† ์–ด๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์–ด๋ ค์›Œ์กŒ์Šต๋‹ˆ๋‹ค. ํ•„์—ฐ์ ์œผ๋กœ ๊ฐœ๋ฐœ์ž๋Š” ๋ Œ๋”๋งํ•  ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ๋ Œ๋”๋ง ์ฝœ๋ฐฑ์— ๋ณต์žกํ•œ ๋…ผ๋ฆฌ๋ฅผ ๋„ฃ๊ณ  ๋…ผ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฐฉ๋ฒ•์€ ์ „์ฒด ํŠธ๋ฆฌ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  ์ €์žฅ์†Œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ _ํ†ตํ•ด_ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ์–•์€ ๋ Œ๋”๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ StoreComponent ๊ฐ€ ReactElement ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ธ๋กœ ์ด๋™ํ•˜๊ณ  ์ €์žฅ์†Œ๊ฐ€ ์ƒˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด ์ €์žฅ์†Œ๊ฐ€ ํŠน์ • ์†Œํ’ˆ์„ ์žฌ์ •์˜ํ•˜๋Š” ReactElement๋ฅผ ๋ณต์ œํ•ฉ๋‹ˆ๋‹ค. Component ์ƒ์„ฑ์ž์™€ ์†Œํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์ด ํŒจํ„ด์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. TypeScript์—์„œ ๋ชจ๋ธ๋งํ•˜๊ธฐ๊ฐ€ ๊ฐ€์žฅ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ›Œ๋ฅญํ•œ ์ , '์˜†์œผ๋กœ' ์š”๊ตฌ ์‚ฌํ•ญ์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š๊ณ ๋Š” ๋ฐฉ๋ฒ•์„ ์ƒ๊ฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@threepointone https://github.com/reactjs/react-future/pull/28 ์— ์ „์›์„ ๊ณต๊ธ‰ํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌํ˜„ ์ œ์•ˆ ์ค‘ ํ•˜๋‚˜์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์˜ˆ์—์„œ @pspeter3 , Store๋ฅผ ํ•ญ์ƒ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ๋งŒ๋“œ๋Š” ๋ฐ ์‹ฌ๊ฐํ•œ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๊นŒ / shouldComponentUpdate: ()=> true ? ์–ด์จŒ๋“  '์ž์‹'์ด ์ฒด์ธ์— Store ์—†์ด ๋ Œ๋”๋ง๋˜์—ˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ๋‚ด ์ค˜์„œ ๊ณ ๋งˆ์›Œ!

@threepointone ๊ทธ๊ฒƒ์ด ๋ฐ”๋กœ ์šฐ๋ฆฌ๊ฐ€ ๊ฒฝ๊ณ„๋ฅผ ์œ„ํ•ด ํ•œ ์ผ์ž…๋‹ˆ๋‹ค. ๊ทธ ์˜ํ–ฅ์ด ์ •ํ™•ํžˆ ๋ฌด์—‡์ธ์ง€๋Š” ๋ถˆ๋ถ„๋ช…ํ–ˆ์ง€๋งŒ, ํŒ€์›๋“ค์˜ ํผํฌ๋จผ์Šค ๊ฑฑ์ •์ด ์žˆ์—ˆ๋‹ค. ํ…Œ์ŠคํŠธ์˜ ์–ด๋ ค์›€๊ณผ ๊ฒฐํ•ฉ๋œ ๊ฑฑ์ •์€ React.cloneElement(this.props.child, {data: this.state.data}) ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

@pspeter3 ํ…Œ์ŠคํŠธ ๊ฐ๋„๊ฐ€ ํ™•์‹คํžˆ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ShallowRenderer๊ฐ€ '๋ Œ๋” ์ฝœ๋ฐฑ'์„ ์ธ์‹ํ–ˆ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ๋„์›€์ด ๋ ๊นŒ์š”?

Ps- '์ฝœ๋ฐฑ ๋ Œ๋”๋ง':thumbs_up:

@sebmarkbage es-observable ์— ๋Œ€ํ•œ ํ˜„์žฌ ๋…ผ์˜๋Š” subscribe ๊ฐ€ ๋น„๋™๊ธฐ๋ฅผ ๋ณด์žฅํ•˜๊ณ  [Symbol.observer] ๋ฉ”์„œ๋“œ๊ฐ€ ๋™๊ธฐ์‹์œผ๋กœ ๋ฐ”๋กœ ๊ฐ€๊ธฐ ๋ฐ ๊ตฌ๋…์— ์ œ๊ณต๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋…ผ์˜ ์ค‘์ด๋‹ค.

๋™๊ธฐ ๊ตฌ๋…์— ์ฐฌ์„ฑํ•˜์—ฌ ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€์™€ ํ•จ๊ป˜ ์ด ํ‹ฐ์ผ“ ์— ์ฐธ์—ฌํ–ˆ์ง€๋งŒ ๊ฑฐ๊ธฐ์— ์ถ”๊ฐ€ํ•  ๋‚ด์šฉ์ด ์žˆ๋Š”์ง€ ๋ชฐ๋ž์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์—ฌ๊ธฐ์— ์žˆ๋Š” ์•„์ด๋””์–ด๊ฐ€ ์™ธ๋ถ€ ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋งค์šฐ ๊นจ๋—ํ•œ ํŒจํ„ด์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์กฐ๊ธˆ ๊ฐ€์ง€๊ณ  ๋†€๊ณ  ๋‚œ ํ›„์—๋Š” HOC ์ ‘๊ทผ ๋ฐฉ์‹์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.

- ๋˜ํ•œ @gaearon ๋‚œ ๋‹น์‹  HOC ์ •์  ์‚ฌ์šฉํ•˜์—ฌ, ์œ„์˜ ๋น„ํŠธ ๋‹จ์ˆœํ™” ComposedComponent.observe ํ•˜๊ณ  ์‚ฌ์šฉํ•˜์—ฌ this.state ๋ณด๋‹ค๋Š” this.state.data - https://gist.github.com/tgriesser/d5d80ade6f895c28e659

์ œ์•ˆ๋œ es7 ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ์ •๋ง ๋ฉ‹์ง€๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

<strong i="20">@observing</strong>
class Foo extends Component {
  static observe(props, context) {
    return {
      myContent: xhr(props.url)
    };
  }
  render() {
    var myContent = this.props.data.myContent;
    return <div>{myContent}</div>;
  }
}

ํด๋ž˜์Šค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” data ์— ๋Œ€ํ•œ getter๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์›๋ž˜ ์ œ์•ˆ๋œ API(๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ตฌ๋…์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ๋บ€ ๊ฐ’์œผ๋กœ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋ณ€ ์†Œ์Œ์ด ํ›จ์”ฌ ์ ์Šต๋‹ˆ๋‹ค. ๊ตฌ๋…/๊ตฌ๋… ์ทจ์†Œ).

๋™๊ธฐ ๊ตฌ๋…์ด ์—†์œผ๋ฉด ํฐ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

"์ƒํƒœ ๋ฌธ์ œ"์™€ "์ธก๋ฉด ๋ฐ์ดํ„ฐ ๋กœ๋“œ"(ํŒŒ์ƒ ๋ฐ์ดํ„ฐ)๋ฅผ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. stateless "React-way"๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์–ด๋–ค ์‹œ์ ์—์„œ๋“  ์ƒํƒœ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•˜๊ณ  UI = React(state) ํŒจํ„ด์— ๋งž์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์™„์ „ํžˆ ๋ฐฉํƒ„์œผ๋กœ ๋งŒ๋“ค๊ณ , ๋” ๋งŽ์€ ์˜ˆ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์ข‹์€ ํ”„๋ ˆ์  ํ…Œ์ด์…˜์„ ํ•  ์ˆ˜ ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. https://github.com/AlexeyFrolov/slt . ๋ฐ˜๋ฉด์— ์ž˜ ํ…Œ์ŠคํŠธ๋˜์—ˆ์œผ๋ฉฐ ํ”„๋กœ๋•์…˜ ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์˜๋ฆฌํ•œ ๋งˆ์Œ์˜ ๊ธฐ์—ฌ๋ฅผ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„,

์šฐ๋ฆฌ ํšŒ์‚ฌ์—์„œ ๋ฐ˜๋…„ ์ „์— ์ด ์ œ์•ˆ์œผ๋กœ ํ•ด๊ฒฐ๋œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์— ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์šฐ์—ฐํžˆ ๋ฐœ๊ฒฌํ•˜์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์ด ์žฌ๋ฏธ์žˆ์Šต๋‹ˆ๋‹ค.
์šฐ๋ฆฌ๋Š” ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์— React๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค(๋งค์šฐ ์ˆœํ™˜์ ์ธ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ Microsoft Visio์˜ ๋ณต์žก์„ฑ์„ ๊ฐ€์ง„ ํŽธ์ง‘๊ธฐ๋ฅผ ์ƒ๊ฐํ•ด ๋ณด์„ธ์š”). ๋ฐ”๋‹๋ผ ๋ฐ˜์‘ ์ˆ˜๊ฐ€ ์šฐ๋ฆฌ์˜ ์„ฑ๋Šฅ ์š”๊ตฌ ์‚ฌํ•ญ์„ ๋”ฐ๋ผ๊ฐ€์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ”Œ๋Ÿญ์Šค๋Š” ๋งŽ์€ ์–‘์˜ ์ƒ์šฉ๊ตฌ์™€ ๋ชจ๋“  ๊ตฌ๋…์˜ ์˜ค๋ฅ˜ ๊ฒฝํ–ฅ์œผ๋กœ ์ธํ•ด ์šฐ๋ฆฌ์—๊ฒŒ ์•ฝ๊ฐ„์˜ ์‹คํŒจ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋„ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋ƒˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์„ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋…น์•„์›ƒ ์˜ต์ €๋ฒ„๋ธ”(ํŠนํžˆ: ์ž๋™ ๊ตฌ๋…)์˜ ์›์น™์— ๋”ฐ๋ผ ์ž์ฒด ์˜ต์ €๋ฒ„๋ธ” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ์‹ค์ œ๋กœ React ๊ตฌ์„ฑ ์š”์†Œ์˜ ํ˜„์žฌ ์ˆ˜๋ช… ์ฃผ๊ธฐ์— ๋งค์šฐ ๊น”๋”ํ•˜๊ฒŒ ๋“ค์–ด๋งž์œผ๋ฉฐ ์ด์ƒํ•œ ํ•ดํ‚น์ด๋‚˜ ์ž์‹ ๋ Œ๋”๋ง ์ฝœ๋ฐฑ๋„ ํ•„์š”ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค(์•„๋ž˜์—์„œ ์‚ฌ์šฉ๋˜๋Š” ObserverMixin์€ ์•ฝ 10loc์ž„).
์ด๊ฒƒ์€ ์šฐ๋ฆฌ์˜ DX๋ฅผ ๋งŽ์ด ํ–ฅ์ƒ์‹œ์ผฐ๊ณ  ์šฐ๋ฆฌ ํŒ€์„ ์œ„ํ•ด ์•„์ฃผ ์ž˜ ์ž‘๋™ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์„ ์˜คํ”ˆ ์†Œ์Šค ๋กœ ๊ฒŒ์‹œํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋™์•ˆ ์ด๊ฒƒ์€ ์ „ํˆฌ์—์„œ ์ž…์ฆ๋˜์—ˆ๊ณ (์˜ˆ๋ฅผ ๋“ค์–ด ES7 ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์–ด๋ ˆ์ด ํด๋ฆฌํ•„์„ ์ œ๊ณตํ•จ) ๊ณ ๋„๋กœ ์ตœ์ ํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋‹ค์Œ์€ ์งง์€ ํƒ€์ด๋จธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค( JSFiddle ๋กœ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ). IMHO DX๋Š” ์ด๋ณด๋‹ค ๋” ์ข‹์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค... :์•ˆ๋„:

var store = {};
// add observable properties to the store
mobservable.props(store, {
    timer: 0
});

// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
    store.timer = 0;
}

setInterval(function() {
    store.timer += 1;
}, 1000);

var TimerView = React.createClass({
    // This component is actually an observer of all store properties that are accessed during the last rendering
    // so there is no need to declare any data use, nor is there (seemingly) any state in this component
    // the combination of mobservable.props and ObserverMixin does all the magic for us.
    // UI updates are nowhere forced, but all views (un)subscribe to their data automatically
    mixins: [mobservable.ObserverMixin],

    render: function() {
        return (<span>Seconds passed: {this.props.store.timer}</span>);
    }
});

var TimerApp = React.createClass({
    render: function() {
        var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
        return (<div>
            <div>Started rendering at: {now.toString()}</div>
            <TimerView {...this.props} />
            <br/><button onClick={resetTimer}>Reset timer</button>
        </div>);
    }
});

// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);

์ด ์ ‘๊ทผ ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ๋ธ”๋กœ๊ทธ ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. BTW, ES6 ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฐ/๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ๊ฐ€ lib ์— ์ถ”๊ฐ€๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์Šฌํ”„๊ฒŒ๋„ ๋‚˜๋Š” react-europe ์ „์— ์ด ์Šค๋ ˆ๋“œ๋ฅผ ๋ณด์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜๊ฐ์„ ์ฃผ๋Š” ์ด์•ผ๊ธฐ๋ฅผ ํ•ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! :+1: ์ €๋Š” ํŠนํžˆ GraphQL์˜ ์ถ”์ƒํ™”์™€ Redux ์ด๋ฉด์˜ ์‚ฌ๊ณ  ์ž‘์—…์„ ์ข‹์•„ํ–ˆ์Šต๋‹ˆ๋‹ค. :)

@mweststrate ์ €๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ๊ถ๊ทน์ ์œผ๋กœ "Observable"๊ณผ "Immutable data" ์†”๋ฃจ์…˜ ์ค‘์—์„œ ์„ ํƒํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ ์†”๋ฃจ์…˜(https://github.com/AlexeyFrolov/slt/issues/4)์—์„œ ๋‘ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ์žฅ์ ์„ ๋ชจ๋‘ ๊ฐ–๊ธฐ ์œ„ํ•ด ์–ด๋–ค ์‹์œผ๋กœ๋“  ํ˜ผํ•ฉํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์†”๋ฃจ์…˜์—์„œ๋Š” ์–ด๋–ค ์‹œ์ ์—์„œ๋“  ์ƒํƒœ์˜ ์ผ๊ด€์„ฑ์— ์ค‘์ ์„ ๋‘” "๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ" ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค. Observable๊ณผ Generator๋„ ์ง€์›ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ "ํŒŒ์ƒ" ๋˜๋Š” "๋ณด์ถฉ" ๋ฐ์ดํ„ฐ(์˜ˆ: ํŽ˜์ด์ง€ ๋ณธ๋ฌธ, ์ž์‚ฐ, ์ถ”์ฒœ, ๋Œ“๊ธ€, ์ข‹์•„์š”)๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์•ฑ ์ƒํƒœ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค.

https://github.com/AlexeyFrolov/slt#rules -์˜ˆ์ œ

์œ„์น˜ ํ—ค๋”๋กœ API ๋ฆฌ๋””๋ ‰์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ฃผ๋Š” ๋ณต์žกํ•œ ์‹ค์ œ(๋‚ด ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ) ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

import r from "superagent-bluebird-promise";
import router from "./router";

export default {
    "request": function (req)  {
        let route = router.match(req.url);
        let session = req.session;
        route.url = req.url;
        return this
            .set("route", route)
            .set("session", req.session);
    },
    "route": {
        deps: ["request"],
        set: function (route, request) {
            let {name, params: { id }} = route;
            if (name === "login") {
                return this;
            }
            let url = router.url({name, params: {id}});
            let method = request.method ? request.method.toLowerCase() : "get";
            let req = r[method]("http://example.com/api/" + url);
            if (~["post", "put"].indexOf(method)) {
                req.send(request.body);
            }
            return req.then((resp) => {
                let ctx = this.ctx;
                let path = url.substr(1).replace("/", ".");
                if (!resp.body) {
                    let location = resp.headers.location;
                    if (location) {
                        ctx.set("request", {
                            method: "GET",
                            url: location.replace('/api', '')
                        });
                    }
                } else {
                    ctx.set(path, resp.body);
                }
                return ctx.commit();
            });
        }
    }
}

๋‚˜๋จธ์ง€์—์„œ๋Š” React์— ์ง์ ‘ ๋ฐ”์ธ๋”ฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๊ท€ํ•˜์™€ ๋™์ผํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค(์ œ ๊ฒฝ์šฐ์—๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Œ). ๊ณต๋™์˜ ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์–ด๋–ป๊ฒŒ๋“  ํž˜์„ ๋ชจ์•„์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๋ คํ•˜๊ธฐ ์–ด๋ ค์šด ๋งŽ์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜์œ ์ƒ๊ฐ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์œ„์˜ ์˜๊ฒฌ์—์„œ ์ตœ์ข… ์‚ฌ์šฉ์ž๊ฐ€ "๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”" ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ํ•  ๋•Œ๋งˆ๋‹ค ์ƒ๊ฐํ•ด์•ผ ํ•˜๋Š” ๊ฐ€๋Šฅํ•œ ๋ฒ”์œ„๋ฅผ ๋‚˜์—ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์„œ๋ฒ„ ์ธก ๋ Œ๋”๋ง
  • .state ๋ฐ .data ์ดˆ๊ธฐํ™”
  • .context , .props , .state ๋ฐ .observe ์–ฝํž˜
  • ๋น„๋™๊ธฐ ๋ Œ๋”๋ง

๋‚˜๋Š” ์ด ์ œ์•ˆ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๊ณ  ๋ถˆ์•ˆ์ •ํ•˜๋ฉฐ ๋””๋ฒ„๊ทธํ•˜๊ธฐ ์–ด๋ ค์šด ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ด์–ด์งˆ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@glenjamin connect ๋ฐ disconnect ์ˆ˜๋ช… ์ฃผ๊ธฐ ํ›„ํฌ๊ฐ€ ์ œ์•ˆํ•œ ๋‚ด์šฉ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์ œ์—์„œ ๋ฒ—์–ด๋‚œ ๊ฒƒ์ด๋ผ๋ฉด ์‚ฌ๊ณผ๋“œ๋ฆฝ๋‹ˆ๋‹ค(์ •ํ™•ํžˆ ๋งํ•  ์ˆ˜ ์—†์Œ). ๊ทธ๋Ÿฌ๋‚˜ ๋‹ค์Œ์€ ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•ด API๋ฅผ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ด ๋ฌธ์ œ์— ์ ‘๊ทผํ•˜๋Š” ํฅ๋ฏธ๋กœ์šด ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•œ ์˜ˆ์ž…๋‹ˆ๋‹ค. https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit -todomvc/loggit/renderers/precompute_react_renderer.js#L72

์ด ๋ฐฉ๋ฒ•์€ ํŠธ๋ฆฌ๋ฅผ ๊ฑท๋Š” ์ €์˜ ํ•ด์ปค์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ œ ์งˆ๋ฌธ์€ ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๊ณต๊ฐœ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด "๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ"์—์„œ ํ˜์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜ํ–ฅ์‹์œผ๋กœ ์ž‘์—…": https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit-todomvc/loggit/react_interpreter.js#L8

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์— ๊ตฌ์„ฑ ์š”์†Œ ํŠธ๋ฆฌ์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•ด API๋ฅผ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ด ๋ฌธ์ œ์— ์ ‘๊ทผํ•˜๋Š” ํฅ๋ฏธ๋กœ์šด ๋ฐฉ๋ฒ•์ด ๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•œ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

@swannodette๊ฐ€ ReactConf์—์„œ ์ด์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ–ˆ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค. Om Next๊ฐ€ ํ•˜๋Š” ๊ฑด์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

๋„ค, ์ฒซ ReactConf์—์„œ ๊ฐ™์€ ๊ฒƒ์„ ์ œ์•ˆํ–ˆ์Šต๋‹ˆ๋‹ค. ์ตœ์‹  Om.next๋ฅผ ๋ณด๊ฑฐ๋‚˜ EuroClojure ์ด์•ผ๊ธฐ๋ฅผ ๋“ค์–ด๋ณธ ์ ์ด ์—†์ง€๋งŒ ์ด์ „์—๋Š” Om์ด ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌ์ถ•ํ•ด์•ผ ํ–ˆ๋˜ ์ž์ฒด ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ ํŠธ๋ฆฌ๋ฅผ ๊ฑท๋Š” ์ €์˜ ํ•ด์ปค์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ œ ์งˆ๋ฌธ์€ ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ๊ณต๊ฐœ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด "๊ฐ€๋กœ ๋ฐ์ดํ„ฐ ๋กœ๋“œ"์—์„œ ํ˜์‹ ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜ํ–ฅ์‹์œผ๋กœ ์ž‘์—…"

์ด๊ฒƒ์€ ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์–•์€ ๋ Œ๋”๋ง๊ณผ ๋Œ€๋žต ๋™์ผํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ReactEurope ์—์„œ @sebmarkbage์— ๋Œ€ํ•œ DOM ๋ฐ String ๋ Œ๋”๋ง์˜ ํ”ผ์–ด๋กœ์„œ ์ด๊ฒƒ์„ ์ผ๊ธ‰ API๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค. 0.14 ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ด๋ฅผ ์œ„ํ•œ ๊ธธ์„ ๋ฉ‹์ง€๊ฒŒ ํฌ์žฅํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐ ๋ฐ˜์‘์€ ์•ฝ๊ฐ„ ๋‚ฎ์€ ์ˆ˜์ค€์ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์›น ํ•ญ๋ชฉ๊ณผ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์—์„œ ์‹คํ—˜ํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฌ์šธ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋ฉด ํŠธ๋ฆฌ๋ฅผ ํƒ์ƒ‰ํ•˜๊ณ  ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•„ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ๋™์˜ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ๊ฐ€์ƒ DOM ํŠธ๋ฆฌ์— ์•ก์„ธ์Šคํ•˜๋Š” ๊ฒƒ์€ ์™„์ „ํžˆ ๋ณ„๊ฐœ์˜ ๋ฌธ์ œ๋กœ ์ทจ๊ธ‰๋˜๋”๋ผ๋„ ์•ก์„ธ์Šคํ•˜๊ณ  ์‹ถ์€ ๋†€๋ž๋„๋ก ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.
React๊ฐ€ 0.14 ์ดํ›„๋กœ ๋‚˜์•„๊ฐ€๋Š” ๊ธธ์„ ๊ณ ๋ คํ•  ๋•Œ ๊ทธ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์˜ต์ €๋ฒ„๋ธ”์˜ ๋ณต์žก์„ฑ์„ ๊ฐ์•ˆํ•  ๋•Œ, ์ด ์Šค๋ ˆ๋“œ์˜ ํ›Œ๋ฅญํ•œ ์—ฌ๋Ÿฌ๋ถ„์ด Meteor์˜ ๋ฐ˜์‘ ๋ฐ์ดํ„ฐ ๊ตฌํ˜„์„ ์‚ดํŽด๋ณด๊ธฐ๋ฅผ ๊ฒธ์†ํ•˜๊ฒŒ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค: https://github.com/meteor/meteor/wiki/Tracker-Manual. React์™€ ์กฐ์ •ํ•˜๋ ค๋ฉด componentWillMount ๋ฉ”์„œ๋“œ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฌธ์ž ๊ทธ๋Œ€๋กœ 3-5์ค„์˜ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  componentWillMount() {
    if (typeof this.getState === 'function') {
      Tracker.autorun(() => {
        // Assuming this.getState() calls some functions that return
        // reactive data sources
        this.setState(this.getState());
      });
    }
  }

Tracker(๋ณ„๋„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์‰ฝ๊ฒŒ ์ถ”์ถœ๋จ)๊ฐ€ React์—์„œ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ง€์›์˜ ํ•„์š”์„ฑ์„ ์ œ๊ฑฐํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ํ™•์‹คํžˆ ๊ทธ๋ ‡๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

MOBservable์€ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์˜†์œผ๋กœ ์ƒˆ๋กœ ๊ณ ์น˜๋Š” ๋งค์šฐ ์œ ์‚ฌํ•œ ํŒจํ„ด์„ ๋”ฐ๋ฅด๋ฏ€๋กœ ํ˜„์žฌ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๋ฉ”์„œ๋“œ + ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ํƒ€์‚ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ํŒจํ„ด์„ ํ‘œํ˜„ํ•˜๊ณ  ํƒ€์‚ฌ ๋ฐ์ดํ„ฐ ์†Œ์Šค ๊ฐœ๋…์„ ํ‘œํ˜„ํ•˜๊ธฐ์— ์ถฉ๋ถ„ํ•œ ์œ ์—ฐ์„ฑ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ์ผ๋งŒ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

    componentWillMount: function() {
        var baseRender = this.render;
        this.render = function() {
            if (this._watchDisposer)
                this._watchDisposer();
            var[rendering, disposer] = mobservableStatic.watch(() => baseRender.call(this), () => {
                    this.forceUpdate();
            });
            this._watchDisposer = disposer;
            return rendering;
        }
    },

@Mitranim ๋™์˜ํ•ฉ๋‹ˆ๋‹ค. ์ •๋ง ์ž˜ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐพ์•„์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๊ทธ๊ฒƒ์€ ํšจ๊ณผ์ ์œผ๋กœ https://github.com/facebook/react/pull/3920 ์ด ์ œ์•ˆํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์–ด๋–ค ์ œ์•ˆ์ด ๋” ๋‚˜์€์ง€ ๊ฒฐ์ •ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์‚ฌ์šฉํ•ด ๋ณธ ๊ฒฐ๊ณผ ๋ฐ˜์‘ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ(๋งํฌํ•œ ๋Œ€๋กœ, https://github.com/meteor/meteor/wiki/Tracker-Manual)์ด ๊ฐ€์•ผ ํ•  ๊ธธ์ด๋ผ๊ณ  ๋Œ€๋ถ€๋ถ„ ํ™•์‹ ํ•˜์ง€๋งŒ ํ•ฉ์˜์— ๋„๋‹ฌํ•˜์ง€ ๋ชปํ–ˆ๊ณ  ์šฐ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ๋ฌด์—‡์ด ๊ฐ€์žฅ ํ•ฉ๋ฆฌ์ ์ธ์ง€ ์•Œ์•„๋‚ด๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ํ”ผ๋“œ๋ฐฑ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

@Mitranim @jimfb ์ €๋Š” ๋ช‡ ๋…„ ๋™์•ˆ Meteor์˜ ์—ด๋ ฌํ•œ ํŒฌ์ด์—ˆ์Šต๋‹ˆ๋‹ค. Tracker๋Š” ์ •๋ง ๋ฉ‹์ง€๊ณ  ๋งค์šฐ ๋งค๋ ฅ์ ์ž…๋‹ˆ๋‹ค. ๋งค์šฐ ๊ฐ„๋‹จํ•œ ๋ฒ„์ „์˜ ํŠธ๋ž˜์ปค๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์„ ๋ณด์—ฌ์ฃผ๋Š” ํ”„๋ ˆ์  ํ…Œ์ด์…˜์„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/ccorcos/meteor-track/blob/master/client/main.js

๊ทธ๋ฆฌ๊ณ  Tracker๋กœ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ฆผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/ccorcos/meteor-tracker-streams

ํ•˜์ง€๋งŒ Blaze ๋Œ€์‹  React๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์™„์ „ํžˆ ์ „ํ™˜ํ•˜๋ฉด์„œ Tracker๊ฐ€ ๋„ˆ๋ฌด ๋ณต์žกํ•˜๊ณ  ๋•Œ๋กœ๋Š” ๊ฐ„๋‹จํ•œ publish/subsrcribe/onChange ๋ฉ”์„œ๋“œ๊ฐ€ 100๋ฐฐ ๋” ์‰ฝ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ, ๋ชจ๋“  ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒฌ์„ ์œ„ํ•ด Tracker์—๋Š” 99%์˜ ๊ฒฝ์šฐ์— ์ดํ•ด๊ฐ€ ๊ฐ€๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ถ€์ž‘์šฉ์ด ์žˆ์ง€๋งŒ ๋•Œ๋กœ๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ React ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ๋ณด์ด๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

componentWillMount: ->
  <strong i="15">@c</strong> = Tracker.autorun =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})
componentWillUnmount: ->
  @c.stop()

๋ถ€์ž‘์šฉ์— ๋Œ€ํ•œ ์ œ ์š”์ ์€ c.stop() ๊ตฌ๋…๊ณผ ๋‚ด๋ถ€ ์ž๋™ ์‹คํ–‰๋„ ์ค‘์ง€๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@ccorcos ์˜ˆ, https://github.com/facebook/react/pull/3920์—์„œ ๋ชจ๋“  ๋ถ€์ž‘์šฉ์€ ์™„์ „ํžˆ React ๋‚ด๋ถ€์— ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค, React ์ฝ”์–ด๋Š” ๋Œ์—ฐ๋ณ€์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ์™„์ „ํžˆ ๋ถˆ๋ณ€/๊ธฐ๋Šฅ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๊ทธ๊ฒƒ๋“ค์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ถ€์ž‘์šฉ์€ ์™ธ๋ถ€์—์„œ ๋ณผ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค... ๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ๋ƒฅ onChange ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ๋“ค์ด ๊ฐ€์žฅ ํ˜ธํ™˜๋˜๋Š” ๊ฒƒ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. Meteor(Tracker), RxJS, Highland.js ๋“ฑ์„ ์‚ฌ์šฉํ•˜๋“  ์ƒ๊ด€์—†์ด ์ด๋ฒคํŠธ ์ฝœ๋ฐฑ์œผ๋กœ ํ•ญ์ƒ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. React ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

Redux๊ฐ€ ๋งˆ์Œ์— ๋“œ๋Š” ์ ์€ ์ด ๋…ผ๋ฆฌ๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ์™ธํ•˜๊ณ  React ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ˆœ์ˆ˜ํ•œ ๊ธฐ๋Šฅ์œผ๋กœ ์œ ์ง€ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@ccorcos ์ฃผ์š” ๋ฌธ์ œ๋Š” ๊ตฌ์„ฑ ์š”์†Œ ์ธ์Šคํ„ด์Šค๊ฐ€ ํŒŒ๊ดด๋  ๋•Œ ๋ชจ๋“  "๊ตฌ๋…"์„ ์ •๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ์ž‘์„ฑ์ž๋Š” ์ข…์ข… ์ด ์ •๋ฆฌ๋ฅผ ์žŠ์–ด๋ฒ„๋ ค ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์ด ์ž๋™์ด๊ธฐ๋ฅผ ์›ํ•˜๋ฏ€๋กœ ์“ฐ๊ธฐ๊ฐ€ ๋” ์‰ฝ๊ณ (๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์ ์Œ) ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค(์ž๋™ ์ •๋ฆฌ).

@jimfb ๋งž์Šต๋‹ˆ๋‹ค. ์ž๋™ ๊ตฌ๋… ์ทจ์†Œ๋Š” Tracker์˜ ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ ์ •๋ง ๋ฉ‹์ง„ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ๋ฐ˜์‘ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋Œ€ํ•œ ๊ฐ ํ˜ธ์ถœ์€ ๊ตฌ๋…์„ ๋‹ค์‹œ ์„ค์ •ํ•˜๊ณ  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ์„ ์ค‘์ง€ํ•˜๋ฉด ์ •๋ฆฌํ•  ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค!

์˜ˆ, ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ "์ž๋™์œผ๋กœ" ๊ตฌ๋… ์ทจ์†Œ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋งˆ์šดํŠธ ํ•ด์ œ๋  ๋•Œ c.stop() ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ mixin์„ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”์ƒํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

componentWillMount: ->
  <strong i="7">@autorun</strong> =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ๋‹ค๋ฅธ API์™€ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋งˆ์šดํŠธ ํ•ด์ œ ๋“ฑ์˜ ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ๊ตฌ๋…์„ ์ทจ์†Œํ•˜๋Š” ๊ธฐ๋ณธ ์ œ๊ณต ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณด์„ธ์š”. ์ €๋Š” Meteor์˜ ์—ด๋ ฌํ•œ ํŒฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๋‹น์‹ ์—๊ฒŒ ์ด๊ฒƒ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๊ฐ€๋”, ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์ด ๋‚ด์šฉ์„ ์„ค๋ช…ํ•˜๋Š” ๊ฒƒ์ด ์ •๋ง ์–ด๋ ต๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ•œํŽธ, ๊ฐ„๋‹จํ•œ ๋ฆฌ์Šค๋„ˆ/์ด๋ฒคํŠธ ์ด๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ดํ•ดํ•˜๊ณ  ๊ตฌํ˜„ํ•˜๊ธฐ๊ฐ€ ํ›จ์”ฌ ๋” ๊ฐ„๋‹จํ•˜๋ฉฐ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋ฐ˜์‘์„ฑ ์‹œ์Šคํ…œ๊ณผ ๋งค์šฐ ํ˜ธํ™˜๋˜๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค...

@ccorcos ์šฐ๋ฆฌ๋Š” ๋‹ค๋ฅธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์„์ง€๋„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ™•์ธํ•ด๋ณด๋‹ˆ Tracker์—๋Š” ์˜๊ตฌ ๊ตฌ๋…์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์„ค์ •ํ•˜๋Š” ๊ฐ ํ•จ์ˆ˜(์•ก์„ธ์Šคํ•˜์—ฌ)๋Š” ๋ณ€๊ฒฝ๋  ๋•Œ _ํ•œ ๋ฒˆ_ ๋‹ค์‹œ ์‹คํ–‰๋˜๊ณ  ๊ตฌ๋…์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋‹ค์‹œ ์•ก์„ธ์Šคํ•˜๋ฉด ํ•œ ๋ฒˆ ๋” ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด "๊ตฌ๋…"์ด ๋‹ค์‹œ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ๋“ฑ๋“ฑ.

@Mitranim ์€ ์ •ํ™•ํ•˜๊ณ  #3920์˜ ์˜๋ฏธ๋Š” Meteor๋ณด๋‹ค ๋” "์ž๋™"์ด๋ฉฐ(๊ตฌ๋… ์ทจ์†Œ๋Š” ์‹ค์ œ๋กœ ์ž๋™์ž…๋‹ˆ๋‹ค) ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€์—์„œ ๋ง ๊ทธ๋Œ€๋กœ API ํ‘œ๋ฉด์ ์ด 0์ด๊ธฐ ๋•Œ๋ฌธ์— ํ›จ์”ฌ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

@ccorcos @Mitranim ์‚ฌ์šฉํ•  ์ค€๋น„๊ฐ€ ๋œ Tracker/Vue.js ์˜๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฒฝ์šฐ Mobservable ์„ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ _render_ ๋™์•ˆ ์•ก์„ธ์Šค๋˜๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ๋งˆ์šดํŠธ ํ•ด์ œ ์‹œ ๋ชจ๋“  ๊ตฌ๋…์„ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค(๊ทธ๋•Œ๊นŒ์ง€ ๊ตฌ๋…์ด ํ™œ์„ฑ ์ƒํƒœ๋กœ ์œ ์ง€๋จ). ์ง€๊ธˆ๊นŒ์ง€ Mendix์—์„œ ๊ฝค ํฐ ํ”„๋กœ์ ํŠธ์— ์„ฑ๊ณต์ ์œผ๋กœ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž์ฒด ๋ชจ๋ธ ๊ฐœ์ฒด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋Œ€์‹  ๊ธฐ์กด ๊ฐœ์ฒด๋ฅผ ์žฅ์‹ํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ์— ๋ฐฉํ•ด๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ํ™•์ธํ•ด๋ณด๋‹ˆ Tracker์— ์˜๊ตฌ ๊ตฌ๋…์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

@Mitranim ๊ตฌ๋…์€ ์˜๊ตฌ์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ ์ข€ ๋ด.

sub = Meteor.subscribe('chatrooms')
# this subscription lasts until...
sub.stop()

์ด์ œ Tracker์— ๋ช‡ ๊ฐ€์ง€ ํฅ๋ฏธ๋กœ์šด ๊ธฐ๋Šฅ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ ์ข€ ๋ด.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')
# this subscription lasts until...
comp.stop()

๋งˆ์ง€๋ง‰ ์˜ˆ์ œ๋Š” ๊ทธ๋‹ค์ง€ ์œ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ด๋Ÿฐ ์ผ์„ ํ•  ๋•Œ๊นŒ์ง€.

roomId = new ReactiveVar(1)
comp = Tracker.autorun ->
  Meteor.subscribe('messages', roomId.get())
# when I change the roomId, the autorun will re-run
roomId.set(2)
# the subscription to room 1 was stopped and now the subscription to room 2 has started
# the subscription is stopped when I call stop...
comp.stop()

๋ฐ˜์‘ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋Œ€ํ•œ ์ข…์†์„ฑ์„ ์„ค์ •ํ•˜๋Š” ๊ฐ ํ•จ์ˆ˜(์•ก์„ธ์Šคํ•˜์—ฌ)๋Š” ๋ณ€๊ฒฝ๋  ๋•Œ ํ•œ ๋ฒˆ ๋‹ค์‹œ ์‹คํ–‰๋˜๊ณ  ๊ตฌ๋…์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

๊ตฌ๋…์€ ์ž๋™ ์‹คํ–‰์ด ๋‹ค์‹œ ์‹คํ–‰๋˜๊ฑฐ๋‚˜(๋ฐ˜์‘ ์ข…์†์„ฑ์ด ๋ณ€๊ฒฝ๋จ) ํ•ด๋‹น ๊ตฌ๋…์ด ์žˆ๋Š” ๊ณ„์‚ฐ์ด ์ค‘์ง€๋  ๋•Œ๊นŒ์ง€ ์ง€์†๋ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค compute.onInvalidate ํ›„ํฌ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋˜‘๊ฐ™์€ ์ผ์„ ํ•ด๋‚ธ ์•„์ฃผ ๊ธฐ๋ฐœํ•œ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค. Tracker๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์–ด์ฉŒ๋ฉด ๋‹น์‹ ์€ ๊ทธ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์ง€์ €๋ถ„ํ•ด์งˆ ์ˆ˜ ์žˆ๋Š”์ง€๋„ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')

# is the same as

comp = Tracker.autorun (c) ->
  sub = null
  Tracker.nonreactive ->
    # dont let comp.stop() stop the subscription using Tracker.nonreactive
    sub = Meteor.subscribe('chatrooms')
  c.onInvalidate ->
    # stop the subscription when the computation is invalidated (re-run)
    sub.stop()
# invalidate and stop the computation
comp.stop()

@ccorcos ์ด์ œ ํ˜ผ๋ž€์˜ ์›์ธ์ด ๋ณด์ž…๋‹ˆ๋‹ค. ๋‚ด ์‚ฌ์ „์ด์—ˆ๋‹ค. ๊ตฌ๋…์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•  ๋•Œ _Meteor ๊ตฌ๋…_์„ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋“ค์€ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ํ‘ธ์‹œ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒฐ์ •ํ•˜์ง€๋งŒ ์ด ํ† ๋ก ์˜ ์ฃผ์ œ์ธ ๋ณด๊ธฐ ๊ณ„์ธต ์—…๋ฐ์ดํŠธ์™€ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. _subscription_์„ ๋งํ•  ๋•Œ ๋‚˜๋Š” ์ „ํ†ต์ ์ธ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์™€ ๋ฐ˜์‘์„ฑ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ์˜์กดํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์žฌ์‹คํ–‰ํ•˜๋Š” Tracker์˜ ๋Šฅ๋ ฅ ์‚ฌ์ด์— ํ‰ํ–‰์„ ์„ ๊ทธ๋ ธ์Šต๋‹ˆ๋‹ค. React ๊ตฌ์„ฑ ์š”์†Œ์˜ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ setState ๋˜๋Š” forceUpdate ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.

@mweststrate ์˜ˆ๋ฅผ ๋“ค์–ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํฅ๋ฏธ๋กญ๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

์•„ ์˜ˆ. ๊ทธ๋ž˜์„œ ๊ทธ๋“ค์€ ๊ทธ๊ฒƒ์„ ํ•˜๋Š” ์˜๋ฆฌํ•œ ๋ฐฉ๋ฒ•์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    let sub = Meteor.subscribe('messages')
    return {
      loading: !sub.ready(),
      messages: Messages.find().fetch()
    }
  })
componentWillUnmount: function() {
  this.comp.stop()
}

๊ตฌ๋…์€ ๋ฌธ์ œ ์—†์ด ๋งค๋ฒˆ ๋‹ค์‹œ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค. sub.ready ๋ฐ Messages.find.fetch๋Š” ๋ชจ๋‘ "๋ฐ˜์‘์ "์ด๋ฉฐ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™ ์‹คํ–‰์ด ๋‹ค์‹œ ์‹คํ–‰๋˜๋„๋ก ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. Tracker์˜ ๋ฉ‹์ง„ ์ ์€ ์ž๋™ ์‹คํ–‰์„ ์ˆจ๊ธฐ๊ธฐ ์‹œ์ž‘ํ•˜๊ณ  ๋ฌธ์„œ์—์„œ ํŠน์ • ๊ธฐ๋Šฅ์ด "๋ฐ˜์‘ ์ปจํ…์ŠคํŠธ" ๋‚ด์— ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ฌธ์„œ์— ํฌํ•จํ•  ๋•Œ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ mixin์— ๋˜์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    return this.getReactiveData()
  })
componentWillUnmount: function() {
  this.comp.stop()
}

๊ทธ๋ฆฌ๊ณ  ๋‚˜๋ฉด ์ž‘๋™ํ•˜๋Š” ๋งˆ์ˆ ์ฒ˜๋Ÿผ ๋ฐ˜์‘ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ๋‚จ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!

getReactiveData: function() {
  let sub = Meteor.subscribe('messages')
  return {
    loading: !sub.ready(),
    messages: Messages.find().fetch()
  }
}

ํŠธ๋ž˜์ปค๊ฐ€ ์ด๋ ‡๊ฒŒ ๋ฉ‹์ง„๋ฐ...

@ccorcos ์ถ”์ ๊ธฐ ์Šคํƒ€์ผ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ž๋™ unsubs์— ๋Œ€ํ•ด ๋‹ค์†Œ ์ž˜๋ชป๋œ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. componentWillUnmount ์—์„œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ค‘์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ณ„์†ํ•ด์„œ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๋„๋‹ฌํ•˜๊ณ  setState ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค(ํ˜„์žฌ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” isMounted() ๋กœ ํ™•์ธํ•˜์ง€ ์•Š๋Š” ํ•œ).

์ฐธ๊ณ ๋กœ ์šฐ์•„ํ•œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ๋งŒ๋“ค์–ด ๊ตฌ์„ฑ ์š”์†Œ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์ž…๋‹ˆ๋‹ค. [1] , [2] . ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณด์ž…๋‹ˆ๋‹ค.

export class Chat extends React.Component {
  <strong i="13">@reactive</strong>
  updateState () {
    this.setState({
      auth: auth.read(),
      messages: messages.read()
    })
  }

  /* ... */
}

@Mitranim ๊ฝค ๊น”๋”ํ•ฉ๋‹ˆ๋‹ค -- ๊ทธ๋ž˜๋„ ๊ณ ์ฐจ์› ๊ธฐ๋Šฅ์„ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค. ;)

@sebmarkbage @jimfb ์ €๋Š” ์ด ์Šค๋ ˆ๋“œ์™€ alt ์Šค๋ ˆ๋“œ(#3858)๋ฅผ ๋ช‡ ๋‹ฌ ๋™์•ˆ ํŒ”๋กœ์šฐํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ•ต์‹ฌ ํŒ€์ด ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ํ•ฉ์˜์— ๋„๋‹ฌํ–ˆ๋Š”์ง€ ๋˜๋Š” ์ ์–ด๋„ ์ผ๋ฐ˜์ ์ธ ๋ฐฉํ–ฅ์— ๋„๋‹ฌํ–ˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

@oztune ์—…๋ฐ์ดํŠธ ์—†์Œ; ์šฐ๋ฆฌ๋Š” ๋‹ค๋ฅธ ์šฐ์„ ์ˆœ์œ„์— ์ง‘์ค‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฃผ์ œ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์„ ๋•Œ ์Šค๋ ˆ๋“œ ์ค‘ ํ•˜๋‚˜์— ๊ฒŒ์‹œํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„, ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฒ”์šฉ API๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ˜์‘ ๊ตฌ์„ฑ๊ธฐ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์ฆ‰, ๊ณ ์ฐจ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { compose } from `react-komposer`;

// Create a component to display Time
const Time = ({time}) => (<div>{time}</div>);

// Create the composer function and tell how to fetch data
const composerFunction = (props, onData) => {
    const handler = setInterval(() => {
    const time = new Date().toString();
    onData(null, {time});
  }, 1000);

  const cleanup = () => clearInterval(handler);
  return cleanup;
};

// Compose the container
const Clock = compose(composerFunction)(Time);

// Render the container
ReactDOM.render(<Clock />, document.getElementById('react-root'));

์—ฌ๊ธฐ ๋ผ์ด๋ธŒ ๋ฒ„์ „์ด ์žˆ์Šต๋‹ˆ๋‹ค: https://jsfiddle.net/arunoda/jxse2yw8/

๋˜ํ•œ Promises, Rx.js Observables ๋ฐ With Meteor's Tracker๋ฅผ ์‚ฌ์šฉ ํ•˜์—ฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ด์— ๋Œ€ํ•œ ๋‚ด ๊ธฐ์‚ฌ๋ฅผ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. Let's Compose Some React Containers

@arunoda ์šฐ๋ฆฌ๋Š” ๋งค์šฐ ๋น„์Šทํ•œ ์ผ์„ ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€ ๊ถ๊ธˆํ•œ ์ ์€ prop์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค composerFunction ์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

@oztune ์‹ค์ œ๋กœ ์ด์ œ ๋‹ค์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ด Lokka ์™€ Meteor๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ๋กœ์ปฌ ์บ์‹œ๊ฐ€ ์žˆ์œผ๋ฉฐ ์šฐ๋ฆฌ๊ฐ€ composerFunction์„ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœํ•˜๋”๋ผ๋„ ์„œ๋ฒ„์— ๋„๋‹ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

const options =  {propsToWatch: ["postId"]};
const Clock = compose(composerFunction, options)(Time);

์–ด๋–ค ์•„์ด๋””์–ด?

@arunoda ๊ทธ๊ฒƒ์ด ์šฐ๋ฆฌ๋„ ์‹œ๋„ํ•œ ๊ฒƒ์ด์ง€๋งŒ, ๊ทธ๊ฒƒ์€ ์•ก์…˜๊ณผ ๊ทธ ์˜์กด์„ฑ ์‚ฌ์ด์— ์•ฝ๊ฐ„์˜ ๋‹จ์ ˆ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด์ œ react-async์™€ ์œ ์‚ฌํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์–ด์ง„ ์ž‘์—…์„ ์ฆ‰์‹œ ์ˆ˜ํ–‰ํ•˜๋Š” ๋Œ€์‹  composerFunction์ด ํ•จ์ˆ˜์™€ ํ‚ค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํ‚ค๊ฐ€ composerFunction์— ์˜ํ•ด ๋ฐ˜ํ™˜๋œ ์ด์ „ ํ‚ค์™€ ๋‹ค๋ฅธ ๊ฒฝ์šฐ ์ƒˆ ๊ธฐ๋Šฅ์ด ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ด github ์Šค๋ ˆ๋“œ์˜ ์ ‘์„ ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ Twitter(๋™์ผํ•œ ์‚ฌ์šฉ์ž ์ด๋ฆ„)์—์„œ ๊ณ„์†ํ•ด์„œ ๊ธฐ์ฉ๋‹ˆ๋‹ค.

@oztune ๋‚˜๋Š” ์ƒˆ๋กœ์šด GH ์ด์Šˆ๋ฅผ ๋งŒ๋“ค์—ˆ๊ณ  ๊ฑฐ๊ธฐ์„œ ์šฐ๋ฆฌ์˜ ์ฑ„ํŒ…์„ ๊ณ„์†ํ•ฉ์‹œ๋‹ค. ํŠธ์œ„ํ„ฐ๋ณด๋‹ค ํ›จ์”ฌ ๋‚˜์€ ๊ฒƒ ๊ฐ™์•„์š”.

๋‚ด๊ฐ€ Observable์„ ์†Œํ’ˆ(์˜ˆ: this.props.todo$)์œผ๋กœ ์ „๋‹ฌํ•˜๊ณ  JSX์— ํฌํ•จํ•  ์ˆ˜ ์žˆ๋„๋ก JSX๊ฐ€ Observable์„ ์ง์ ‘ ์ดํ•ดํ•˜๊ณ  ๋ Œ๋”๋งํ•˜๋„๋ก ํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด API๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๊ณ  ๋‚˜๋จธ์ง€๋Š” React ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌ๋˜๊ณ  HoC๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅ ํ•ญ๋ชฉ์„ ์ฑ„์šฐ๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. props์— ์ผ๋ฐ˜ ๋ฐ์ดํ„ฐ ๋˜๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” ์ค‘์š”ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํŠน๋ณ„ํ•œ this.data๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

{this.props.todo$

๋˜ํ•œ React render๋Š” ์ถ”๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—†์ด ๋งํฌ์— ์„ค๋ช…๋œ ๋””์ž์ธ์„ ํ—ˆ์šฉํ•˜๋Š” Oservable[JSX]์„ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://medium.com/@milankinen/containers -are-dead-long-live-observable-combinators-2cb0c1f06c96#.yxns1dqin

https://github.com/milankinen/react-combinators

์•ˆ๋…•ํ•˜์„ธ์š”,
ํ˜„์žฌ๋กœ์„œ๋Š” rxjs ์ŠคํŠธ๋ฆผ๊ณผ ํ•จ๊ป˜ ์ƒํƒœ ๋น„์ €์žฅ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹ค๋ฅธ API์˜ ํ•„์š”์„ฑ์„ ์ดํ•ดํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.
๋‚˜๋Š” ์˜ˆ์ œ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ณด๋“œ ์œ„์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๊ณ  26์— ๋„๋‹ฌํ•˜๋ฉด ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋„๋ก ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
์ด ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜๋Š”์ง€ ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.
์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
https://jsfiddle.net/a6ehwonv/74/

@giltig : ์ €๋„ ์ตœ๊ทผ์— ์ด๋ ‡๊ฒŒ ๋ฐฐ์šฐ๊ณ  ์žˆ๋Š”๋ฐ ์ข‹์•„์š”. Cycle.js์™€ ํ•จ๊ป˜ํ•ฉ๋‹ˆ๋‹ค.

๋ธŒ๋ฆฌ์ง•์„ ์œ„ํ•ด ์ฃผ์ œ๋ฅผ ์ „๋‹ฌํ•˜์ง€ ์•Š๊ณ ๋„ ๊ตฌ์„ฑ ์š”์†Œ์— ์ •์˜๋œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์–ด๋–ป๊ฒŒ๋“  ์‰ฝ๊ฒŒ ๋“ค์„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ดํ•ดํ•œ๋‹ค๋ฉด ์—ฌ๊ธฐ์— ์ œ์•ˆ๋œ ๊ฒƒ๊ณผ๋Š” ๊ฑฐ์˜ ๋ฐ˜๋Œ€์ž…๋‹ˆ๋‹ค. ๋˜๋Š” ํ•ฉ์„ฑ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด React vdom์„ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ "ref"๋ฅผ ๊ด€์ฐฐ์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ CSS ํƒœ๊ทธ๊ฐ€ ๊ด€์ฐฐ๋˜์ง€ ์•Š๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋” ํฐ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด React ๊ธฐ๋Šฅ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก RxJ๋Š” CombineLatest์—์„œ ๊ฐœ์ฒด๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const myFancyReactComponent = ({surface, number, gameover}) => (
        <div> 
          {gameover ? gameover : surface}
          {number}
        </div>
)

const LiveApp = Rx.Observable.combineLatest(
    LiveSurface, DynamicNumberView, DynamicGameOver,
    myFancyReactComponent
)

์•ˆ๋…•,
์šฐ๋ฆฌ๊ฐ€ Cycle์ด๋‚˜ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋ณด๋‹ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ ํ˜ธํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ฉฐ(๊ฐœ๋ฐœ์ž์—๊ฒŒ ๋” ๋งŽ์€ ์ œ์–ด ๊ถŒํ•œ์ด ์žˆ์Œ) ๋˜ํ•œ React๊ฐ€ ๊ฐ€์ง„ ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ํž˜์„ ์ฆ๊ธฐ๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
ํ˜„์žฌ React์šฉ์œผ๋กœ ๋งŽ์€ ๋ Œ๋” ์—”์ง„์ด ๊ฐœ๋ฐœ๋˜์—ˆ์œผ๋ฉฐ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์œ ๊ฐ์ž…๋‹ˆ๋‹ค(ReactNative, ReactDom, ReactThree ๋“ฑ). Rxjs๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์œ„์—์„œ ๋ณด์—ฌ์ค€ ์˜ˆ์™€ ๊ฐ™์ด ๋ฐ˜์‘ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜ต์ €๋ฒ„๋ธ”์ด props์ธ ํ•œ ํฌ์กฐ๋ฅผ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ƒ๊ฐ์ด ๋” ์‰ฌ์›Œ์กŒ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ์œผ๋กœ์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์„ ํƒํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

BTW MyFancyReactComponent๋กœ ์ˆ˜ํ–‰ํ•œ ์ž‘์—…์€ ๊ฐ€๋Šฅํ•˜๋ฉฐ jsx๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์ผ๋ถ€ ๊ฒฝ์šฐ์—๋Š” ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์ œ์™€ ๊ด€๋ จํ•˜์—ฌ - ๊ฒฐ๊ตญ React ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ๋ฌด์—‡์ด๋“  ๋  ์ˆ˜์žˆ๋Š” ์ฒ˜๋ฆฌ๊ธฐ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ํšจํ•œ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋‚ด๋ถ€ ์ฃผ์ œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ์„ ํƒํ–ˆ์ง€๋งŒ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ์—ฐํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜ต์ €๋ฒ„๋ธ”์ด props์ธ ํ•œ ํฌ์กฐ๋ฅผ ๋ฐ›์•„๋“ค์ผ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ƒ๊ฐ์ด ๋” ์‰ฌ์›Œ์กŒ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ์œผ๋กœ์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์ด ์šฐ๋ฆฌ๊ฐ€ ์„ ํƒํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์†Œํ’ˆ์€ ์žฅ๊ธฐ์ ์œผ๋กœ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ๊ทธ๋Ÿฐ ๋งฅ๋ฝ์—์„œ ์ „ํ˜€ ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค..

Suspense(์บ์‹œ + ์ปจํ…์ŠคํŠธ)๊ฐ€ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ชจ๋ธ๋กœ ๋๋‚œ ๊ฒƒ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ์บ์‹œ ์ž์ฒด๊ฐ€ ๊ตฌ๋…์— ๋Œ€ํ•œ ์ง€์›์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. https://github.com/facebook/react/issues/13206 ์—์„œ Suspense์˜ ๋‚จ์€ ์ž‘์—…์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๋˜ํ•œ ๋” ๊ฒฉ๋ฆฌ๋œ ์‚ฌ๋ก€๋ฅผ ์œ„ํ•œ ๊ตฌ๋… ํŒจํ‚ค์ง€๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰