컴포넌트는 UI를 독립적으로 쪼개고, 조각을 재사용하고, 각각의 조각을 격리시킨다. 이 장에서는 컴포넌트의 개념을 소개한다. 컴포넌트 API 에 관한 참조글은 여기서 볼 수 있다.
개념적으로, 컴포넌트는 자바스크립트 함수와 같다. 컴포넌트들은 ("프로퍼티" 라고 하는) 임의의 값들을 받는다. 그리고 화면에 보여야 하는 것을 묘사한 리엑트 엘리먼트를 리턴한다.
함수 컴포넌트, 클래스 컴포넌트
컴포넌트를 정의하는 가장 간단한 방법은 자바스크립트 함수를 사용하는 것이다:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
이 함수는 유효한 리엑트 컴포넌트이다. 왜냐하면 데이터를 담고있는 하나의 "props"(프로퍼티들로 구성된) 객체 아규먼트를 받고, 리엑트 엘리먼트를 리턴한다. 이런 컴포넌트를 "함수 컴포넌트" 라고 부른다. 왜냐면 말 그대로 자바스크립트 함수이기 때문이다.
컴포넌트를 정의할 때 ES6 class를 사용할 수도 있다:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
위 두 개의 컴포넌트는 리엑트의 시점에서 동일하다.
함수와 클래스 컴포넌트는 둘 다 다음장에서 다룰 추가적인 기능들이 있다.
컴포넌트 렌더링 하기
이전에, 돔 태그를 나타내는 리엑트 엘리먼트만 다루었다:
const element = <div />;
하지만, 엘리먼트는 유저가 정의한 컴포넌트를 나타낼 수도 있다:
const element = <Welcome name="Sara" />;
리엑트가 유저가 정의한 컴포넌트를 나타내는 엘리먼트를 다룰 때는, JSX 애트리뷰트와 자식들을 컴포넌트에 하나의 객체로 보낸다. 이 객체를 "props"라고 한다.
예를 들어, 이 코드는 페이지에 "Hello, Sara"를 렌더한다:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);
이 예제에서 무슨일이 일어나는 지 살펴보자:
- <Welcome name="Sara" /> 엘리먼트로 root.render()를 호출한다.
- 리엑트는 {name: 'Sara'}를 props로 Wlecome Component를 호출한다.
- Welcom 컴포넌트는 결과로 <h1>Hello, Sara</h1> 엘리먼트를 리턴한다.
- 리엑트 돔은 <h1>Hello, Sara</h1>에 매칭되도록 돔을 효율적으로 업데이트 한다.
Note: 항상 컴포넌트 이름은 대문자로 시작한다.
리엑트는 소문자로 시작하는 컴포넌트는 돔 태그로 다룬다. 예를 들어 <div />는 HTML div 태그를 의미한다. 하지만, <Welcome />은 컴포넌트를 나타내고, Welcome은 import하거나 해서 범위안에 있어야 한다.
이 규칙이 만들어진 이유에 대해 궁금하면 JSX In Depth를 읽어보라.
컴포넌트 구성하기
컴포넌트는 아웃풋에서 다른 컴포넌트를 참조할 수 있다. 이건 모든 상세의 레벨에서 같은 컴포넌트 추상화를 사용하도록 해준다. 버튼, 폼, 다이어로그, 화면: 리엑트 앱에 있는, 모든 것들은 컴포넌트로 표현한다.
예를 들어, 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>
);
}
전형적으로, 새로운 리엑트 앱은 최 상위에 하나의 앱 컴포넌트를 갖는다. 하지만, 리엑트를 기존 앱에 통합한다면, 버튼과 같은 작은 컴포넌트부터 시작해서 점진적으로 뷰 계층의 위로 올라갈 것이다.
컴포넌트 추출하기
컴포넌트를 작은 컴포넌트로 쪼개는걸 두려워하지 마라.
예를 들어, 이 Comment 컴포넌트를 보자:
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는 댓글 안에서 렌더링 된다는 것을 알 필요가 없다. prop에 주어진 이름이 좀더 일반적이기 때문이다: author보다는 user를 사용했다.
사용된 곳의 문맥보다는 컴포넌트의 화면에서의 특징으로 프로퍼티들을 네이밍하는 것을 추천한다.
이제 댓글을 약간 더 간소화 시켰다:
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의 일부분이 여러번 사용되거나 (버튼, 패널, 아바타) 또는 자체만으로도 충분히 복잡한 경우가 (앱, FeedStrory, Comment 등), 별도의 컴포넌트로 추출될 수 있는 좋은 예이다.
읽기 전용 Props
컴포넌트를 함수로 선언하든 클래스로 선언하든, 스스로의 props를 절대 수정할 일이없다. 아래 sum 함수를 보자:
function sum(a, b) {
return a + b;
}
이런 함수를 "순수한 함수 (pure)"라고한다. 왜냐하면 input값을 바꾸려는 시도를 하지 않기 때문이다. 그리고 항상 같은 input값에 같은 결과를 리턴한다.
대조적으로, 이 함수는 순수하지 않은 함수이다. 왜냐하면, input을 바꾸기 때문이다:
function withdraw(account, amount) {
account.total -= amount;
}
리엑트는 꽤 유연하지만 한가지 엄격한 규칙이 있다 :
모든 리엑트 컴포넌트는 props를 존중하며 순수한 함수처럼 동작한다
물론, 애플리케이션 UI는 동적이고 매 순간 변화한다. 다음 장에서, 새로운 개념인 "상태" 를 소개한다. 상태는 리엑트 컴포넌트가 유저 액션, 네트워크 응답, 또는 다른 모든것에 이 룰을 해치지않으면서 아웃풋을 바꾸도록 해준다.
'front > react' 카테고리의 다른 글
[react]Rendering Element (0) | 2022.08.12 |
---|---|
[react] Introducing JSX (0) | 2022.08.11 |
[react] Hello World (0) | 2022.08.08 |
댓글