'React JS'에 해당되는 글 7건


Handling Events


React elements에서의 이벤트 처리는 DOM elements에서의 이벤트 처리와 비슷하다.
다만 약간의 문법적인 차이가 있을 뿐이다.

  • React 이벤트는 소문자보다는 카멜 케이스를 이용하여 명명한다.
  • JSX를 이용하여 문자열보다는 함수형의 이벤트 핸들러를 전달한다.


HTML의 예는 다음과 같다.

<button onclick="activateLasers()">
  Activate Lasers
</button>


React는 이와 조금 다르다.

<button onClick={activateLasers}>
  Activate Lasers
</button>


또다른 차이점이라면 React에서는 기본적인 동작을 막기 위해 false를 리턴할 수 없다는 것이다.
대신에 명시적으로 preventDefault를 호출해야 한다. 예를들어 일반적인 HTML에서는 새로운 페이지가 열리는
기본적인 링크 동작을 막기 위해 아래와 같은 코드를 사용한다.

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>


React에서는 위의 코드 대신에 다음과 같이 한다.

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}


여기서 e는 synthetic 이벤트이다. React에서는 W3C의 사양에 따라 이러한 synthetic 이벤트들을 정의한다.
따라서 개발자들은 브라우저간의 호환성을 걱정할 필요가 없다. 더 많은 것을 알고 싶다면 SyntheticEvent의 참조
가이드를 보라.


보통 React를 이용할 때는 DOM elements가 생성된 후 이벤트 리스너를 추가하기 위해 addEventListener
호출할 필요가 없다. 대신에 초기 렌더링 되는 시점에 리스너를 제공해주면 된다.
ES6 class를 이용하여 component를 정의한 경우 이벤트 핸들러는 클래스의 메서드로 만드는 것이 일반적이다.
예를들어 아래의 Toggle component는 사용자가 “ON”이나 “OFF”를 토글할 수 있는 버튼을 렌더링한다.

class Toggle extends React.Component {
 constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // This binding is necessary to make this work in the callback
    this.handleClick = this.handleClick.bind(this);
 }

 handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
 }

 render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
 }
}

ReactDOM.render(
 <Toggle />,
 document.getElementById('root')
);

CodePen에서 확인하기


위의 코드에서 JSX callback 내에 있는 this의 의미에 대해 주의해야 한다. JavaScript에서는 클래스 메소드가
기본적으로 bound 되지 않는다. 만약 this.handleClick을 bind하는 것을 잊은 상태로 onClick에 전달했다면
함수가 실제로 호출되었을 때 this는 undefined가 된다.


이 것은 React만의 특별한 동작은 아니다. JavaScript에서 함수가 작동하는 방식의 일부이다. 일반적으로
onClick={this.handleClick}처럼 뒤에 ()가 없는 함수를 참조하는 경우 그 함수를 bind 하는 것이다.


만일 bind를 호출하는 것이 번거롭다면 이를 해소할 두가지 방법이 있다. 만일 실험적인 속성 초기화 문법을 사용
한다면 callback의 정확한 bind를 위해 속성 초기화를 이용할 수 있다.

class LoggingButton extends React.Component {
 // This syntax ensures this is bound within handleClick.
 // Warning: this is *experimental* syntax.
 handleClick = () => {
    console.log('this is:', this);
 }

 render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
 }
}


이러한 문법은 React app 생성 시 기본적으로 사용된다.
만일 속성 초기화 문법을 사용하지 않는다면 callback에 화살표 함수를 사용할 수 있다.

class LoggingButton extends React.Component {
   handleClick() {
     console.log('this is:', this);
   }

   render() {
      // This syntax ensures this is bound within handleClick
      return (
        <button onClick={(e) => this.handleClick(e)}>
          Click me
        </button>
      );
   }
}


이 문법에서의 문제점은 LoggingButton이 렌더링되는 시점에 서로 다른 각각의 callback이 생성된다는 것이다.
대부분의 경우에 이 것은 문제되지 않는다. 하지만 만일 callback이 prop으로서 하위 component로 전달된다면
이 컴포넌트들은 추가적으로 다시 렌더링 될 수 있다. 따라서 이런 종류의 성능 관련 문제를 피하기 위해서 일반적으로
생성자에서 binding 하거나 속성 초기화 문법을 사용할 것을 권장한다.






'Study > React' 카테고리의 다른 글

React 살펴보기 #8  (0) 2017.05.15
React 살펴보기 #7  (0) 2017.04.20
React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요

Components and Props

component는 UI를 독립적이고 재사용 가능한 부분으로 나누는 개별적인 조각으로 생각할 수 있다.
개념상으로는 component는 JavaScript의 함수와 같다. component는 (“props”라고 불리는)임의의 입력을 
받아서 화면에 그려지는 방식을 기술하는 React elements를 리턴해준다.


Functional and Class Components

가장 간단하게 component를 만드는 방법은 JavaScript 함수를 만드는 것이다.

function Welcome(props) {
 return <h1>Hello, {props.name}</h1>;
}

이 함수는 하나의 “props”라는 데이터를 포함한 객체를 인자로 받아서 React elements를 리턴해주므로 유효한
React component라고 할 수 있다. 이러한 component는 말 그대로 JavaScript의 함수(function)와 같은

형태이기에 “functional”이라고 부른다.


물론 ES6 class를 이용해서도 component를 만들 수 있다.

class Welcome extends React.Component {
 render() {
	return <h1>Hello, {this.props.name}</h1>;
 }
}

React의 관점에서 봤을 때 위의 두 component는 동일하다.


Class들은 우리가 다음 장에서 논의할 추가적인 특징들을 가지고 있다. 그 때까지는 간단하게 functional 
components를 사용할 것이다.


Rendering a Component 

지금까지는 분명 DOM tag를 표현하는React elements들 만을 보아왔다.

const element = <div />;

그러나 elements는 사용자 정의 components를 표현할 수도 있다.

const element = <Welcome name="Sara" />;

React가 elements를 통해 사용자 정의 component가 표현되는 과정을 감지할 때 React는 하나의 객체형태로
JSX 속성을 component에 전달하게 되는데 우리는 이 객체를 “props”라고 부른다.


예를 들어 아래 코드는 화면에 “Hello, Sara”를 표시하게 된다.

function Welcome(props) {
 return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
 element,
 document.getElementById('root')
);

CodePen에서 확인해보자

이 코드에서 무슨 일이 벌어지는 지 되짚어보자.

  1. 우리는 <Welcome name="Sara" />라는 element를 인자로 하여 ReactDOM.render()를 호출한다.
  2. React는 {name: 'Sara'}를 props로 하여 Welcome component를 호출한다.
  3. Welcome component는 그 결과 값으로 <h1>Hello, Sara</h1>라는 element를 리턴한다.
  4. React DOM은 <h1>Hello, Sara</h1>와 일치하도록 효율적으로 DOM을 업데이트 한다.

경고 :
component이름은 항상 대문자로 시작해야 한다.
예를들어 <div />는 DOM tag를 표시하며 <Welcome />는 component를 표시한다 그리고 Welcome
같은 scope 내에 있어야만 한다.

Composing Components

component는 그 자신의 출력에서 다른 component를 참조할 수 있다. 이를 통해서 모든 세부적인 수준에서라도 
공통적으로 이용 가능한 component의 추상화를 사용할 수 있다. React에서는 버튼, form, 다이얼로그, 화면 등이
일반적으로 component로 표현된다.


예를 들면 Welcome을 여러번 렌더링할 수 있는 App이라는 component를 생성할 수 있다.

function Welcome(props) {
 return <h1>Hello, {props.name}</h1>;
}

function App() {
 return (
	<div>
	  <Welcome name="Sara" />
	  <Welcome name="Cahal" />
	  <Welcome name="Edite" />
	</div>
 );
}

ReactDOM.render(
 <App />,
 document.getElementById('root')
);

CodePen에서 확인해보자


일반적으로 새로운 React app을 만들 때에는 제일 먼저 App이라는 하나의 component를 만들어 시작한다. 
하지만 만일 기존에 존재하는 app에 React를 통합하는 경우라면 버튼 같은 작은 component로부터 시작하여
점차로 view 계층의 상위 요소로 구성해 나갈 수 있다.


경고 :
Components는 반드시 하나의 root element만을 리턴해야 한다. 이 때문에 모든 <Welcome />element를 포함한 <div />를 추가한 것이다.

Extracting Components

components를 더 작은 components로 나누는 것을 두려워 하지 말라.


예를 들어 아래의 Comment component를 살펴보자

function Comment(props) {
 return (
	<div className="Comment">
	  <div className="UserInfo">
		<img className="Avatar"
		  src={props.author.avatarUrl}
		  alt={props.author.name}
		/>
		<div className="UserInfo-name">
		  {props.author.name}
		</div>
	  </div>
	  <div className="Comment-text">
		{props.text}
	  </div>
	  <div className="Comment-date">
		{formatDate(props.date)}
	  </div>
	</div>
 );
}

CodePen에서 확인해보자


이 component는 props의 author (객체 타입), text (문자열 타입), 그리고 date (날짜 타입)에 접근 할 수
있다. 그리고 소셜 미디어 웹사이트의 comment를 표시한다.


이 component는 모두 중첩된 구조로 되어있어 수정하기가 어렵고 각각을 개별적인 부분으로 재사용하기도 
어렵다. 이 component로부터 몇개의 작은 component를 추출해보자.


우선 Avatar를 추출해보자

function Avatar(props) {
 return (
	<img className="Avatar"
	  src={props.user.avatarUrl}
	  alt={props.user.name}
	/>
 );
}

Avatar는 Commnet안에서 렌더링 될 것을 알 필요가 없다. 즉, 다른 어떤 component에서 렌더링이 될 수도
있는 것이다. 때문에 prop을 author보다 일반적인 user로 이름 붙였다.


props의 이름은 component가 사용되는 맥락보다는 component 자체의 관점에서 지어줄 것을 권장한다.


이제 Comment를 조금 간단하게 할 수 있다.

function Comment(props) {
 return (
	<div className="Comment">
	  <div className="UserInfo">
		<Avatar user={props.author} />
		<div className="UserInfo-name">
		  {props.author.name}
		</div>
	  </div>
	  <div className="Comment-text">
		{props.text}
	  </div>
	  <div className="Comment-date">
		{formatDate(props.date)}
	  </div>
	</div>
 );
}

다음으로는 Avatar를 렌더링하고 다음에 사용자의 이름을 렌더링하는 UserInfo component를 추출해보자

function UserInfo(props) {
 return (
	<div className="UserInfo">
	  <Avatar user={props.user} />
	  <div className="UserInfo-name">
		{props.user.name}
	  </div>
	</div>
 );
}

이렇게 하면 Comment를 좀 더 단순하게 만들 수 있다.

function Comment(props) {
 return (
	<div className="Comment">
	  <UserInfo user={props.author} />
	  <div className="Comment-text">
		{props.text}
	  </div>
	  <div className="Comment-date">
		{formatDate(props.date)}
	  </div>
	</div>
 );
}

CodePen에서 확인해보자


component를 추출하는 작업은 처음에는 귀찮은 작업으로 보일 수도 있다. 그러나 재사용 가능한 components의
모음을 가지고 있으면 더 큰 app에서 발생하는 비용을 줄여줄 것이다. 경험에 비추어보았을 때 UI의 일부가 여러번
반복해서 사용되거나(ButtonPanelAvatar), 그 자체로 충분히 복잡하다면(AppFeedStory
Comment) 이러한 것들은 재사용 가능한 component를 만들기 위한 좋은 후보자들이다.


Props are Read-Only

component가 function으로 선언되었건 Class로 선언되었건 props를 수정할 수는 없다. 아래의 sum 
function을 살펴보자

function sum(a, b) {
 return a + b;
}

이런 함수는 전달받은 입력값을 변경하지도 않고 같은 값이 입력된다면 항상 리턴 값도 동일하기 때문에 “pure”라고 
부른다.


반면에 전달 받는 입력 값을 변경하는 아래 함수는 impure다.

function withdraw(account, amount) {
 account.total -= amount;
}

React는 꽤 유연하지만 하나의 엄격한 규칙이 있다.


모든 React components는 props에 대해서는 반드시 pure로 작동해야 한다.


물론 애플리케이션의 UI들은 유동적이며 시간이 지남에 따라 변한다. 다음 장에서는 “state”라는 새로운 개념에 대해
소개할 것이다. State는 이러한 규칙을 위반하지 않으면서도 사용자의 동작, 네트워크 응답 및 기타 다른 요소들에

대한 응답으로 시간 경과에 따라 출력을 변경할 수 있도록 해준다.

'Study > React' 카테고리의 다른 글

React 살펴보기 #7  (0) 2017.04.20
React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
React 살펴보기 #2  (0) 2017.02.06
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요


Redering Elements

Elements는 React 애플리케이션을 만드는 가장 작은 구성요소이다.
elements는 화면에 보여주고자 하는 것에 대한 표현이다.

const element = <h1>Hello, world</h1>;

브라우저의 DOM elements와 달리 React elements는 일반적인 객체이며 생성하는데 비용이 적게 든다.
React DOM은 React elements와 일치하도록 DOM을 업데이트한다.

주의 :
elements는 더 널리 알려진 개념인 “components”와 혼동할 수 있다. components에 대해서는 다음 
장에서 설명할 것이다. Elements는 component가 어떻게 구성되는지를 알려주는 요소이며 따라서 다음 
장으로 넘거가기 전에 이 장을 반드시 읽을 것을 권한다.



Rendering an Element into the DOM

HTML 문서 안 어딘가에 <div> 태그가 있는 상황을 가정해보자

<div id="root"></div>

이 <div> 태그 안에 있는 모든 것들은 React DOM에 의해 관리될 것이기 때문에 “root” DOM node라 부를 
것이다. React만으로 만들어지는 애플리케이션은 보통 한 개의 root DOM node를 갖게 된다. 만일 기존의 
애플리케이션에 React를 통합하는 상황이라면 원하는 만큼의 각각 독립된 root DOM node를 만들 수도 있다.
root DOM node 안에 React elements를 렌더링하기 위해서는 React.render() 함수를 통해 (함수의 
파라미터로) React elements와 root DOM node를 모두 전달해준다.

const element = <h1>Hello, world</h1>;
ReactDOM.render(
 element,
 document.getElementById('root')
);

CodePen에서 실행해보자
위의 코드는 화면에 “Hello, world”를 표시할 것이다.


Updating the Rendered Element

React elements는 불변성을 가지고 있다. 어떤 element를 생성했다면 그 children이나 속성들을 수정할 수
없다. 하나의 element는 영화 속의 한 프레임과 같다. 그것은 특정 시점에 UI를 표현해 주는 것이다.


우리가 아는 한 UI를 업데이트하는 유일한 방법은 새로운 element를 만들어 React.render()로 전달하는
것이다.

function tick() {
 const element = (
	<div>
	  <h1>Hello, world!</h1>
	  <h2>It is {new Date().toLocaleTimeString()}.</h2>
	</div>
 );
 ReactDOM.render(
	element,
	document.getElementById('root')
 );
}

setInterval(tick, 1000);

CodePen에서 실행해보자

위의 코드는 매초마다 setInterval의 callback 함수에서 React.render()를 호출한다.

주의 :
일반적으로 모든 React 애플리케이션은 React.render()를 한 번만 호출한다. 다음 장에서 이러한 
코드가 어떻게 상태(state)를 포함하는 components로 캡슐화 되는지에 대해 배우게 될 것이다. 각각이 
연관되는 내용이므로 주제를 건너 뛰지 않을 것을 권한다.



React Only Updates What's Necessary

React DOM은 element와 그 children을 이전 요소와 비교하여 DOM을 원하는 상태로 만드는 데 필요한 
DOM 업데이트 만 적용한다.


CodePen 이용하여 마지막 예제를 확인할 수 있다.


비록 우리가 tick을 호출할때마다 전체 UI 구조를 표현할 element를 생성했다고 할지라도 실제로는 그 내용이
변경되는 text 노드만이 React DOM에 의해서 업데이트 된다.


우리의 경험에 의하면 “전체 시간 안에서 UI가 어떻게 변하는지”에 대해 생각하는 것보다 “특정한 시점에 UI가 
어떻게 보여져야 하는지”에 대해 생각하는 것이 버그를 줄일 수 있는 방법이다.

'Study > React' 카테고리의 다른 글

React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
React 살펴보기 #2  (0) 2017.02.06
React 살펴보기 #1  (0) 2017.02.06
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요


앞서의 두 포스팅은 Quick Start에 있는 내용이었고 이번 번역할 JSX In Depth는 Advanced Guides에
있는 내용이입니다. 하지만 JSX를 한 번에 살펴보는 것이 좋을 것 같아 바로 이 JSX In Depth를 번역합니다.


JSX 심화

기본적으로 JSX는 단지 React.createElement(component, props, ...children) 함수에 대한
문법적인 편의성만을 제공한다. 아래 JSX 코드를 보자

<MyButton color="blue" shadowSize={2}>
 Click Me
</MyButton>

이 코드는 컴파일 후 다음과 같이 바뀐다.

React.createElement(
 MyButton,
 {color: 'blue', shadowSize: 2},
 'Click Me'
)

만일 태그에 자식이 없다면 아래 코드와 같이 self-closing 태그(/>)를 사용할 수 있다.

<div className="sidebar" />

이 코드는 아래와 같이 컴파일된다.

React.createElement(
 'div',
 {className: 'sidebar'},
 null
)

특정 JSX가 컴파일 후 어떻게 javascript 코드로 바뀌는지를 테스트해보고 싶다면 the online Babel compiler
를 사용하면 된다.


Specifying The React Element Type

JSX 태그의 처음 부분은 React element의 type을 결정한다.
대문자로 시작된 JSX 태그는 React Component를 가리킨다. 이러한 태그들은 해당 이름으로 된 변수들의
직접적인 참조로 컴파일되기 때문에 만일 <Foo />라는 JSX 표현식을 사용하게 되면 Foo라는 객체는
같은 scope 내에 있어야 한다. 


React Must Be in Scope

JSX가 React.createElement 함수를 호출하도록 컴파일되기 때문에 React 라이브러리는 JSX 코드의
scope 내에 있어야 한다.


예를 들면 아래 코드는 비록 React와 CustomButton이 javascript로부터 직접 참조되지 않더라도
2개의 import문이 모두 필요하다.

mport React from 'react';
import CustomButton from './CustomButton';

function WarningButton() {
 // return React.createElement(CustomButton, {color: 'red'}, null);
 return <CustomButton color="red" />;
}

자바 스크립트 번들을 사용하지 않고 React를 스크립트 태그로 추가했다면 이미 React 글로벌의 scopre 내에 
있다고 할 수 있을 것이다.


Using Dot Notation for JSX Type

JSX 내에서 React 컴포넌트는 점(.) 표시로 참조될 수 있다. 이 것은 많은 React 컴포넌트를 추출할 수 있는
단일 모듈을 사용할 때 편리하다. 예를들면 만일 MyComponents.DatePicker가 컴포넌트라면 아래 예제와
같이 JSX에서 직접 사용 가능하다

import React from 'react';

const MyComponents = {
 DatePicker: function DatePicker(props) {
	return <div>Imagine a {props.color} datepicker here.</div>;
 }
}

function BlueDatePicker() {
 return <MyComponents.DatePicker color="blue" />;
}


User-Defined Components Must Be Capitalized

element가 소문자로 시작한다면 이것은 <div>나 <span>같은 기존 html 태그로 참조하게 될 것이고 그 결과
React.createElement에 ‘div’나 ‘span’같은 문자열로 전달될 것이다.


하지만 <Foo />와 같이 대문자로 시작을 한다면 React.createElement(Foo) 형태로 컴파일 될 것이고
작성된 javascript 파일에서 선언되거나 import된 component와 상응하게 될 것이다.


따라서 component 이름은 대문자로 시작하는 것을 권장한다. 만일 작성된 코드에 소문자로 시작되는 
component가 존재한다면 그것을 JSX에 사용하기 전에 대문자로 시작되는 변수에 먼저 할당을 해야 한다.



예를들면 아래 코드는 정상적으로 작동하지 않을 것이다.

import React from 'react';

// Wrong! This is a component and should have been capitalized:
function hello(props) {
 // Correct! This use of <div> is legitimate because div is a valid HTML tag:
 return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
 // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
 return <hello toWhat="World" />;
}

위의 코드를 수정하기 위해서는 hello라는 이름을 Hello로 바꾸고 <Hello />로 그 것을 참조해야 한다.

import React from 'react';

// Correct! This is a component and should be capitalized:
function Hello(props) {
 // Correct! This use of <div> is legitimate because div is a valid HTML tag:
 return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
 // Correct! React knows <Hello /> is a component because it's capitalized.
 return <Hello toWhat="World" />;
}


Choosing the Type at Runtime 

일반적인 표현식을 React element type으로 사용할 수 없다. 만일 일반적인 표현식을 element type으로
사용하고 싶다면 먼저 그 표현식을 대문자로 시작하는 변수에 할당하여야 한다. 이러한 상황은 종종 prop을
기반으로 하여 서로 다른 컴포넌트를 렌더링하고자 할 때 발생한다.

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
 photo: PhotoStory,
 video: VideoStory
};

function Story(props) {
 // Wrong! JSX type can't be an expression.
 return <components[props.storyType] story={props.story} />;
}

이 문제를 바로잡기 위해서는 먼저 대문자로 시작하는 변수에 할당해야 한다.

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
 photo: PhotoStory,
 video: VideoStory
};

function Story(props) {
 // Correct! JSX type can be a capitalized variable.
 const SpecificStory = components[props.storyType];
 return <SpecificStory story={props.story} />;
}


Props in JSX

JSX에서 prop을 지정하는 방법에는 몇가지가 있다.


JavaScript Expressions

javascript의 표현식을 {}로 둘러 싸서 prop으로 전달할 수 있다. 아래 예에서 보자.

<MyComponent foo={1 + 2 + 3 + 4} />

MyComponent에서 props.foo는 10이 된다. 1+2+3+4가 {}둘러싸여 javascript 연산으로 평가되었기
때문이다.


if문이나 for 루프는 javascript의 표현식이 아니기 때문에 JSX 내에서 직접 사용할 수는 없다. 대신에 다음의
예제처럼 코드를 이용하여 처리할 수 있다.

function NumberDescriber(props) {
 let description;
 if (props.number % 2 == 0) {
	description = <strong>even</strong>;
 } else {
	description = <i>odd</i>;
 }
 return <div>{props.number} is an {description} number</div>;
}


String Literals

문자열도 prop으로 전달할 수 있다. 아래의 두 JSX표현은 동일한 것이다.

<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

문자열로 전달할 때 그 문자열은 HTML escaping 처리되지 않는다. 따라서 아래 두 코드도 동일하다.

<MyComponent message="&lt;3" />

<MyComponent message={'<3'} />

이런 동작들 사이에 서로 관련성은 없으며 다만 완벽한 설명을 위해 언급된 것 뿐이다.


Props Default to "True"

만일 prop에 아무런 값을 넘기지 않는다면 prop은 기본적으로 true라는 값을 갖게 된다. 아래의 두
JSX 표현식은 동일하다.

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

일반적으로 이러한 표현은 ES6의 축약 표현에서 {foo}가 {foo: foo}를 의미하기 때문에 권장하지 않는다.
대신에 {foo: true}와 같이 명시적으로 사용해야 한다. 이러한 동작은 단지 HTML의 동작과 일치하도록 하기
위한 것일 뿐이다.


Spread Attributes

이미 object 형태의 props이 존재하고 이것을 JSX로 전달하려고 한다면 전체 props object를 모두
전달하기 위한 전개(spread) 연산자로 를 사용할 수 있다. 아래의 두 컴포넌트는 동일한 동작을 한다.

function App1() {
 return <Greeting firstName="Ben" lastName="Hector" />;
}

function App2() {
 const props = {firstName: 'Ben', lastName: 'Hector'};
 return <Greeting {...props} />;
}

전개(Spread) 연산자는 generic containers를 만들때 유용하다. 그러나 이러한 사용이 코드를 더 복잡하게
만들 수도 있다. 다수의 관련 없는 props를 컴포넌트에 넘김으로써 관리하기가 힘들어질 수 있기 때문이다.
따라서 이러한 문법은 반드시 필요한 부분에만 적절하게 사용하여야 한다.


Children in JSX

여는 태그와 닫는 태그가 있는 JSX 표현에서 이 두 태그 사이에 있는 내용은 props.children라는 특별한
prop으로 전달된다. children을 전달하는데는 몇가지 다른 방법이 있다.


String Literals

우선 여는 태그와 닫는 태그 사이에 문자열을 넣을 수 있으며, 이 때의 props.children는 단지 문자열로만
취급된다. 이러한 방식은 기존 HTML 요소를 사용하는 경우 유용하다. 아래의 예를 보자

<MyComponent>Hello world!</MyComponent>

이 코드는 유효한 JSX 표현식이며 MyComponent의 props.children의 값은 단순히 “Hello world!”라는
문자열이다. HTML은 escaping되지 않으므로 일반적으로 아래 코드와 같이 HTML을 작성하듯이 JSX를 작성할
수 있다.

<div>This is valid HTML &amp; JSX at the same time.</div>

JSX는 각 줄의 첫 번째와 마지막에 있는 공백은 모두 제거한다. 또한 비어있는 줄도 제거한다. 태그에 인접한
줄바꿈도 제거된다. 문자열 중간에 있는 줄바꿈은 하나의 공백으로 처리된다. 이런 이유로 아래의 모든 코드는
동일하다.

<div>Hello World</div>

<div>
 Hello World
</div>

<div>
 Hello
 World
</div>

<div>

 Hello World
</div>


JSX Children

JSX elements도 children으로 사용을 할 수 있으며 이러한 표현은 중첩된 컴포넌트를 표현하는데 유용하다.

<MyContainer>
 <MyFirstComponent />
 <MySecondComponent />
</MyContainer>

서로 다른 유형의 children을 함께 사용할 수 있기 때문에 문자열 children을 JSX children과 함께 사용할 수
있다. 이러한 방식은 JSX를 HTML과 유사하게 사용할 수 있는 또다른 방법이며 따라서 아래의 코드는 JSX로도
또 HTML로도 유효한 표현식이다.

<div>
 Here is a list:
 <ul>
	<li>Item 1</li>
	<li>Item 2</li>
 </ul>
</div>

React 컴포넌트는 다수의 React elements를 리턴할 수 없다. 하지만 하나의 JSX 표현식에는 다수의
children이 있을 수 있기 때문에 다수의 요소로 컴포넌트를 렌더링 하고 싶다면 위의 코드와 같이 div
씌운 중첩된 구조를 만들면 된다.


JavaScript Expressions

{}로 감싼다면 어떠한 javascript 표현식이라도 children으로 전달할 수 있다. 아래 예제들은 동일한 코드이다.

<MyComponent>foo</MyComponent>

<MyComponent>{'foo'}</MyComponent>

이러한 방식은 임의의 길이를 갖는 JSX 표현식의 목록을 렌더링 하는데 유용하다. 아래 예제는 HTML 목록을
렌더링하는 코드이다.

function Item(props) {
 return <li>{props.message}</li>;
}

function TodoList() {
 const todos = ['finish doc', 'submit pr', 'nag dan to review'];
 return (
	<ul>
	  {todos.map((message) => <Item key={message} message={message} />)}
	</ul>
 );
}

javascript 표현식은 다른 유형의 children과 함께 섞어서 사용할 수 있다. 이 것은 문자열 template 대신에
사용하면 유용하다.

function Hello(props) {
 return <div>Hello {props.addressee}!</div>;
}


Functions as Children

보통 JSX에 삽입되는 javascript 표현식은 문자열이나 React element나 이들의 목록으로 평가된다. 그러나
props.children은 렌더링 방법에 대해 알 필요가 없다는 점 뿐만 아니라 어떤 종류의 데이터도 전달할 수
있다는 점에서 다른 종류의 prop과 동일하게 작동한다. 예를들어 컴포넌트에서 callback을 props.children
으로 사용할 수도 있는 것이다.

// Calls the children callback numTimes to produce a repeated component
function Repeat(props) {
 let items = [];
 for (let i = 0; i < props.numTimes; i++) {
	items.push(props.children(i));
 }
 return <div>{items}</div>;
}

function ListOfTenThings() {
 return (
	<Repeat numTimes={10}>
	  {(index) => <div key={index}>This is item {index} in the list</div>}
	</Repeat>
 );
}

커스텀 컴포넌트에 전달된 children은 렌더링이 진행되기 전에 React가 해석할 수 있는 것이라면 어떤 형태로도
바뀔 수 있다. 이러한 방식은 일반적인 사용법은 아니지만 JSX가 수행할 수 있는 작업을 확장하고자 할 때 유용하게
사용될 수 있다.


Booleans, Null, and Undefined Are Ignored

falsenullundefined 그리고 true 등도 모두 유효한 children들이다. 이 것들은 별도로 렌더링 하지
않는다. 아래 JSX 표현식들은 모두 동일하게 렌더링된다. 

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

이러한 표현들은 React elements를 조건에 따라 렌더링할 때 유용하다. 아래 예제는 만일 showHeader가
true이면 <Header />만 렌더링한다.

<div>
 {showHeader && <Header />}
 <Content />
</div>

한 가지 주의할 점은 숫자 0과 같이 false를 의미하는 듯한 값은 React에서 렌더링 된다는 점이다. 예를들면 
아래의 코드에서 만일 props.messages가 비어있는 배열이라면 props.messages.length는 0
되어 false로서 기능하는 것이 아니라 0이 출력되게 된다.

<div>
 {props.messages.length &&
	<MessageList messages={props.messages} />
 }
</div>

반대로 falsetrue null 그리고 undefined 과 같은 것들을 화면상에 출력하기 위해서는 먼저
이것들을 문자열로 변환해야 한다.

<div>
 My JavaScript variable is {String(myVariable)}.
</div>


'Study > React' 카테고리의 다른 글

React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
React 살펴보기 #2  (0) 2017.02.06
React 살펴보기 #1  (0) 2017.02.06
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요


JSX 소개


아래 변수 선언을 살펴보자


const element = <h1>Hello, world!</h1>;


조금은 우스꽝스럽게도 보이는 이 문법은 문자열도 아니고 그렇다고 HTML도 아니다.
이것은 JSX라고 불리는 javascript의 확장 문법이다. React를 이용하여 UI를 표현할 때 JSX를 사용하는 것을
권장한다. JSX는 템플릿 언어를 연상시키기도 하지만 javascript의 모든 기능을 사용할 수 있다.


JSX는 React의 “elements”를 생성한다. “elements”를 DOM 구조에 렌더링하는 과정은 다음 장에서 살펴볼
것이고 여기에서는 React를 시작하는데 필요한 JSX의 기본적인 내용을 살펴볼 것이다.


Embedding Expressions in JSX


중괄호({})를 이용하면 어떠한 javascript 표현이라도 JSX에 포함시킬 수 있다. 예를 들어 ‘2+2’라는 수식이나
user.name같은 객체 표현식 또는 formatName(user)같은 함수 호출도 가능하다.


function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
	Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);


가독성을 위해 여러 줄로 코드를 구성하였다. 필수적인 요소는 아니지만 이 코드를 수행할 때 세미콜론 자동입력
룰의 위험을 피하기 위해 적절하게 괄호로 둘러 싸는 것이 좋을 것이다.


JSX is an Expression Too


컴파일 후에 JSX 표현식은 일반적인 javascript의 객체들로 바뀐다. 이 말은 if문이나 for loop 내에서
JSX표현식을 변수에 할당 하거나 함수의 인자로 사용하거나 함수의 리턴 값으로 사용할 수 있다는
것이다.


function getGreeting(user) {
  if (user) {
	return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

Specifying Attributes with JSX


특정 문자열을 속성 값으로 사용하기 위해 따옴표(“”)를 이용할 수 있다.


const element = <div tabIndex="0"></div>;


또한 중괄호({})를 이용하여 속성 값으로 javascript 표현식을 사용할 수도 있다.


const element = <img src={user.avatarUrl}></img>; 


속성 값으로 사용하기 위해 중괄호 내에서 따옴표를 사용하면 안된다. 이렇게 사용할 경우 JSX는 이 속성
값을 표현식이 아닌 문자열로 인식하게 된다(즉, { 2+2 }는 4로 인식하지만 {“2+2”}는 2+2라는 
문자열로 인식한다는 뜻이다). 중괄호는 표현식에 따옴표는 문자열에 각가 사용하되 두 기호를 동시에
사용하면 안된다.


Specifying Children with JSX


태그의 내용이 비어있을 경우에는 XML의 형식과 같이 />로 닫아주어야 한다. 


const element = <img src={user.avatarUrl} />;


JSX 태그는 자식을 포함할 수 있다.


const element = (
 <div>
	<h1>Hello!</h1>
	<h2>Good to see you here.</h2>
 </div>
);

주의 : 
JSX는 HTML 보다는 javascript에 가깝기 때문에 React DOM은 HTML 속성 스타일의 표기법이 아닌
camelCase 형식으로 속성 이름을 표기하도록 규정하고 있다.
예를 들면 class는 className으로, tabindex는 tabIndex로 표현하는 식이다.

JSX Prevents Injection Attacks


JSX에서의 사용자 입력은 안전하다.


const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;


기본적으로 React DOM에서는 렌더링하기 전에 JSX에 포함되는 사용자 입력 값들을 escaping 처리한다.
이런 방식은 애플리케이션에서 명시적으로 구현된 코드가 아닌 다른 코드의 주입을 결코 허용하지 않기 때문에 
안전을 보장할 수 있다. 모든 외부 코드들은 렌더링 전에 문자열로 처리된다. 이것은 XSS(cross-site-scripting)
공격을 막아준다.


JSX Represents Objects


Babel은 JSX 코드를 React.createElements()호출로 컴파일한다. 따라서 아래 두 블럭의 코드는 동일하다.


const element = (
 <h1 className="greeting">
	Hello, world!
 </h1>
);

-

const element = React.createElement(
 'h1',
 {className: 'greeting'},
 'Hello, world!'
);


React.createElements()는 버그 방지를 위한 몇가지 체크를 수행하지만 본질적으로 아래 코드와 같이
객체를 생성한다.


// Note: this structure is simplified
const element = {
 type: 'h1',
 props: {
	className: 'greeting',
	children: 'Hello, world'
 }
};


이런 객체들을 “React elements”라고 부른다. 이러한 객체들은 화면에서 보고자 하는 UI에 대한 기술로
생각해도 좋다. React는 이러한 객체를 읽고 이를 사용하여 DOM을 구성하고 최신 상태로 유지한다.


다음 장에서 DOM 구조에 React elements를 렌더링하는 방법을 살펴볼 것이다.
(제 블로그에서는 JSX에 대한 연속성을 위해 다음 포스팅에서는 JSX In Death를 번역할 것입니다.)


팁 :
“Babel” 문법 구조를 찾아내기 쉽도록 ES6와 JSX에 대해 highlight 기능이 있는 편집기를 사용하는 것을
권장한다.


'Study > React' 카테고리의 다른 글

React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
React 살펴보기 #2  (0) 2017.02.06
React 살펴보기 #1  (0) 2017.02.06
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요


React 개요


(이 글은 Till60 카테고리에 포함되었던 내용인데 글의 성격상 React 번역 부분만 따로 옮겨온
것입니다.)


아래 내용은 React 공식 홈페이지의 메인 페이지 내용을 의역한 것이다.


  1. React는 UI의 상호작용을 보다 쉽게 처리할 수 있는데, 변경된 데이터와 관련된 내용만을 업데이트
    하고 렌더링 하는 방식으로 이를 효율적으로 수행한다. 이러한 방식은 결국 예측 가능한 코드를 만들 
    수 있게 하고 따라서 디버깅이 쉬워진다.
  2. 자신의 상태를 관리할 수 있는 캡슐화된 컴포넌트를 만듦으로써 복잡한 UI를 쉽게 구성할 수 있으며

    이러한 로직을 자바스크립트로 작성하기 때문에 다양한 데이터 형식을 쉽게 전달할 수 있고 또 DOM

    에 의존하지 않고 상태를 유지할 수 있다.

  3. React는 특정한 기술 스택에 의존하지 않고 React 내에서 새로운 기능들을 개발 할 수 있도록 구현

    되었다. 또한 React Native를 이용하면 Node를 이용하여 서버측에 렌더링 하거나 강력한 모바일

    앱에 렌더링 하는 것도 가능하다.


◆ 간단한 컴포넌트


React의 기본은 입력 데이터를 전달받아 화면에 표시해주는 render()함수를 구현하는 것이다.
아래 코드는 XML 구문과 유사한 JSX를 이용하여 구현된 코드이다. 하지만 JSX 사용이 필수적인
것은 아니다.


class HelloMessage extends React.Component {
	render() {
			return <div>Hello {this.props.name}</div>;
	}
}
ReactDOM.render(<HelloMessage name="Jane" />, mountNode);


위의 코드에서 눈여겨 봐야 할 것은 HelloMessage라는 클래스 이름과 ReactDOM.render의 
호출이다.ReactDOM.render의 호출을 통해 비로소 화면에 데이터가 출력되는데 이 함수 안에서
HelloMessage 클래스가 어떻게 사용되고 있는지 또 그 안에 name이라는 property가 어떻게 
HelloMessage 클래스로 전달되는지(ReactDOM.render 함수의 파라미터에 있는 name은 
this.props.name이라는 형태로 HelloMessage 클래스 안으로 전달된다)를 주의해서 보자. 
이 코드의 결과는 브라우저 화면에 “Hello Jane” 이라는 문자열을 출력하게 된다.


◆ 상태를 갖는 컴포넌트


클래스 내에서 this.props를 이용하여 접근 가능한 입력 데이터 외에도 this.state로 접근 가능한 
내부 상태 데이터도 관리할 수 있다. 컴포넌트의 상태가 변경되면 render() 함수를 다시 호출하여 
렌더링 된 마크업 문서를 업데이트하게 된다.


class Timer extends React.Component {
 		constructor(props) {
				super(props);
				this.state = {secondsElapsed: 0};
		 }

 		tick() {
				this.setState((prevState) => ({
	  				secondsElapsed: prevState.secondsElapsed + 1
				}));
		  }

 		componentDidMount() {
				this.interval = setInterval(() => this.tick(), 1000);
		 }

 		componentWillUnmount() {
				clearInterval(this.interval);
 		}

 		render() {
				return (
	  				<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
				);
		 }
}

ReactDOM.render(<Timer />, mountNode);


Component의 lifecycle에 대해서는 추후 자세히 알아보기로 하고 여기서는 간단하게 흐름만 알아보도록 하자

1) 우선 constructor에서 이 컴포넌트의 상태를 나타낼 this.state에 0값으로 설정된 secondsElapsed라는 
요소를 할당하고 있다.


2) render() 함수가 호출된다.


3) Component의 lifecycle에 의해 componentDidMount가 호출되며 이 안에서 자바스크립트의 
setInterval 함수를 통해 1초에 한 번씩 Timer 클래스에 구현된 tick()함수를 호출한다.


4) tick()함수에서는 이전 상태 값을 파라미터로 전달받아 여기에 1을 더한 후 this.state에 할당된 seconds-
Elapsed에 값을 새로 설정한다.


이러한 과정을 거쳐서 변경된 this.state만을 새롭게 렌더링하여 화면에 보여주게 된다.


◆ Application


props와 state를 이용하여 만든 ToDo Application이다. 이 예제에서는 state를 이용하여 사용자가 
입력한 텍스트 뿐만 아니라 항목들의 목록을 추적하여 관리한다. 이러한 과정에서 event handler는 
마치 inline으로 렌더링 되는 것 처럼 보이지만 실제로는 event delegation을 이용하여 수집되고 
구현된다.


class TodoApp extends React.Component {
	constructor(props) {
			super(props);
			this.handleChange = this.handleChange.bind(this);
			this.handleSubmit = this.handleSubmit.bind(this);
			this.state = {items: [], text: ''};
	}

 		render() {
				return (
	  			<div>
					<h3>TODO</h3>
					<TodoList items={this.state.items} />
					<form onSubmit={this.handleSubmit}>
		  			<input onChange={this.handleChange} value={this.state.text} />
		  			<button>{'Add #' + (this.state.items.length + 1)}</button>
					</form>
	  			</div>
  			 );
 		}

 		handleChange(e) {
				this.setState({text: e.target.value});
		}

 		handleSubmit(e) {
				e.preventDefault();
				var newItem = {
	  				text: this.state.text,
	 				 id: Date.now()
				};
				this.setState((prevState) => ({
	  				items: prevState.items.concat(newItem),
	  				text: ''
				}));
		 }
	}

	class TodoList extends React.Component {
 		render() {
  			return (
	  			<ul>
	   			{this.props.items.map(item => (
		  				<li key={item.id}>{item.text}</li>
					))}
	  			</ul>
				);
 		}
	}

	ReactDOM.render(<TodoApp />, mountNode);


앞서 상태를 갖는 컴포넌트에서와 같이 흐름을 살펴보도록 하자.

1) TodoApp 클래스에서는 생성자에서 이벤트를 처리할 두 함수인 handleChange와 handleSubmit을
this. handleChange와 this. handleSubmit에 할당한다. this.state에는 items라는 배열과 text라는
문자열로 구성된 object를 할당한다.


2) handleChange 함수는 event 객체를 인자로 전달받아 this.state 중 text에 event target에 입력된
값을 할당한다.


3) handleSubmit에서는 handleChange에서 this.state에 할당한 text 값과 날짜를 기반으로 한 id
값을 이용하여 새로운 item을 생성하고 이 item을 this.state의 items 배열에 추가(concat)한 후
setState 호출로 인해 새로 렌더링이 실행된다.


4) TodoList 클래스는 render 함수만 정의되어있으며 파라미터로 전달받은 item 객체를 이용하여 
li 리스트를 만든다.


ReactDOM.render 함수가 호출되면 1)에서 초기화가 진행되고 TodoApp 클래스의 render() 함수가
호출되는데 render 함수 내에서는 먼저 this.state 중 items 배열을 파라미터로 하여 TodoList 클래스를
렌더링한다. 즉, 4)의 과정이 실행된다. 이어서 input 폼에는 onChange 이벤트 발생 시 handleChange
함수를 호출한다. 2)의 과정이 실행되는 것이다. input의 value는 최초 상태 또는 item이 등록된 후에는
This.state.text 값이 ‘’로 설정되므로 비어있게 될 것이다. 버튼에는 this.state의 items 배열을 참조하여
현재까지 등록된 item + 1의 수가 표시된다. 마지막으로 Add 버튼을 클릭하면 handleSubmit 함수가
호출되면서 3)의 과정이 진행된다.


◆ 컴포넌트에서 외부 플러그인 사용


React는 컴포넌트에서 외부 라이브러리나 프레임워크와 인터페이스 할 때 유연할 뿐만 아니라 hooks를
제공하고 있다. 아래 예제는 Markdown 라이브러리인 remarkable을 이용하여 textarea의 text를
실시간으로 변환하는 예제이다.


class MarkdownEditor extends React.Component {
	constructor(props) {
			super(props);
			this.handleChange = this.handleChange.bind(this);
			this.state = {value: 'Type some *markdown* here!'};
	}

 		handleChange() {
				this.setState({value: this.refs.textarea.value});
 		}

 		getRawMarkup() {
				var md = new Remarkable();
				return { __html: md.render(this.state.value) };
 		}

 		render() {
				return (
	  				<div className="MarkdownEditor">
					<h3>Input</h3>
					<textarea
		  				onChange={this.handleChange}
		  				ref="textarea"
		  				defaultValue={this.state.value} />
					<h3>Output</h3>
					<div
		  				className="content"
		  				dangerouslySetInnerHTML={this.getRawMarkup()}
					/>
	  				</div>
				);
 		}
	}

ReactDOM.render(<MarkdownEditor />, mountNode);


마찬가지로 흐름을 살펴보면

1) MarkdownEditor의 생성자에서 handleChange를 bind하고 this.state에는 value라는 키의 문자열
값을 할당한다.


2) handleChange 함수에서는 textarea의 값을 받아 setState를 통해 상태를 변화시키고 다시 렌더링한다.


3) getRawMarkup 함수에서는 remarkable 라이브러리 객체를 생성하고 this.state.value에 저장된
문자열을 remarkable 객체를 이용하여 렌더링한 후 리턴한다.


ReactDOM.render가 호출되면 1)의 초기화가 진행되고 render 함수가 호출되면 textarea의 onChange 
이벤트 처리를 통해 내용이 변경될 때마다 handleChange 함수를 호출하고 this.state를 변경한다. 
content라는 클래스명을 가진 div에서는 dangerouslySetInnerHTML을 통해 this.state에 할당된
값을 화면에 출력하는데 이 때 getRawMarkup() 함수를 호출한다. 즉, 3)의 과정을 거친 내용이 화면에
출력된다.

'Study > React' 카테고리의 다른 글

React 살펴보기 #6  (0) 2017.03.27
React 살펴보기 #5  (0) 2017.03.04
React 살펴보기 #4  (0) 2017.02.17
React 살펴보기 #3  (0) 2017.02.11
React 살펴보기 #2  (0) 2017.02.06
React 살펴보기 #1  (0) 2017.02.06
블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요



React 개요


지난 시간까지 mongoDB 모델링을 가볍게, 아~주 가볍게 살펴보았다.

그리고 몇차례에 걸쳐 수정했으나 여전히 변경의 여지가 남아있는 찜찜한 상태로 글을 마쳤다.

일단 현 상태로도 정상적으로 기능은 하기에 mongoDB에 대한 깊은 내용은 좀 더 뒤로 미루고

코드가 더 복잡해지기 전에 js쪽 소스들을 React로 conversion 하고자 한다.


최초 Till60 개발에 대한 글을 작성했을 때도 언급했듯이 React 역시 처음 다뤄보는 기술이다.

그런 만큼 우선은 공식 홈페이지에서 개요라도 집고 넘어가야 할 것 같아 우선 오늘은 공식

홈페이지의 메인페이지 내용을 정리해보고자 한다.


1. 기능 목표


기능적으로는 추가 개발 내용이 없다.


2. 기술 목표


기존에 jQuery와 Bootstrap으로 개발된 코드를 React로 conversion.

우선 이번에는 React에 대해 기본 개념을 잡고 다음부터 Sign In, Sign Up 등의 가벼운 화면 작업을

시작하도록 한다.


3. 기술 적용 내용


아래 내용은 React 공식 홈페이지의 메인 페이지 내용을 의역한 것이다.


————————————————————————————————————————————————


1. React는 UI의 상호작용을 보다 쉽게 처리할 수 있는데, 변경된 데이터와 관련된 내용만을 업데이트하고

렌더링 하는 방식으로 이를 효율적으로 수행한다. 이러한 방식은 결국 예측 가능한 코드를 만들 수 있게

하고 따라서 디버깅이 쉬워진다.


2. 자신의 상태를 관리할 수 있는 캡슐화된 컴포넌트를 만듦으로써 복잡한 UI를 쉽게 구성할 수 있으며

이러한 로직을 자바스크립트로 작성하기 때문에 다양한 데이터 형식을 쉽게 전달할 수 있고 또 DOM

에 의존하지 않고 상태를 유지할 수 있다.


3. React는 특정한 기술 스택에 의존하지 않고 React 내에서 새로운 기능들을 개발 할 수 있도록 구현

되었다. 또한 React Native를 이용하면 Node를 이용하여 서버측에 렌더링 하거나 강력한 모바일

앱에 렌더링 하는 것도 가능하다.


————————————————————————————————————————————————


  • 간단한 컴포넌트


React의 기본은 입력 데이터를 전달받아 화면에 표시해주는 render()함수를 구현하는 것이다.

아래 코드는 XML 구문과 유사한 JSX를 이용하여 구현된 코드이다. 하지만 JSX 사용이 필수적인

것은 아니다.


class HelloMessage extends React.Component {

  render() {

    return <div>Hello {this.props.name}</div>;

  }

}


ReactDOM.render(<HelloMessage name="Jane" />, mountNode);

위의 코드에서 눈여겨 봐야 할 것은 HelloMessage라는 클래스 이름과 ReactDOM.render의 호출이다.

ReactDOM.render의 호출을 통해 비로소 화면에 데이터가 출력되는데 이 함수 안에서 HelloMessage

클래스가 어떻게 사용되고 있는지 또 그 안에 name이라는 property가 어떻게 HelloMessage 클래스로

전달되는지(ReactDOM.render 함수의 파라미터에 있는 name은 this.props.name이라는 형태로

HelloMessage 클래스 안으로 전달된다)를 주의해서 보자. 이 코드의 결과는 브라우저 화면에 “Hello Jane”

이라는 문자열을 출력하게 된다.


  • 상태를 갖는 컴포넌트


클래스 내에서 this.props를 이용하여 접근 가능한 입력 데이터 외에도 this.state로 접근 가능한 내부 상태

데이터도 관리할 수 있다. 컴포넌트의 상태가 변경되면 render() 함수를 다시 호출하여 렌더링 된 마크업

문서를 업데이트하게 된다.


class Timer extends React.Component {

  constructor(props) {

    super(props);

    this.state = {secondsElapsed: 0};

  }


  tick() {

    this.setState((prevState) => ({

      secondsElapsed: prevState.secondsElapsed + 1

    }));

  }


  componentDidMount() {

    this.interval = setInterval(() => this.tick(), 1000);

  }


  componentWillUnmount() {

    clearInterval(this.interval);

  }


  render() {

    return (

      <div>Seconds Elapsed: {this.state.secondsElapsed}</div>

    );

  }

}


ReactDOM.render(<Timer />, mountNode);


Component의 lifecycle에 대해서는 추후 자세히 알아보기로 하고 여기서는 간단하게 흐름만 알아보도록 하자


1) 우선 constructor에서 이 컴포넌트의 상태를 나타낼 this.state에 0값으로 설정된 secondsElapsed라는 

    요소를 할당하고 있다.

2) render() 함수가 호출된다.

3) Component의 lifecycle에 의해 componentDidMount가 호출되며 이 안에서 자바스크립트의 setInterval

    함수를 통해 1초에 한 번씩 Timer 클래스에 구현된 tick()함수를 호출한다.

4) tick()함수에서는 이전 상태 값을 파라미터로 전달받아 여기에 1을 더한 후 this.state에 할당된 seconds-

    Elapsed에 값을 새로 설정한다.


이러한 과정을 거쳐서 변경된 this.state만을 새롭게 렌더링하여 화면에 보여주게 된다.


  • Application


props와 state를 이용하여 만든 ToDo Application이다. 이 예제에서는 state를 이용하여 사용자가 입력한

텍스트 뿐만 아니라 항목들의 목록을 추적하여 관리한다. 이러한 과정에서 event handler는 마치 inline으로 

렌더링 되는 것 처럼 보이지만 실제로는 event delegation을 이용하여 수집되고 구현된다.


class TodoApp extends React.Component {

  constructor(props) {

    super(props);

    this.handleChange = this.handleChange.bind(this);

    this.handleSubmit = this.handleSubmit.bind(this);

    this.state = {items: [], text: ''};

  }


  render() {

    return (

      <div>

        <h3>TODO</h3>

        <TodoList items={this.state.items} />

        <form onSubmit={this.handleSubmit}>

          <input onChange={this.handleChange} value={this.state.text} />

          <button>{'Add #' + (this.state.items.length + 1)}</button>

        </form>

      </div>

    );

  }


  handleChange(e) {

    this.setState({text: e.target.value});

  }


  handleSubmit(e) {

    e.preventDefault();

    var newItem = {

      text: this.state.text,

      id: Date.now()

    };

    this.setState((prevState) => ({

      items: prevState.items.concat(newItem),

      text: ''

    }));

  }

}


class TodoList extends React.Component {

  render() {

   return (

      <ul>

       {this.props.items.map(item => (

          <li key={item.id}>{item.text}</li>

        ))}

      </ul>

    );

  }

}


ReactDOM.render(<TodoApp />, mountNode);


앞서 상태를 갖는 컴포넌트에서와 같이 흐름을 살펴보도록 하자.


1) TodoApp 클래스에서는 생성자에서 이벤트를 처리할 두 함수인 handleChange와 handleSubmit을

    this. handleChange와 this. handleSubmit에 할당한다. this.state에는 items라는 배열과 text라는

    문자열로 구성된 object를 할당한다.

2) handleChange 함수는 event 객체를 인자로 전달받아 this.state 중 text에 event target에 입력된

    값을 할당한다.

3) handleSubmit에서는 handleChange에서 this.state에 할당한 text 값과 날짜를 기반으로 한 id

    값을 이용하여 새로운 item을 생성하고 이 item을 this.state의 items 배열에 추가(concat)한 후

    setState 호출로 인해 새로 렌더링이 실행된다.

4) TodoList 클래스는 render 함수만 정의되어있으며 파라미터로 전달받은 item 객체를 이용하여 

    li 리스트를 만든다.


ReactDOM.render 함수가 호출되면 1)에서 초기화가 진행되고 TodoApp 클래스의 render() 함수가

호출되는데 render 함수 내에서는 먼저 this.state 중 items 배열을 파라미터로 하여 TodoList 클래스를

렌더링한다. 즉, 4)의 과정이 실행된다. 이어서 input 폼에는 onChange 이벤트 발생 시  handleChange

함수를 호출한다. 2)의 과정이 실행되는 것이다. input의 value는 최초 상태 또는 item이 등록된 후에는

This.state.text 값이 ‘’로 설정되므로 비어있게 될 것이다. 버튼에는 this.state의 items 배열을 참조하여

현재까지 등록된 item + 1의 수가 표시된다. 마지막으로 Add 버튼을 클릭하면 handleSubmit 함수가

호출되면서 3)의 과정이 진행된다.


  • 컴포넌트에서 외부 플러그인 사용


React는 컴포넌트에서 외부 라이브러리나 프레임워크와 인터페이스 할  때 유연할 뿐만 아니라 hooks를

제공하고 있다. 아래 예제는 Markdown 라이브러리인 remarkable을 이용하여 textarea의 text를

실시간으로 변환하는 예제이다.


class MarkdownEditor extends React.Component {

  constructor(props) {

    super(props);

    this.handleChange = this.handleChange.bind(this);

    this.state = {value: 'Type some *markdown* here!'};

  }


  handleChange() {

    this.setState({value: this.refs.textarea.value});

  }


  getRawMarkup() {

    var md = new Remarkable();

    return { __html: md.render(this.state.value) };

  }


  render() {

    return (

      <div className="MarkdownEditor">

        <h3>Input</h3>

        <textarea

          onChange={this.handleChange}

          ref="textarea"

          defaultValue={this.state.value} />

        <h3>Output</h3>

        <div

          className="content"

          dangerouslySetInnerHTML={this.getRawMarkup()}

        />

      </div>

    );

  }

}


ReactDOM.render(<MarkdownEditor />, mountNode);


마찬가지로 흐름을 살펴보면


1) MarkdownEditor의 생성자에서 handleChange를 bind하고 this.state에는 value라는 키의 문자열

    값을 할당한다.

2) handleChange 함수에서는 textarea의 값을 받아 setState를 통해 상태를 변화시키고 다시 렌더링한다.

3) getRawMarkup 함수에서는 remarkable 라이브러리 객체를 생성하고 this.state.value에 저장된

    문자열을 remarkable 객체를 이용하여 렌더링한 후 리턴한다.

ReactDOM.render가 호출되면 1)의 초기화가 진행되고 render 함수가 호출되면 textarea의 onChange 

이벤트 처리를 통해 내용이 변경될 때마다 handleChange 함수를 호출하고 this.state를 변경한다. 

content라는 클래스명을 가진 div에서는 dangerouslySetInnerHTML을 통해 this.state에 할당된

값을 화면에 출력하는데 이 때 getRawMarkup() 함수를 호출한다. 즉, 3)의 과정을 거친 내용이 화면에

출력된다.


4. 참조 (site 또는 서적)


React 공식 홈페이지 : https://facebook.github.io/react/


5. 확인 사항 (의문 사항)


실제 HTML과 jQuery만으로 구현된 웹 페이지를 React로 conversion하면서 좀 더 심화된

내용 확인이 필요하다.


6. 평가


오늘은 간단히 React 홈페이지의 메인 페이지에 있는 4가지 예제을 살펴보았다. 아직은 그야말로

맛보기 정도에 불과하지만 그래도 대략 감을 잡기에는 부족하지 않은 내용이었다. 앞으로 현재까지

진행된 Till60 홈페이지를 React로 conversion하면서 보다 깊은 내용을 확인해보도록 하겠다.

블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요