React는 사용자 인터페이스를 만들기 위한 Javascript 라이브러리 중 하나이다.
- 선언형
- 컴포넌트 기반
위 두 가지는 React의 특징이다.
React는 상호작용이 많은 UI를 만들 때 생기는 어려움을 줄여준다. 애플리케이션의 각 상태에 대한 간단한 뷰만 설계하면 데이터가 변경됨에 따라 적절한 컴포넌트만 효율적으로 갱신하고 렌더링한다. 선언형 뷰는 코드를 예측 가능하고 디버그하기 쉽게 만들어 준다.
React는 컴포넌트 기반이기 때문에 스스로 상태를 관리하는 캡슐화된 컴포넌트를 만들고 이를 조합하여 더 복잡한 UI를 만들 수 있다. 컴포넌트를 잘 활용하면 코드의 재사용성과 유지보수에서 이점을 가진다.
JSX는 Javascript의 확장문법으로 우리가 작성한 React 컴포넌트를 화면에 보여주기 위해서 사용한다. JSX는 React"엘리먼트"를 생성합니다. React에서는 항상 JSX를 리턴해줘야 하기 때문에 React를 배울 때 필수적으로 알아야 한다. 물론 이걸 사용하지 않고 작성하려 한다면 다음과 같이 써줘야 한다.
//JSX를 사용하지 않은 코드
React.createElements('header', { className:'App-container'}...);
React.createElements('p',{ className:'body'...})
JSX를 사용하지 않는다면 복잡도해보이고 가독성 떨어져 보인다.
//JSX를 사용한 코드
<header className='App-container'> ... </header>
<p> ... </p>
두 코드를 조금만 비교해 봐도 JSX를 사용한 코드가 훨씬 덜 복잡해보이고 가독성도 좋아 보인다. 때문에 우리는 React를 사용하면서 JSX문법을 필수적으로 사용해야 한다.
이런 JSX는 사용할 때 Javascript 코드로 변환되어야 하기 때문에 몇가지 규칙 필요하다.
- 반드시 하나의 엘리먼트로 감싸야 한다.
- 내부에 Javascript 코드를 쓸 땐 {} 안에서 작성해야 한다.
- 내부에서는 if문 사용이 불가능하다. ==> 삼항연산자 사용
- 클래스이름 적용시, className 사용
엘리먼트는 화면에 표시할 내용을 기술한다. 그리고 이를 ReactDOM
을 이용해서 HTML파일
에 있는 특정 타겟에게 붙여준다.
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, 'HTML파일의 특정 타겟');
React엘리먼트는 불변객체이다. 즉, 생성한 이후 해당 엘리먼트의 자식이나 속성을 변경할 수 없다.
그래서 엘리먼트를 업데이트 하는 유일한 방법은 새로운 엘리먼트를 생성하고 이를 다시 렌더링 하는 것이다.
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>It is {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
ReactDOM은 해당 엘리먼트와 이전 엘리먼트와 비교해서 필요한 경우에만 DOM을 업데이트한다.
개념적으로 컴포넌트는 Javascript의 함수와 유사하다. props라는 임의의 인자를 받고 이를 통해서 표시되는 React엘리먼트를 반환합니다.
이러한 컴포넌트의 사용으로 인해 코드의 재사용과 유지보수가 용이해 진다.
// 함수 컴포넌트
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
//객체인자를 받은 후 React엘리먼트를 반환한다.
// 클래스 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// class는 몇 가지 추가 기능이 있다.
React 엘리먼트는 사용자 정의 컴포넌트로도 나타낼 수 있다.
const element = <Welcome name="Sara"/>;
ReactDOM.render(element, document.getElementById('root'));
React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX 어트리뷰트와 자식을 해당 컴포넌트에 단일 객체로 전달하는데 이 객체를 props
라고 한다.
props = {name: 'Sara'}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(<App />,document.getElementById('root'));
function Comment(props) { //{author:{...}, text:'...', date: ... }을 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>
);
}
상위 코드에서 컴포넌트들을 분리해낸다.
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
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>
);
}
위 코드 처럼 재사용 가능한 컴포넌트를 만들어 놓는 것은 더 큰 작업을 할 때 유용하게 사용 가능하다.
함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트의 자체 props를 수정해서는 안된다.
모든 React 컴포넌트는 자신의 props를 다룰 때 반드시 순수 함수처럼 동작해야 한다.
결과만 놓고 말한다면 YES 다. 어떻게 가능한지 코드를 통해 설명하겠다.
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
혹은
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
이렇게 사용한다면 여러개의 엘리먼트를 리턴할 수 있다.
State는 props와 유사하지만, 비공개이며 컴포넌트에 의해 완전히 제어된다.
React.Component
를 확장하는 동일한 이름의 ES6 class
를 생성render()
라고 불리는 빈 메소드 추가render()
메소드 안에 작성render
내용 안에 있는 props
를 this.props
로 변경class App extends React.Component {
render() {
//함수의 내용 입력
}
}
render()
메소드 안에 있는 this.props.date
를 this.state.date
로 변경this.state
를 지정하는 class constructor
를 추가props
를 기본 constructor
에 전달class App extends React.Component {
constructor(super) {
super(super)
this.state = {// 값을 객체형태로 지정}
}
render() {
//함수의 내용 입력
}
}
컴포넌트가 삭제될 때 해당 컴포넌트가 사용 중이던 리소스를 확보하는 것이 중요하다. 컴포넌트 클래스에서 특별한 메서드를 이용해서 컴포넌트가 마운트되거나 언마운트 될 때 일부 코드를 작동할 수 있다. 이러한 메서드들을 생명주기 메서드라고 불린다.
class App extends React.Component {
constructor(super) {
super(super)
this.state = {/* 값을 객체형태로 지정*/}
}
componentDidMount() {
// 마운트 될 때 작동할 코드
}
componentWillUnmount() {
// 언마운트 될 때 작동할 코드
}
render() {
//함수의 내용 입력
}
}
setState
를 이용해서 수정// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
별도의 setState() 호출로 이러한 변수를 독립적으로 업데이트 할 수 있다.
컴포넌트는 자신의 state를 자식 컴포넌트에 props로 전달할 수 있다.