컴포넌트(Component)란?
-기능을 단위별로 캡슐화하는 리액트의 기본 단위
-기본적으로 자바스크립트 함수 혹은 클래스
-속성들을 입력으로 받아들이며 내부적으로 각자의 상태를 관리
-리액트로 작성된 클라이언트 측 애플리케이션을 구성하는 기본 단위
-요소를 그룹화하는 방법
쉽게 말해, UI들을 그룹화하여 반복적으로 재정의할 수 있도록 사용하는 것

사용할 리액트 라이브러리
react |
리액트 코어 라이브러리 |
react-dom |
리액트를 위한 렌더러(코드의 결과를 브라우저나 다른 플랫폼에 렌더링하는 기능을 제공하는 라이브러리) 컴포넌트를 DOM에 렌더링하거나 서버 측 렌더링을 위해 문자열로 렌더링하는 기능을 제공 |
prop-types |
컴포넌트로 전달되는 데이터에 대한 타입 검사 등의 기능을 제공 (validator) |
실제로 브라우저에서 뭔가를 보기 위해서는 react-dom을 이용해야 한다.
즉, 컴포넌트를 생성하고 관리하기 위해서는 리액트의 render 메소드를 호출해서 컴포넌트와 컴포넌트 요소를 렌더링해야 한다.
ReactDom의 render 메소드
ReactDOM.render(
ReactElement element,
DOMElement container,
[function callback]
) -> ReactComponent
여기서 ReactElement는 리액트 요소를 나타내고, DOMElement는 DOM객체(document.getElementById('root'))를 뜻한다.
리액트 요소란?
-경량이고 상태가 없으며 내부 상태의 변경이 불가능한 요소
-리액트 요소는 크게 ReactComponentElement 타입과 ReactDOMElement타입 2가지가 존재
-ReactDOMElement는 DOM요소를 가상으로 표현한 객체
-ReactComponentElement는 리액트 컴포넌트를 표현하는 함수나 클래스에 대한 참조를 의미
-리액트 컴포넌트를 렌더링 할 것인지, DOM 요소를 렌더링할 것인지를 알려준다.
-리액트는 리액트 요소를 이용해 UI를 구성한다.
리액트 요소는 React.createElement 메소드를 이용해 생성할 수 있다.
React.createElement(
String/ReactClass type,
[object props],
[children...]
) -> ReactElement
React.createElement() 메소드는 문자열이나, 컴포넌트, props객체, 자식 컴포넌트들을 인수로 전달받아 리액트 요소를 리턴한다.
type
- 생성할 HTML 요소의 태그 이름(div, p..)을 문자열로 전달하거나 리액트 클래스를 전달
props
- HTML 요소에 지정될 특성을 지정하거나 컴포넌트 클래스 인스턴스에 사용할 속성들을 지정한다.
children
- 이 매개변수를 통해 컴포넌트를 조합할 수 있다.
- 순서대로 해당 컴포넌트를 중첩하거나 다른 리액트 요소를 중첩할 수 있다.
- 또는 HTML요소에 출력될 텍스트를 전달할 수도 있다.
한마디로, '무엇을 생성할지', '어떻게 구성할지', '어떤 것을 포함시킬지'를 입력한다.
*React.createElement 함수 사용 예
//React, ReactDOM 라이브러리 import
import React from "react";
import { render } from "react-dom";
const node = document.getElementById("root");
const root = React.createElement(
"div",
{},
React.createElement(
"h1",
{},
"Hello, world!",
React.createElement(
"a",
{ href: "mailto:mark@ifelse.io" },
React.createElement("h1", {}, "React In Action"),
React.createElement("em", {}, "...and now it really is!")
)
)
);
render(root, node);
*결과

리액트 요소의 특징
- 문자열을 이용해 (div, a, p...) DOM 요소를 생성한다.
- props 객체를 이용해 리액트의 요소를 설정할 수 있다. DOM 요소들에 지정할 수 있는 특성과 유사하다.
- 리액트 요소는 중첩할 수 있다.
- 리액트는 리액트 요소를 이용해 가상 DOM을 구성하며, React-dom 라이브러리는 가상 DOM을 이용해 브라우저 DOM을 갱신한다.
- 리액트 요소는 리액트에서 컴포넌트를 구성하기 위한 기본 단위이다.
리액트 클래스 생성
- 리액트 컴포넌트는 React.Component 기반 클래스를 확장하여서 생성할 수도 있다.
class MyReactclassComponent extends Component {
render() {}
}
React.Component 추상 기반 클래스를 상속하는 자바스크립트 클래스를 선언하여 클래스를 기반으로 컴포넌트를 생성할 수 있다.
여기서 render 메소드는 하나의 리액트 요소 또는 리액트 요소 배열을 리턴한다.
render 메소드
화면에 무언가를 출력하는 컴포넌트라면 반드시 render 메소드를 정의해야한다.
render 메소드는 리액트 요소를 리턴한다는 점에서 리액트 요소를 생성하는것과 매우 유사하지만, 리액트 요소와 달리 내부데이터(컴포넌트 내부에 저장된 상태) 뿐만 아니라 컴포넌트 메소드와 React.Component 추상 기반 클래스로 부터 상속된 추가 메소드에도 접근이 가능하다.
또한 리액트 클래스는 리액트 요소와는 달리 props 객체와 같은 보조 인스턴스를 생성한다.
props 객체는 컴포넌트에 전달할 수 있는 데이터이며, 해당 컴포넌트의 자식 컴포넌트에도 전달할 수 있다.
props 객체를 이용해 컴포넌트를 생성하는 시점에 컴포넌트의 속성(props)를 지정할 수 있다.
#render 메소드 사용법 예시
import React, { Component } from "react";
import { render } from "react-dom";
import PropTypes from "prop-types";
const node = document.getElementById("root");
//Post 컴포넌트로 사용할 리액트 클래스 생성
class Post extends Component {
render() {
return React.createElement(
"div",
{
className: "post"
},
React.createElement(
"h2",
{
className: "postAuthor",
id: this.props.id
},
//여기서 this는 리액트 클래스가 아닌 컴포넌트 인스턴스(h2)를 뜻한다
this.props.user,
React.createElement(
"span",
{
className: "postBody"
},
this.props.content
)
)
);
}
}
Post.propTypes = {
user: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
};
//createElement 메소드에 Post 리액트 클래스와 필요한 속성들을 전달
const App = React.createElement(Post, {
id: 1, //#H
content: " said: This is a post!",
user: "mark"
});
//React-dom 라이브러리가 App컴포넌트를 렌더링한다.
render(App, node);
#중첩된 컴포넌트 추가 예시
import React, { Component } from "react";
import { render } from "react-dom";
import PropTypes from "prop-types";
const node = document.getElementById("root");
class Post extends Component {
render() {
return React.createElement(
"div",
{
className: "post"
},
React.createElement(
"h2",
{
className: "postAuthor",
id: this.props.id
},
this.props.user,
React.createElement(
"span",
{
className: "postBody"
},
this.props.content
),
//자식 컴포넌트들을 렌더링할 수 있도록 children 속성을 컴포넌트에 추가
this.props.children
)
);
}
}
Post.propTypes = {
user: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
id: PropTypes.number.isRequired
};
//Post 컴포넌트와 동일한 방법으로 Comment 컴포넌트 생성
class Comment extends Component {
render() {
console.log("yo");
return React.createElement(
"div",
{
className: "comment"
},
React.createElement(
"h2",
{
className: "commentAuthor"
},
this.props.user,
React.createElement(
"span",
{
className: "commentContent"
},
this.props.content
)
)
);
}
}
Comment.propTypes = {
id: PropTypes.number.isRequired,
content: PropTypes.string.isRequired,
user: PropTypes.string.isRequired
};
const App = React.createElement(
Post,
{
id: 1,
content: " said: This is a post!",
user: "mark"
},
//Post 컴포넌트에 Comment 컴포넌트를 중첩
React.createElement(Comment, {
id: 2,
user: "bob",
content: " commented: wow! how cool!"
})
);
render(App, node);
#함수형 컴포넌트와 클래스형 컴포넌트의 차이
- 함수형 컴포넌트에는 state와 LifeCycle이 빠져있다.
- 때문에 컴포넌트 초기 마운트가 아주 미세하게 빠르고, 메모리 자원을 클래스형 컴포넌트보다 덜 사용한다.
* 미세한 차이이기 때문에, 컴포넌트를 무수히 많이 렌더링하는게 아니라면 큰 성능 차이는 없다.
리액트의 상태
#props 예제
MyName.js
import React, { Component } from 'react'
class MyName extends Component {
render() {
return (
React.createElement(
'div',
{},
'Hello, this is ',
React.createElement(
"b",
{},
this.props.name
)
)
);
}
}
export default MyName;
App.js
import React, { Component } from 'react';
import MyName from './MyName';
class App extends Component {
render() {
return (
React.createElement(
MyName,
{
name: "React!"
}
)
);
}
}
export default App;
부모 Element인 App에서 name이라는 props에 데이터를 지정하주고, 자식 Element인 MyName은 자신이 받아온 props값을 this.키워드를 사용해서 조회할 수 있다.
defaultProps
가끔 실수로 props를 빠트리는 경우나 비워둬야할 경우를 대비해서 props의 기본값을 설정해줄 수 있다.
MyName.js
import React, { Component } from 'react'
class MyName extends Component {
static defaultProps = {
name: 'Default Name!!'
}
render() {
return (
React.createElement(
'div',
{},
'Hello, this is ',
React.createElement(
"b",
{},
this.props.name
)
)
);
}
}
export default MyName;
App.js
import React, { Component } from 'react';
import MyName from './MyName';
class App extends Component {
render() {
return (
React.createElement(
MyName,
{
}
)
);
}
}
export default App;
컴포넌트의 상태 (state)
#state 예제
App.js
import React, { Component } from 'react';
import Counter from './Counter';
class App extends Component {
render() {
return (
React.createElement(Counter)
);
}
}
export default App;
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
//클래스 field에서 state 정의
state = {
number: 0
}
/*
생성자를 사용해 state 정의
constructor(props) {
super(props);
this.state = {
number: 0
}
}
*/
handleIncrease = () => {
this.setState({
number: this.state.number + 1
});
}
handleDecrease = () => {
this.setState({
number: this.state.number - 1
});
}
render() {
return (
React.createElement(
'div',
{},
React.createElement(
'h1',
{},
'카운터'
),
React.createElement(
'div',
{},
'값 : ',
this.state.number
),
React.createElement(
'button',
{ onClick: this.handleIncrease },
'+'
),
React.createElement(
'button',
{ onClick: this.handleDecrease },
'-'
),
)
);
}
}
export default Counter;
#Counter.js에서 constructor를 통해 state를 정의할때, super(props) 를 호출한 이유
- 컴포넌트를 만들면서, Component를 상속했기 때문에 그냥 constructor를 작성하게 되면 기존의 Component의 constuctor를 덮어쓰게 되므로, super를 통해 미리 실행하고 추가로 필요한 작업을 작성하는 것이다.
메소드
handleIncrease = () => {
this.setState({
number: this.state.number + 1
});
}
handleDecrease = () => {
this.setState({
number: this.state.number - 1
});
}
다른 형식 (화살표 함수를 사용하지 않는 방법)
handleIncrease() {
this.setState({
number: this.state.number + 1
});
}
handleDecrease() {
this.setState({
number: this.state.number - 1
});
}
이때 주의할 점으로, 클릭이벤트가 발생했을때, this가 undefined로 나타나서 제대로 처리되지 않는다.
함수가 클릭이벤트로 전달되는 과정에서 this와의 연결이 끊어지기 때문.
때문에 생성자(constructor)에서 다음과 같은 작업이 필요하다.
constructor(props) {
super(props);
this.handleIncrease = this.handleIncrease.bind(this);
this.handleDecrease = this.handleDecrease.bind(this);
}
- 클래스로 생성한 컴포넌트는 메소드를 자동으로 바인딩하지 않기 때문에, 생성자 내에서 메소드들을 직접 바인딩 해주어야 한다.
JSX
- JSX는 HTML, XML과 유사한 자바스크립트 확장 기능으로 컴포넌트를 더욱 쉽고 익숙한 방법으로 작성하는데 도움이 된다.
- JSX를 React.createElement 함수 대신 사용할 수 있다.
- 위의 state 예제에 JSX를 적용하면 다음과 같다.
App.js
import React, { Component } from 'react';
import Counter from './Counter';
class App extends Component {
render() {
return (
<Counter />
);
}
}
export default App;
Counter.js
import React, { Component } from 'react';
class Counter extends Component {
//클래스 field에서 state 정의
state = {
number: 0
}
/*
생성자를 사용해 state 정의
constructor(props) {
super(props);
this.state = {
number: 0
}
}
*/
handleIncrease = () => {
this.setState({
number: this.state.number + 1
});
}
handleDecrease = () => {
this.setState({
number: this.state.number - 1
});
}
render() {
return (
<div>
<h1>카운터</h1>
<div>값: {this.state.number}</div>
<button onClick={this.handleIncrease}>+</button>
<button onClick={this.handleDecrease}>-</button>
</div>
);
}
}
export default Counter;
JSX의 특징
감싸져 있는 엘리먼트
- 두개 이상의 엘리먼트는 무조건 하나의 엘리먼트로 감싸져잇어야한다.
- 간단한 해결방법 중 하나는 다음과 같이 div 태그로 감싸주는 것이다.
class App extends Component {
render() {
return (
<div>
<div>
Hello
</div>
<div>
Bye
</div>
</div>
);
}
}
- 단순히 감싸기 위해 div 태그를 사용하다가 스타일이 꼬인다거나 할 수 있기 때문에, Fragment 를 사용해 감싸줄 수도 있다.
(v16.2에 도입되었다고 한다.)
class App extends Component {
render() {
return (
<Fragment>
<div>
Hello
</div>
<div>
Bye
</div>
</Fragment>
);
}
}
JSX 안에서 자바스크립트 값을 사용할 경우
- 해당 변수 명을 중괄호로 묶어서 사용한다.
class App extends Component {
render() {
return (
<Fragment>
<div>
Hello {name}
</div>
<div>
Bye
</div>
</Fragment>
);
}
}
JSX의 style 설정
- HTML에서 style은 그냥 텍스트 형태로 작성했었지만, 리액트에서는 객체 형태로 작성해야 한다.
import React, { Component } from 'react';
class App extends Component {
render() {
const style = {
backgroundColor: 'black',
padding: '16px',
color: 'white',
fontSize: '12px'
};
return (
<div style={style}>
hi there
</div>
);
}
}
export default App;
camelCase (카멜 표기법)
- 리액트에서 이벤트 함수나 JSX 내부 속성값을 정의할때는 카멜 표기법을 사용해야 한다.
*camelCase (카멜 표기법) : 각 단어의 첫문자를 대문자로 표기하고 붙여쓰되, 맨처음 문자는 소문자로 표기