[React] Components 와 Props

1. Components
리액트는 컴포넌트로 구성된다. 컴포넌트는 UI를 작은 조각으로 나눈 것으로, 이러한 컴포넌트들을 조합하여 전체 애플리케이션을 구성한다. 컴포넌트는 재사용이 가능한 API로 수많은 기능들을 내장하고 있으며, 컴포넌트 하나에서 해당 컴포넌트의 생김새와 작동 방식을 정의한다. 개념적으로는 자바스크립트 함수와 유사하지만 리액트 컴포넌트의 입력과 출력은 일반적인 자바스크립트와는 차이점이 있다.

리액트에서의 입력은 Props, 출력은 React element가 된다.
함수 컴포넌트와 클래스 컴포넌트
컴포넌트는 함수 컴포넌트와 클래스 컴포넌트로 나뉜다.
함수 컴포넌트
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
함수로 props 객체 인자를 받고 react element를 반환한다.
이런 컴포넌트는 js 함수이기 때문에 "함수 컴포넌트" 라고 부른다.
클래스 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
클래스 컴포넌트는 함수 컴포넌트와는 다르게 react.component를 상속받아서 만들어진다.
컴포넌트 렌더링
import React from "react";
import ReactDOM from "react-dom";
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
- 사용자 정의 컴포넌트에서 루트 돔 노드를 인자로 ReactDom.render() 함수가 호출된다.
- Welcome 이라는 함수에서 name:"Sara" 이라는 props 값을 호출하고, element가 생성된다.
- 생성된 엘리먼트를 루트 돔 노트에 넣어서 ReactDOM은 DOM을 구성하게 된다.
참고
컴포넌트를 만들때, 컴포넌트의 이름은 항상 대문자로 시작해야 한다.
리액트는 소문자로 시작하는 컴포넌트를 DOM 태그로 인식한다.
컴포넌트 합성
컴포넌트는 다른 컴포넌트를 참조해서 만들 수 있다. 리액트 앱에서는 버튼, 폼, 다이얼로그, 화면 등을 컴포넌트로 표현한다.
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="ChulSu" />
<Welcome name="Yuri" />
<Welcome name="Huni" />
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
App 리턴 부분에 Welcome 컴포넌트 세개를 넣었다.
컴포넌트 추출
유지보수와 재사용성을 위해 컴포넌트 추출도 가능하다.
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로 변경하였다. 컴포넌트는 어디에서든 재사용될 수 있기에 props의 이름은 명시적인 것보다는 포괄적인 편이 좋다.
UserInfo 추출
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
UserInfo 컴포넌트는 이렇게 추출할 수 있다.
두 개의 컴포넌트를 사용해서 다시 처음의 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>
);
}
이렇게 훨씬 간단해진 것을 볼 수 있다.
2. Props
props는 property의 약자로 리액트 컴포넌트의 속성을 나타낸다. props는 컴포넌트에 전달할 다양한 정보를 담고 있는 자바스크립트 객체로, 컴포넌트에 어떤 데이터를 전달하고 전달된 데이터에 따라 다른 내용을 화면에 렌더링하고 싶을 때, 해당 데이터를 props에 넣어서 전달한다.
props는 읽을 수만 있고, 값을 변경할 수는 없다. props의 값은 리액트 컴포넌트가 엘리먼트를 생성하기 위해 사용하는 값인데, 이 값들이 엘리멘트를 생성하는 도중에 갑자기 바뀌면 제대로 된 엘리먼트를 생성할 수 없기 때문이다. 따라서 다른 props의 값으로 엘리멘트를 생성하려면, 새로운 값을 component에 전달하여 새로 엘리먼트를 생성해야 한다.
Props 지정하기
<Cat name="Chulsu" age={15}/>
<Cat name="Yuri" age={10}/>
위의 코드에서 Cat 컴포넌트에 name props와 age props를 지정하였다.
위와 같이 같은 타입의 컴포넌트에 다른 props 값을 주어 컴포넌트의 효율적인 재사용이 가능하다.
Props 사용하기
destructuring 없이 props 사용하기
function Tool(props) {
const name = props.name;
const tool = props.tool;
return (
<div>
<h1>My name is {name}.</h1>
<p>My favorite design tool is {tool}.</p>
</div>
);
}
export default Tool;
이 코드는 비구조화 할당(destructuring)을 사용하지 않고 props 객체에서 값을 직접 추출한다. 예시 코드는 Tool 컴포넌트에서 'name'과 'tool'이라는 두 개의 속성을 추출한다.
destructuring으로 props 사용하기
function Tool({ name, tool }) {
return (
<div>
<h1>My name is {name}.</h1>
<p>My favorite design tool is {tool}.</p>
</div>
);
}
export default Tool;
Tool 함수의 매개변수로 {name, tool}을 사용하여, 부모 컴포넌트로부터 전달된 props 객체에서 'name'과 'tool' 속성을 직접 추출한다.
destructuring을 사용하면 위의 예제와 다르게 props를 반복해서 쓸 필요가 없어져 코드를 간결하고 명확하게 만들어준다.
<Tool name="Amy" tool="Photoshop"/>
이 컴포넌트를 사용할 때, 부모 컴포넌트는 'name'과 'tool' props를 제공해야 한다. 이렇게 하면 Tool component는 Alice를 name으로 Photoshop을 tool로 사용하여 화면에 표시한다.
Props에 기본값 설정하기
컴포넌트가 부모로부터 특정 props를 받지 않았을 때 기본적으로 사용할 초기값을 지정할 수 있는데, 여기에는 주로 두 가지 방법이 있다.
function Tool({ name = "Unknown", tool = "None" }) {
return (
<div>
<h1>My name is {name}.</h1>
<p>My favorite design tool is {tool}.</p>
</div>
);
}
이 방법은 함수의 매개변수에서 직접 기본값을 설정한다.
function Tool({ name, tool }) {
return (
<div>
<h1>My name is {name}.</h1>
<p>My favorite design tool is {tool}.</p>
</div>
);
}
Tool.defaultProps = {
name: "Unknown",
tool: "None"
};
이 방법은 컴포넌트에 'defaultProps'라는 속성을 설정하여 사용한다. 이것은 주로 클래스 컴포넌트에서 많이 사용되지만, 함수형 컴포넌트에서도 사용할 수 있다.
Component와 Props
Component와 props는 붕어빵틀과 붕어빵으로 비유할 수 있다. Component는 붕어빵 틀로, 안에 재료만 넣으면 똑같은 붕어빵을 여러 번 만드는 것이 가능하고, props는 붕어빵의 안에 들어가는 재료이다. 이처럼, 이미 만들어진 엘리먼트는 변경할 수 없고 새로 만드는 것밖에 안된다.