개념적으로 컴포넌트는 JavaScript 함수와 유사하다.
props
라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환한다.
컴포넌트 종류로 함수 컴포넌트
와 클래스 컴포넌트
두가지가 있다.
함수 컴포넌트와 클래스 컴포넌트
컴포넌트를 정의하는 가장 간단한 방법은 JavaScript 함수
를 작성하는 것이다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
이 함수는 데이터를 가진 하나의 props
(props는 속성을 나타내는 데이터다.) 객체 인자를 받은 후 React 엘리먼트
를 반환하므로 유효한 React 컴포넌트
다. 이러한 컴포넌트는 JavaScript 함수이기 때문에 말 그대로 함수 컴포넌트
라고 호칭한다.
또한 ES6 class
를 사용하여 컴포넌트를 정의할 수 있다.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
React의 관점에서 볼 때 위 두 가지 유형의 컴포넌트는 동일하다.
React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달합니다. 이 객체를
props
라고 한다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
<Welcome name="Sara" />
엘리먼트로 ReactDOM.render()를 호출한다.<h1>Hello, Sara</h1>
엘리먼트를 반환한다.<h1>Hello, Sara</h1>
엘리먼트와 일치하도록 DOM을 효율적으로 업데이트한다.주의: 컴포넌트의 이름은 항상 대문자로 시작한다.
React는 소문자로 시작하는 컴포넌트를 DOM 태그로 처리한다.
예를 들어 <div />
는 HTML div 태그를 나타내지만, <Welcome />
은 컴포넌트를 나타내며 범위 안에 Welcome이 있어야 한다.
컴포넌트는 자신의 출력에 다른 컴포넌트를 참조할 수 있다. 이는 모든 세부 단계에서 동일한 추상 컴포넌트를 사용할 수 있음을 의미한다. React 앱에서는 버튼
, 폼
, 다이얼로그
, 화면
등의 모든 것들이 흔히 컴포넌트
로 표현된다.
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')
);
일반적으로 새 React 앱은 최상위에 단일 App 컴포넌트
를 가지고 있다. 하지만 기존 앱에 React를 통합하는 경우에는 Button과 같은 작은 컴포넌트부터 시작해서 뷰 계층의 상단으로 올라가면서 점진적으로 작업해야 할 수 있다.
컴포넌트를 여러 개의 작은 컴포넌트로 나눌 수 있다.
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>
);
}
이 컴포넌트는 구성요소들이 모두 중첩 구조로 이루어져 있어서 변경하기 어려울 수 있으며, 각 구성요소를 개별적으로 재사용하기도 힘들다. 이 컴포넌트에서 몇 가지 컴포넌트를 추출하겠다.
먼저 Avatar
부터
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Avatar
는 자신이 Comment
내에서 렌더링 된다는 것을 알 필요가 없다.
따라서, props
의 이름을 author
에서 더욱 일반화된 user
로 변경했다.
이제 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
컴포넌트를 추출하겠다.
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>
);
}
재사용 가능한 컴포넌트를 만들어 놓는 것은 더 큰 앱에서 작업할 때 두각을 나타낸다. UI 일부가 여러 번 사용되거나 (Button, Panel, Avatar), UI 일부가 자체적으로 복잡한 (App, FeedStory, Comment) 경우에는
별도의 컴포넌트
로 만드는 게 좋다.
함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 된다.
function sum(a, b) {
return a + b;
}
이런 함수는 순수함수이다. 입력값을 바꾸지 않고 동일한 입력값에 대해 동일한 결과를 반환하기 때문이다.
반면에, 다음 함수는 자신의 입력값을 변경하기 때문에 순수 함수가 아니다.
function withdraw(account, amount) {
account.total -= amount;
}
React는 매우 유연하지만 한 가지 엄격한 규칙이 있다.
모든 React 컴포넌트는 자신의
props
를 다룰 때 반드시 순수 함수처럼 동작해야 한다.
React 컴포넌트는 state를 통해 위 규칙을 위반하지 않고 사용자 액션, 네트워크 응답 및 다른 요소에 대한 응답으로 시간에 따라 자신의 출력값을 변경할 수 있다.
이 엄격한 규칙을 지키기 위해 state
라는 개념을 배워울 것이다.