Integrate Meteor and React - 1

본 포스트는 Meteor 공식 홈페이지에 올라와 있는 TODO APP WITH REACT - Integrate Meteor and React을 순서대로 따라가는 도중에 궁금한 것들을 그때 그때 정리한 글입니다. Meteor와 React 초심자 입장에서 써내려 간 두서 없는 정리글입니다. meteor add react로 추가한 React 관련 패키지들이 아직 최신 버전으로 적용되지는 않는 것 같습니다.

React.render

React.render()는 최상위 컴포넌트의 인스턴스를 만들고, 두 번째 는인자로 전달받은 DOM 엘리먼트에 첫 번째 인자로 전달받는 컴포넌트를 삽입하여 프레임워크를 시작한다.

1
2
3
4
5
if (Meteor.isClient) {
Meteor.startup(function () {
React.render(<App />, document.getElementById("render-target"));
});
}

React.createClass

React에서는 새로운 React 컴포넌트를 만들기 위해 React.createClass()을 사용한다. view 컴포넌트들은 React.createClass()로 선언된 클래스다. React의 컴포넌트들은 render()를 제외하고는 어떠한 메소드도 가질 수 있다. render()는 React 컴포넌트 트리를 리턴해서 최종적으로 실제 HTML을 그린다.

1
2
3
4
5
6
7
8
9
10
11
App = React.createClass({
....
....

render() {
return (
<div></div>
...
);
}
});

render() 내에서 사용되는 <div>, <header> 등의 태그들은 실제 DOM 노드가 아니라 React의 div, header 컴포넌트의 인스턴스이다. 이것들은 React가 다룰 수 있는 데이터의 marker 정도로 생각하면 된다. JSX 컴파일러가 자동으로 HTML 태그들을 React.createElement(tagName) 표현식으로 변경하고 나머지는 그대로 둔다.
React의 컴포넌트는 composable한 성질을 갖는데, 이는 render()가 일반적인 HTML 뿐만 아니라 컴포넌트의 트리도 리턴하기 때문이다.

propTypes & React.PropTypes

React에서는 propTypes를 통해서 부모 컴포넌트로 부터 전달받은 데이터를 검증(validation)할 수 있다. propTypeskey/value의 형태로 이루어져 있는데, 전달받은 프로퍼티의 이름을 key로, 해당 프로퍼티의 타입을 value로 가진다. 반드시 전달받아야 하는 프로퍼티의 경우, .isRequired를 뒤에 붙여준다.

1
2
3
4
Task = React.createClass({
propTypes: {
task: React.PropTypes.object.isRequired
},

위 소스 코드에서, Task 컴포넌트는 부모 컨포넌트로 부터 task를 전달 받으며, task는 object 타입이여야 한다. 그리고 꼭 전달 받아야 하는 프로퍼티라는 것을 알 수 있다.

Warning: Failed propType: Invalid prop task of type object supplied to Task, expected string. Check the render method of App.

object 대신에 string을 넣었더니, 브라우져 콘솔창에 위와 같은 경고 메시지가 출력된다. task의 타입을 string으로 정했기 때문에 string으로 값이 올것이라고 예상했는데, 실제로는 object가 넘어왔다는 얘기이다. 이처럼 propTypesReact.PropTypes는 React의 타입을 검사하는 용도로 사용된다.

아래는 React의 타입을 검증하는데 사용될 수 있는 React.PropTypes의 목록이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
propTypes: {
// 특정 JavaScript 프리미티브 타입에 대한 prop을 명시할 수 있습니다.
// 기본적으로 이것들은 모두 선택적입니다.
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,

// 렌더링될 수 있는 모든 것: 숫자, 문자열, 요소
// 이것들을 포함하는 배열(이나 프래그먼트)
optionalNode: React.PropTypes.node,

// React 엘리먼트
optionalElement: React.PropTypes.element,

// 클래스의 인스턴스 또한 prop으로 명시할 수 있습니다. JavaScript의 instanceof
// 연산자를 사용합니다.
optionalMessage: React.PropTypes.instanceOf(Message),

// 열거형처럼 특정 값들로만 prop을 제한해서 사용할 수 있습니다.
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

// 많은 타입들 중 하나로 사용할 수 있는 객체가 될 수도 있습니다.
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),

// 특정 타입의 배열
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

// 특정 타입의 속성값을 갖는 객체
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

// 특정한 형태(shape)의 객체
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),

// 위에 언급된 것들을 `isRequired`로 연결해서 prop이 제공되지 않을 때 경고를
// 띄우도록 할 수도 있습니다.
requiredFunc: React.PropTypes.func.isRequired,

// 어떤 데이터 타입도 가능
requiredAny: React.PropTypes.any.isRequired,
}

this.props

컴포넌트들은 props라고 불리는 attribute를 통해서 부모로 부터 데이터를 전달 받을 수 있다. 부모 컴포넌트로 부터 받은 데이터는 자식 컴포넌트에서 ‘프로퍼티’로 사용가능 하다. 이 ‘프로퍼티들’은 this.props를 통해 접근할 수 있다. props를 사용해 App에서 전달받은 데이터를 읽어들이고, 마크업을 렌더할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 부모 컴포넌트
renderTasks() {
// Get tasks from this.data.tasks
return this.data.tasks.map((task) => {
return <Task key={task._id} task={task} />;
});
},
// 자식 컴포넌트
render() {
// Give tasks a different className when they are checked off,
// so that we can style them nicely in CSS
const taskClassName = this.props.task.checked ? "checked" : "";

return (
<li className={taskClassName}>
<button className="delete" onClick={this.deleteThisTask}>
&times;
</button>

<input
type="checkbox"
readOnly={true}
checked={this.props.task.checked}
onClick={this.toggleChecked} />

<span className="text">{this.props.task.text}</span>
</li>
);
}

this.refs

자식 컴포넌트의 이름을 지정하기 위해 ref 어트리뷰트를, 컴포넌트를 참조하기 위해 this.refs를 사용한다. native 브라우저 DOM 엘리먼트를 얻기 위해 React.findDOMNode(component)를 호출할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
handleSubmit(event) {
event.preventDefault();

var url = React.findDOMNode(this.refs.urlInput).value.trim();
},

render() {
return (
<div className="container">
<header>
<h1>Soozip.Site</h1>

<form className="new-link" onSubmit={this.handleSubmit} >
<input
type="text"
ref="urlInput"
placeholder="Type to add new links" />
</form>
</header>

<ul>
{this.renderLinks()}
</ul>
</div>
);
}

References

AngularJS application 테스트 Maps in ES6

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×