이 글은 React 공식 홈페이지 Docs 를 일부 번역한 글입니다.
컴포넌트는 UI를 독립적이고, 재사용이 가능한 조각들로 분리해준다.
개념적으로, 컴포넌트는 자바스크립트의 함수와 비슷하다. 임의의 인풋값(“props”라고 불리는)을 받아들이고 화면에 무엇이 표시되어야 하는지 설명되어있는 React elements를 반환한다.
함수와 클래스 컴포넌트
자바스크립트 함수로 가장 간단하게 정의한 컴포넌트는 다음과 같다:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
그리고 ES6의 class기능을 사용하여 다음과 같이 컴포넌트를 정의할 수도 있다:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
리액트 view의 관점에서 이 두 컴포넌트는 동일한 것이다.
컴포넌트 렌더링
이전에, DOM tag를 나타내주는 다음과 같은 React elements를 본 적이 있다:
const element = <div />;
elements는 이러한 형태 뿐만 아니라 사용자 정의 컴포넌트 또한 나타내줄 수 있다.
const element = <Welcome name="Sara" />;
이 때, React는 또한 컴포넌트에게 하나의 객체로 만들어진 JSX 속성들을 넘겨줄 수도 있다. 이것을 “props” 객체라고 부른다.
예를 들어, 아래의 코드는 페이지에 “Hello, Sara”를 렌더링해준다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
컴포넌트 구성
컴포넌트는 다른 컴포넌트를 참조할 수 있다. 어떤 단계의 항목이더라도 같은 컴포넌트를 사용할 수 있다. 버튼, 폼, 알림, 스크린 등 다양한 리액트 앱들은 공통적으로 컴포넌트로 표현되어진다.
일반적으로, 리액트 앱은 가장 상위에 App이라는 싱글 컴포넌트를 가지고 있다.
컴포넌트 추출
컴포넌트를 더 작은 컴포넌트로 분리하는 것을 두려워하지 않아도 된다.
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>
);
}
댓글을 나타내고 있는 이 컴포넌트는 author, text, date 를 props로 받고있다.
그런데 이 컴포넌트는 너무 뭉탱이로 되어있어서 뭔가를 바꿀 때 애매해질 수 있으며 각각을 독립적으로 재사용하기도 어려워보인다. 몇 가지 컴포넌트를 추출해보자.
첫째로, 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>
);
}
Props는 읽기전용!
함수든 클래스든 컴포넌트를 정의할 때는 절대 자기의 props를 수정하게 하면 안된다. 아래의 Sum
함수를 보자:
function sum(a, b) {
return a + b;
}
이 함수는 “pure”하다고 말할 수 있다. 왜냐하면 여기서 인자를 변경하려고 하지 않고, 항상 같은 값을 받아서 같은 값만을 반환하기 때문이다.
반대로, 이 함수는 인자를 변경하기 때문에 “impure”하다고 할 수 있다.
function withdraw(account, amount) {
account.total -= amount;
}
리액트는 엄청나게 유연하지만 하나의 엄격한 규칙을 가지고 있다:
모든 리액트 컴포넌트는 자신의 prop에 관하여서는 “pure”한 함수처럼 행동하여야 한다.