개념상 컴포넌트는 자바스크립트 함수와 비슷합니다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React 엘리먼트를 반환합니다.
컴포넌트를 정의하는 가장 간단한 방법은 자바스크립트 함수로 작성하는 것입니다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
이 함수는 단일 “props” (속성을 나타냄) 객체 인수를 받고 React 요소를 반환하기 때문에 유효한 React 컴포넌트입니다. 이러한 컴포넌트는 말 그대로 자바스크립트 함수이기 때문에 “함수형”이라고 부릅니다.
컴포넌트를 정의하기 위해 ES6 class 를 사용할 수도 있습니다.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
이전에는 DOM 태그를 나타내는 React 요소만 있었습니다.
const element = <div />;
그러나, 요소에 유저가 정의한 컴포넌트를 나타낼 수도 있습니다.
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')
);
Try it on CodePen.
1. <Welcome name="Sara" /> 요소로 ReactDOM.render() 를 호출합니다.
2. React가 {name: 'Sara'} 를 props로 하여 Welcome 컴포넌트를 호출합니다.
3. Welcome 컴포넌트가 그 결과로 <h1>Hello, Sara</h1> 요소를 반환합니다.
4. React DOM이 <h1>Hello, Sara</h1> 과 일치하도록 DOM을 효율적으로 업데이트합니다.
Caveat:
컴포넌트 이름은 항상 대문자를 사용해야 합니다.
예를 들어<div />는 DOM 태그를 나타내지만<Welcome />은 컴포넌트를 나타내며 스코프에Welcome을 필요로 합니다.
컴포넌트는 출력될 때 다른 컴포넌트를 참조할 수 있습니다. 이를 통해 모든 세부 레벨에서 동일한 컴포넌트 추상화를 사용할 수 있습니다. React 앱에서 버튼, 폼, 다이얼로그, 스크린 같은 것들은 모두 일반적으로 컴포넌트로 표현됩니다.
예를 들어, Welcome 을 여러번 렌더링하는 App 컴포넌트를 만들 수 있습니다.
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')
);
Try it on CodePen.
일반적으로, 새 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>
);
}
Try it on CodePen.
이 컴포넌트는 author (객체), text (문자열), date (date)를 props로 받고, 소셜 미디어 웹사이트의 덧글을 나타냅니다.
이 컴포넌트는 중첩 때문에 변경하기 까다로울 수 있으며, 각 파트를 다시 사용하기도 어렵습니다. 여기에서 몇가지 컴포넌트를 추출해봅시다.
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Avatar 는 Comment 내에서 렌더링되는 지 알 필요가 없습니다. 따라서 속성을 author 대신 user 라는 더 일반적인 이름을 사용합니다. 컴포넌트가 사용되는 상황이 아닌 컴포넌트 자체 관점에서 props 이름을 짓는 걸 권장합니다.
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>
);
}
Avartar 다음에 유저의 이름을 렌더링하는 UserInfo 컴포넌트를 추출
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
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>
);
}
Try it on CodePen.
컴포넌트를 추출하는 건 처음에는 쓸데없는 일처럼 보일 수 있지만 재사용 가능한 컴포넌트 팔레트를 사용하면 큰 앱에서 비용을 줄입니다. 좋은 규칙은 UI의 일부가 여러번 사용되거나 (Button, Panel, Avatar), 자체적으로 충분히 복잡하면서 (App, FeedStory, Comment), 재사용 가능한 컴포넌트가 될 후보들 입니다.
props 는 properties 의 줄임말입니다. 우리가 어떠한 값을 컴포넌트에게 전달해줘야 할 때, props 를 사용합니다.
함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안 됩니다.
function sum(a, b) {
return a + b;
}
이런 함수들은 순수 함수라고 호칭합니다. 입력값을 바꾸려 하지 않고 항상 동일한 입력값에 대해 동일한 결과를 반환하기 때문입니다.
반면에 다음 함수는 자신의 입력값을 변경하기 때문에 순수 함수가 아닙니다.
function withdraw(account, amount) {
account.total -= amount;
}
React는 매우 유연하지만 한 가지 엄격한 규칙이 있습니다.
모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 합니다.
예를 들어서, App 컴포넌트에서 Hello 컴포넌트를 사용 할 때 name 이라는 값을 전달해주고 싶다고 가정해봅시다. 그러면, 이렇게 코드를 작성하면 됩니다.
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" />
);
}
export default App;
import React from 'react';
function Hello(props) {
return <div>안녕하세요 {props.name}</div>
}
export default Hello;
컴포넌트에게 전달되는 props 는 파라미터를 통하여 조회 할 수 있습니다. props 는 객체 형태로 전달되며, 만약 name 값을 조회하고 싶다면 props.name 을 조회하면 됩니다.
Hello 컴포넌트에 또 다른 props 를 전달해봅시다.
import React from 'react';
import Hello from './Hello';
function App() {
return (
<Hello name="react" color="red"/> // color 값 생성
);
}
export default App;
그 다음에는, Hello 컴포넌트에서 color 값을 조회해서 폰트의 색상으로 설정을 해보겠습니다.
import React from 'react';
function Hello(props) {
return <div style={{ color: props.color }}>안녕하세요 {props.name}</div>
}
export default Hello;
props 내부의 값을 조회 할 때마다 props. 를 입력하고 있는데요, 함수의 파라미터에서 비구조화 할당 (혹은 구조 분해라고도 불립니다) 문법을 사용하면 조금 더 코드를 간결하게 작성 할 수 있습니다.
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
export default Hello;

컴포넌트에 props 를 지정하지 않았을 때 기본적으로 사용 할 값을 설정하고 싶다면 컴포넌트에 defaultProps 라는 값을 설정하면 됩니다.
import React from 'react';
function Hello({ color, name }) {
return <div style={{ color }}>안녕하세요 {name}</div>
}
Hello.defaultProps = {
name: '이름없음'
}
export default Hello;
import React from 'react';
import Hello from './Hello';
function App() {
return (
<>
<Hello name="react" color="red"/>
<Hello color="pink"/> // 이름 없이 렌더링
</>
);
}
export default App;
