김태희(로토/Roto) [Travel Systems CX] 
Coupang Web Developer
Coupang
Travel CX (Brussel)
김태희(로토/roto)
var HelloMessage = React.createClass({
  render: function() {
    return <div>Hello {this.props.name}</div>;
  }
});
ReactDOM.render(<HelloMessage name="John" />, mountNode);"use strict";
var HelloMessage = React.createClass({
  displayName: "HelloMessage",
  render: function render() {
    return React.createElement(
      "div",
      null,
      "Hello ",
      this.props.name
    );
  }
});
ReactDOM.render(React.createElement(HelloMessage, { name: "John" }), mountNode);Transpiling
// es5 style
var YourComponent = React.createClass({
  render: function() {
    return (
      <div>Hi?</div>
    );
  }
});// es6 style
class YourComponent extends React.Component {
  render() {
    return (
      <div>Hi!</div>
    );
  }
}혹은 atom 사용
// es6 style
class YourComponent extends React.Component {
  render() {
    return (
      <div>Hello! React.js!</div>
    );
  }
}// 틀린 예
render(){
  return (
    <div>쿠팡 최고의 동호회</div>
    <div>쿠뺀으로 오세요!</div>
  );
}
// 올바른 예
render(){
  return (
    <div>
      <div>쿠팡 최고의 동호회</div>
      <div>쿠뺀으로 오세요!</div>
    </div>
  );
}
render 함수가 반환하는 마크업의
최상위에는 단일 Root Node가 있어야 한다.
모든 element는 닫혀야 한다.
// 자주하는 실수
<br>
<img src="bali.png">
<input type="text">
// 올바른 예
<br/>
<img src="bali.png"/>
<input type="text" />attribute는 camelCase로 작성해야한다.
// 잘못된 예
<table cellpadding="5">
  <tr rowspan="2">
  ...
  </tr>
</table>
// 올바른 예
<table cellPadding="5">
  <tr rowSpan="2">
  ...
  </tr>
</table>class는 className으로
label의 for는 htmlFor로
// 잘못된 예
<div class="wrapper"> 
  <label for="name">이름</label>
  <input id="name" />
</div>
// 올바른 예
<div className="wrapper"> 
  <label htmlFor="name">이름</label>
  <input id="name" />
</div><!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
    <script src="https://fb.me/react-15.2.1.js"></script>
    <script src="https://fb.me/react-dom-15.2.1.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      // 이곳에 코드를 작성합니다.
    </script>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
    <script src="https://fb.me/react-15.2.1.js"></script>
    <script src="https://fb.me/react-dom-15.2.1.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      // HelloWorld Component 정의
      var HelloWorld = React.createClass({
        // Component는 render 함수를 기준으로 화면에 그려진다.
        render: function () {
          return (
            <div>Hello React!!!</div>
          );
        }
      });
    </script>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React</title>
    <script src="https://fb.me/react-15.2.1.js"></script>
    <script src="https://fb.me/react-dom-15.2.1.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
    <script type="text/babel">
      // HelloWorld Component 정의
      var HelloWorld = React.createClass({
        render: function () {
          return (
            <div>Hello World!!!</div>
          );
        }
      });
      // HelloWorld Comopnent 마운팅하기
      ReactDOM.render(
        <HelloWorld />,
        document.getElementById('example')
      );
    </script>
  </body>
</html>
// Hello Component 정의
var Hello = React.createClass({
  render: function (){
    return (
      <span>Hello!!</span>
    );
  }
});
// World Component 정의
var World = React.createClass({
  render: function (){
    return (
      <span>World!!</span>
    );
  }
});
// Hello와 World Component를 조합한 새로운 Component
var ThreeHelloOneWorld = React.createClass({
  render: function (){
    return (
      <div>
        <Hello />
        <Hello />
        <Hello />
        <World />
      </div>
    )
  }
});
ReactDOM.render(
  <ThreeHelloOneWorld />,
  document.getElementById('example')
);var Band = React.createClass({
  render: function() {    
    var bandName = 'Cou Fighters';
    return (
      <div>
        Band Name: {bandName}
      </div>
    );
  }    
});
ReactDOM.render(
  <Band />,
  document.getElementById('example')
);변수명을 {}로 감싼다.
var Band = React.createClass({
  render: function() {    
    var bandName = this.props.bandName;
    return (
      <div>
        Band Name: {bandName}
      </div>
    );
  }    
});
ReactDOM.render(
  <Band bandName="Cou Fighters"/>,
  document.getElementById('example')
);var Band = React.createClass({
  render: function() {    
    var bandName = this.props.bandName;
    return (
      <div>
        Band Name: {bandName}
      </div>
    );
  }    
});
var BandLineUp = React.createClass({
  render: function() {
    return (
      <div>
        <h3>Band Line Up</h3>
        <Band bandName="Cou Fighters" />
        <Band bandName="삼거리 별다방" />
        <Band bandName="AZ Taste" />
        <Band bandName="TTB" />
      </div>
    );
  }  
});
ReactDOM.render(
  <BandLineUp />,
  document.getElementById('example')
);// Timer Component 정의
var Timer = React.createClass({
  // Component의 state를 정의하는 함수
  getInitialState: function(){
    return {
      count: 0
    };
  },
  // Component가 화면에 Mount 되면 실행되는 Life cycle 함수
  componentDidMount: function(){
    setInterval(this.tick, 1000);
  },
  // setState를 통해 상태를 갱신하는 함수
  tick: function() {
    this.setState({
      count: this.state.count + 1
    });
  },  
  render: function () {
    return (
      <div>tick count {this.state.count}</div>
    );
  }
});
ReactDOM.render(
  <Timer />,
  document.getElementById('example')
);// Timer Component 정의. props로 initialCount를 받아 자신의 state에 설정
var Timer = React.createClass({
  getInitialState: function () {
    return {
      count: this.props.initialCount
    };
  },
  componentDidMount: function (){
    setInterval(this.tick, 1000);
  },
  tick: function() {
    this.setState({
      count: this.state.count + 1
    });
  },
  render: function (){
    return (
      <div>
        <TimerText count={this.state.count} />
      </div>
    )
  }
});
// count를 props로 받아 화면에 뿌려주는 역할
var TimerText = React.createClass({
  render: function (){
    return (
      <span>현재 Count는 {this.props.count} 이다!</span>
    );
  }
});
// Timer를 렌더링하면서 props로 initialCount를 넘겨줌
ReactDOM.render(
  <Timer initialCount={105}/>,
  document.getElementById('example')
);// 각 케이스에 대응하는 컴포넌트 두개를 생성
var CoupangGreeting = React.createClass({
  render: function() {
    return <h1>Welcome to the Coupang!</h1>;
  }
});
var GuestGreeting = React.createClass({
  render: function() {
    return <h1>Hello Guest!</h1>;
  }
});return 하는 Component를 다르게 하기
// 각 케이스에 대응하는 컴포넌트 두개를 생성
var CoupangGreeting = React.createClass({
  render: function() {
    return <h1>Welcome to the Coupang!</h1>;
  }
});
var GuestGreeting = React.createClass({
  render: function() {
    return <h1>Hello Guest!</h1>;
  }
});
var Greeting = React.createClass({
  render: function(){
    var isLoggedIn = this.props.isLoggedIn;
    if(isLoggedIn){
      return <CoupangGreeting />
    }else{
      return <GuestGreeting />
    }
  }
});var LoginButton = React.createClass({
  render: function() {
    return (
      <button>로그인 버튼</button/>
    )
  }
});
var LogoutButton = React.createClass({
  render: function() {
    return (
      <button>로그아웃 버튼</button>
    );
  }
});
변수를 하나 만들고 condition에 따라 해당 변수에 Component를 넣은 뒤 해당 변수를 binding
var LoginButton = React.createClass({
  render: function() {
    return (
      <button>로그인 버튼</button/>
    )
  }
});
var LogoutButton = React.createClass({
  render: function() {
    return (
      <button>로그아웃 버튼</button>
    );
  }
});
var LoginControl = React.createClass({
  render: function() {
    var button = null;
    if(this.props.isLoggedIn){
      button = <LoginButton />;
    }else{
      button = <LogoutButton />
    }
    return (
      <div>
        Hello! {button}
      </div>
    );
  }
});&& 연산자의 특성을 이용하기
var Ticket = React.createClass({
  isValidTicket() {
    // 유효성 체크한 값 리턴
    return true;
  }
  render: function () {
    return (
      <div>
        <h3>티켓 이름: {this.props.ticketName}</h3>
        {!this.isValidTicket() && 
          <h2>유효기간이 만료된 티켓입니다!</h2>
        } 
      </div>
    );
  }
});삼항연산자 이용하기
var User = React.createClass({ 
  render: function () {
    return (
      <div>
        <h2>{this.props.isLoggedIn ? '로그인 하셨네요!' : '로그인 해주세요 T_T'}</h2> 
      </div>
    );
  }
});var numbers = [1, 2, 3, 4, 5];
var listItems = numbers.map((number) =>
  <li>{number}</li>
);
return (
  <ul>
    {listItems}
  </ul>
);render: function() {
  var selectedProducts = [
    {
      id: 1,
      name: 'Fender American Standard Jazz Bass',
      price: 2000000
    },
    {
      id: 2,
      name: 'Yamaha Revstar RS620 BRB',
      price: 800000
    }
  ];
  var products = [];
  
  selectedProducts.forEach(function(selectedProduct, i){
    products.push(
      <li key={i}>
        선택하신 상품 {selectedProduct.name}의 가격은 {selectedProduct.price}원 입니다.
      </li>
    )
  });
  return (
    <div>
      <ul>
        {products}
      </ul>
    </div>  
  );
}var Timer = React.createClass({
  propTypes: {
    initialCount: React.PropTypes.number
  },
  ......
});
var TimerText = React.createClass({
  propTypes: {
    count: React.PropTypes.number.isRequired
  },
  ....
});React.createClass({
  propTypes: {
    // You can declare that a prop is a specific JS primitive. By default, these
    // are all optional.
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
    optionalSymbol: React.PropTypes.symbol,
    // Anything that can be rendered: numbers, strings, elements or an array
    // (or fragment) containing these types.
    optionalNode: React.PropTypes.node,
    // A React element.
    optionalElement: React.PropTypes.element,
    // You can also declare that a prop is an instance of a class. This uses
    // JS's instanceof operator.
    optionalMessage: React.PropTypes.instanceOf(Message),
    // You can ensure that your prop is limited to specific values by treating
    // it as an enum.
    optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
    // An object that could be one of many types
    optionalUnion: React.PropTypes.oneOfType([
      React.PropTypes.string,
      React.PropTypes.number,
      React.PropTypes.instanceOf(Message)
    ]),
    // An array of a certain type
    optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
    // An object with property values of a certain type
    optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
    // An object taking on a particular shape
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),
    // You can chain any of the above with `isRequired` to make sure a warning
    // is shown if the prop isn't provided.
    requiredFunc: React.PropTypes.func.isRequired,
    // A value of any data type
    requiredAny: React.PropTypes.any.isRequired,
    // You can also specify a custom validator. It should return an Error
    // object if the validation fails. Don't `console.warn` or throw, as this
    // won't work inside `oneOfType`.
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error(
          'Invalid prop `' + propName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    },
    // You can also supply a custom validator to `arrayOf` and `objectOf`.
    // It should return an Error object if the validation fails. The validator
    // will be called for each key in the array or object. The first two
    // arguments of the validator are the array or object itself, and the
    // current item's key.
    customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
      if (!/matchme/.test(propValue[key])) {
        return new Error(
          'Invalid prop `' + propFullName + '` supplied to' +
          ' `' + componentName + '`. Validation failed.'
        );
      }
    })
  },
  /* ... */
});propTypes에 선언된 타입과 props가 다른 경우..
isRequired로 선언된 props를 누락한 경우..
development mode에서만 check
var ClickCounter = React.createClass({
  getInitialState: function() {
    return {
      clickCounter: 0
    };
  },
  render: function () {
    return (
      <div>
        현재 버튼은 {this.state.clickCounter} 번 눌렸습니다.
        <button onClick={this.handleClick}>눌러봅시다!</button>
      </div>
    )
  },
  handleClick: function(){
    this.setState({
      clickCounter: this.state.clickCounter + 1
    });
  }
});
ReactDOM.render(<ClickCounter/>, document.getElementById('example'));대략 이런 모양새
var TodoApp = React.createClass({
  getInitialState: function () {
    return {
      todos: [
        {          
          todoText: '쿠뺀 가입하기'       
        },
        {
          todoText: '악보 외우기'
        },
        {
          todoText: 'Bass 연습하기'
        }
      ]
    };
  },
  render: function () {
    return (
      <div>
        <TodoText />
        <TodoList todos={this.state.todos}/>
      </div>
    );
  }
});var TodoText = React.createClass({
  render: function () {
    return (
      <form>
        <input type="text"
               placeholder="할 일을 입력하세요."/>
        <button type="submit">추가하기</button>
      </form>
    );
  }
});var TodoList = React.createClass({
  propTypes: {
    todos: React.PropTypes.array
  },
  render: function () {
    var todoLiComponents = [];
    var todos = this.props.todos;
    todos.forEach(function(todo, i){
      todoLiComponents.push(
        <li key={i}>
          {todo.todoText}
        </li>
      );
    });
    return (
      <ul>
        {todoLiComponents }
      </ul>
    );
  }
});var TodoText = React.createClass({
  getInitialState: function () {
    return {
      todoText: ''
    };
  },
  render: function () {
    return (
      <form>
        <input type="text"
               placeholder="할 일을 입력하세요."
               value={this.state.todoText}
               onChange={this.handleTodoTextChange}/>
        <button type="submit">추가하기</button>
      </form>
    )
  },
  handleTodoTextChange: function(e) {
    this.setState({
      todoText: e.target.value
    });
  }
});var TodoText = React.createClass({
  propTypes: {
    onAddTodo: React.PropTypes.func.isRequired
  },
  getInitialState: function () {
    return {
      todoText: ''
    };
  },
  render: function () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text"
               placeholder="할 일을 입력하세요."
               value={this.state.todoText}
               onChange={this.handleTodoTextChange}/>
        <button type="submit">추가하기</button>
      </form>
    )
  },
  handleTodoTextChange: function(e) {
    this.setState({
      todoText: e.target.value
    });
  },
  handleSubmit: function(e) {
    e.preventDefault();
    this.props.onAddTodo(this.state.todoText);
    this.setState({
      todoText: ''
    });
  }
});var TodoApp = React.createClass({
  getInitialState: function () {
    return {
      todos: [
        {          
          todoText: '쿠뺀 가입하기'       
        },
        {
          todoText: '악보 외우기'
        },
        {
          todoText: 'Bass 연습하기'
        }
      ]
    };
  },
  render: function () {
    return (
      <div>
        <TodoText onAddTodo={this.handleAddTodo}/>
        <TodoList todos={this.state.todos} />
      </div>
    )
  },
  handleAddTodo: function(todoText) {
    // 기존 state의 todos 를 clone
    var newTodos = this.state.todos.slice();
    newTodos.push({
      todoText: todoText
    });
    this.setState({
      todos: newTodos
    });
  }
});var TodoApp = React.createClass({
  getInitialState: function () {
    return {
      todos: []
    };
  },
  componentDidMount: function() {
    $.get('http://demo4539895.mockable.io/todo')
      .done(function(result){
         this.setState({
           todos: result
         });
      }.bind(this));
  },
  render: function () {
    return (
      <div>
        <TodoText onAddTodo={this.handleAddTodo}/>
        <TodoList todos={this.state.todos} />
      </div>
    )
  },
  handleAddTodo: function(todoText) {
    // 기존 state의 todos 를 clone
    var newTodos = this.state.todos.slice();
    newTodos.push({
      todoText: todoText
    });
    this.setState({
      todos: newTodos
    });
  }
});var TodoApp = React.createClass({
  ...
  render: function () {
    return (
      <div>
        <TodoText onAddTodo={this.handleAddTodo}/>
        {// onCompleted props 추가 }
        <TodoList todos={this.state.todos} 
                  onCompleted={this.handleCompleted}/>
      </div>
    )
  },
  handleCompleted: function(index) {
    // props로 넘어간 이 핸들러가 호출되면서 파라메터로 완료처리할 todo의 index를 넘긴다
    // clone 후 값을 바꾸고 setState
    var newTodos = this.state.todos.slice();
    newTodos[index].isCompleted = true;
    // state가 바뀌면서 다시 화면이 그려짐
    this.setState({
      todos: newTodos
    });
  }
  ...
});var TodoList = React.createClass({
  propTypes: {
    todos: React.PropTypes.arrayOf(React.PropTypes.shape({
      todoText: React.PropTypes.string,
      isCompleted: React.PropTypes.bool
    })).isRequired,
    onCompleted: React.PropTypes.func.isRequired
  },
  render: function () {
    var todoLis = [];
    var todos = this.props.todos;
    todos.forEach(function(todo, i){
      // todo의 isCompleted 값에 따라 동적으로 completed className을 설정
      todoLis.push(
        <li key={i} 
            className={todo.isCompleted ? 'completed' : ''} 
            onClick={this.handleClick}>
          {todo.todoText}
        </li>
      )
    }.bind(this));
    return (
      <ul>
        {todoLis}
      </ul>
    )
  },
  // todo 클릭 시 이벤트 핸들러. 이벤트가 일어난 지점의 index를 구해서 props의 onCompleted Callback 호출
  handleClick: function (e) {
    this.props.onCompleted($(e.target).index());
  }
});var TodoStatus = React.createClass({
  propTypes: {
    todos: React.PropTypes.array.isRequired
  },
  render: function (){
    var todos = this.props.todos;
    var completedCount = todos.filter(function(todo){
      return todo.isCompleted;
    }).length;
    return (
      <div>
        {todos.length} 개의 할 일 중에 {completedCount}개가 완료됨
      </div>
    )
  }
});Todo의 상태를 보여주는 TodoStatus Component 추가
...
render() {
  return (
    <div>
      <TodoText onAddTodo={this.handleAddTodo}/>
      <TodoList todos={this.state.todos} onCompleted={this.handleCompleted}/>
      <TodoStatus todos={this.state.todos} />
    </div>
  )
}
...TodoApp render 에 새 Component 추가
npm install -g create-react-appclass HelloES6React extends React.Component {
  render() {
    const {name} = this.props;
    return (
      <div>hi!!!{name}</div>
    )
  }
}
HelloES6React.propTypes = {
  name: React.PropTypes.string.isRequired
};
ReactDOM.render(<HelloES6React name="로토" />, document.getElementById('example'));import React from 'react';
class TodoText extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      todoText: ''
    };
    this.handleTodoTextChange = this._handleTodoTextChange.bind(this);
    this.handleSubmit = this._handleSubmit.bind(this);
  }
  _handleTodoTextChange(e) {
    this.setState({
      todoText: e.target.value
    });
  }
  _handleSubmit(e) {
    e.preventDefault();
    this.props.onAddTodo(this.state.todoText);
    this.setState({
      todoText: ''
    });
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text"
               placeholder="할 일을 입력하세요."
               value={this.state.todoText}
               onChange={this.handleTodoTextChange}/>
        <button type="submit">추가하기</button>
      </form>
    )
  }
}
export default TodoText;import React from 'react';
class TodoList extends React.Component {
  render() {
    let todoLiComponents = [];
    const todos = this.props.todos;
    todos.forEach((todo, i) => {
      todoLiComponents.push(
        <li key={i}>
          {todo.todoText}
        </li>
      );
    });
    return (
      <ul>
        {todoLiComponents }
      </ul>
    );
  }
}
export default TodoList;import React from 'react';
import TodoText from './TodoText';
import TodoList from './TodoList';
class TodoApp extends React.Component {
  constructor() {
    super();
    this.state = {
      todos: []
    };
    this.handleAddTodo = this._handleAddTodo.bind(this);
  }
  _handleAddTodo(todoText) {
    var newTodos = this.state.todos.slice();
    newTodos.push({
      todoText: todoText
    });
    this.setState({
      todos: newTodos
    });
  }
  render() {
    return (
      <div>
        <TodoText onAddTodo={this.handleAddTodo}/>
        <TodoList todos={this.state.todos}/>
      </div>
    )
  }
}
export default TodoApp;최초 DOM Mounting은 이곳에서.
import React from 'react';
import ReactDOM from 'react-dom';
import TodoApp from './TodoApp';
import './index.css';
ReactDOM.render(
  <TodoApp />,
  document.getElementById('root')
);
class TodoApp extends React.Component {
  // constructor 생략
  state = {
    todos: []
  }
  componentDidMount () {
    $.get('http://demo4539895.mockable.io/todo')
      .done((result) => {
         this.setState({
           todos: result
         });
      });
  }
  render() {
    return (
      <div>
        <TodoText onAddTodo={this.handleAddTodo}/>
        <TodoList todos={this.state.todos}
                  onCompleted={this.handleCompleted}/>
      </div>
    )
  }
  handleAddTodo = (todoText) => {
    // 기존 state의 todos 를 clone
    var newTodos = this.state.todos.slice();
    newTodos.push({
      todoText: todoText
    });
    this.setState({
      todos: newTodos
    });
  };
class TodoList extends React.Component {
  static propTypes = {
    todos: React.PropTypes.array,
    onCompleted: React.PropTypes.func.isRequired
  };
  
  render () {
    var todoLis = [];
    var todos = this.props.todos;
    todos.forEach((todo, i) => {
      todoLis.push(
        <li key={i}
            className={todo.isCompleted ? 'completed' : ''}
            onClick={this.handleClick}>
          {todo.todoText}
        </li>
      )
    });
    return (
      <ul>
        {todoLis}
      </ul>
    )
  }
  handleClick = (e) => {
    this.props.onCompleted($(e.target).index());
  };
}By 김태희(로토/Roto) [Travel Systems CX] 
coupang 쿠키 8기 react.js 강의용 슬라이드