React 개요
(이 글은 Till60 카테고리에 포함되었던 내용인데 글의 성격상 React 번역 부분만 따로 옮겨온
것입니다.)
아래 내용은 React 공식 홈페이지의 메인 페이지 내용을 의역한 것이다.
- React는 UI의 상호작용을 보다 쉽게 처리할 수 있는데, 변경된 데이터와 관련된 내용만을 업데이트
하고 렌더링 하는 방식으로 이를 효율적으로 수행한다. 이러한 방식은 결국 예측 가능한 코드를 만들
수 있게 하고 따라서 디버깅이 쉬워진다. - 자신의 상태를 관리할 수 있는 캡슐화된 컴포넌트를 만듦으로써 복잡한 UI를 쉽게 구성할 수 있으며
이러한 로직을 자바스크립트로 작성하기 때문에 다양한 데이터 형식을 쉽게 전달할 수 있고 또 DOM
에 의존하지 않고 상태를 유지할 수 있다.
- 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 |