리액트를 다루는 기술 1~3장 정리

01 리액트 시작

###리액트 이해

리액트는 뷰(View)만 신경쓰는 라이브러리이다.

리액트는 컴포넌트를 최초로 실행한 초기렌더링과 컴포넌트의 변경으로 다시 실행되는 리렌더링을 통해 뷰를 관리한다. 여기서 컴포넌트 란 생김새와 작동 방식을 정의하고, 재사용이 가능한 API를 통해 컴포넌트에 대한 기능을 제공하는 리액트 프로젝트의 선언체를 의미한다.

리액트에서는 컴포넌트 내의 render 함수를 통해 초기 렌더링 을 다루는 데, 이 함수는 __컴포넌트의 생김새를 정의__하는 역할을 한다. 이 함수는 뷰의 생김새와 동작을 정의하는 객체를 반환하는 데 컴포넌트의 내부에 또 다른 컴포넌트를 넣는 경우에는 그 내부에 있는 컴포넌트들도 재귀적으로 렌더링한다. 이 과정을 진행하며 최상위의 컴포넌트의 렌더링이 완료되면 HTML 마크업을 실제 DOM에 주입한다.

리액트의 업데이트

컴포넌트에서 데이터의 변화가 있을 때 뷰가 변형되는 것처럼 보이지만, 실제로는 새로운 요소로 변경하는 과정을 거친다. 이 부분도 render 함수가 진행하는데 컴포넌트는 데이터를 업데이트할 때 업데이트 값을 수정하는 것이 아닌, __새로운 데이터로 render 함수를 다시 호출__한다. 그리고 이전에 render 함수가 만들었던 컴포넌트 정보와 render 함수가 만든 __컴포넌트 정보를 비교해 DOM 트리를 업데이트__한다.

리액트의 특징

Virtual DOM

Virtual DOM을 사용하면 DOM을 추상화한 자바스크립트 객체를 구성해 데이터 변화를 반영한다.

리액트가 실제 DOM을 업데이트 하는 과정
  1. 데이터를 업데이트하면 전체 UI를 Virtural DOM에 리렌더링한다.
  2. 이전 Virtural DOM과 현재 내용을 비교한다.
  3. 바뀐 부분만 실제 DOM에 반영한다.

리액트 프로젝트 생성

create-react-app 은 웹팩, 바벨을 포함해 프로젝트 작업환경을 구축해주는 도구이다. 아래 명령어를 사용하면 쉽게 프로젝트를 생성할 수 있다.

$ npm create-react-app <프로젝트 이름>
# 생략

# 프로젝트 디렉토리로 이동
$ cd <프로젝트 이름>

# react app 실행
$ npm run start

Compiled successfully!

You can now view hello-react in the browser.

  http://localhost:3000/

Note that the development build is not optimized.
To create a production build, use npm run build.

##02 JSX

JSX 란?

자바스크립트의 확장 문법으로 XML과 비슷하게 생긴 문법이다. 컴포넌트를 좀더 쉽게 작성할 수 있도록 도와준다.

function App() {
	return (
    	<div>
        	Hello <b> react </b>
        </div>
    )
}

/* 위 코드를 아래로 변환한다. */
function App() {
    return React.createElement("div", null, "Hello ", React.createElement("b", null, "react"));
}

JSX의 장점

  1. 보기 쉽고 익숙하다.

  2. 높은 활용도

    HTML 태그를 사용할 수 있을 뿐만 아니라, 만든 컴포넌트도 함께 작성할 수 있다.

JSX 문법

컴포넌트에 여러 요소가 있다면 부모 요소 하나로 감싸야한다.

Virtual DOM에서 컴포넌트 변화를 감지할 때 효율적으로 비교할 수 있도록 컴포넌트 내부는 하나의 DOM 트리로 이루어져야 한다는 규칙이 있기 때문이다.

Fragment 를 이용해 감싸면 따로 HTML 요소를 이용하지 않더라도 하나로 감쌀 수 있다. Fragment는 <></>로 생략할 수 있다.

import React, {Fragment} from 'react'

function App() {
    return (
    	<Fragment>
        	<h1> 리액트 안녕! </h1>
            <h2> 잘 동작하니? </h2>
        </Fragment>
    )
}

자바스크립트 표현식을 작성할 때에는 { } 로 코드를 감싼다.

자바스크립트 표현식을 이용할 때에는 JSX에서 코드를 { } 로 감싸 이용한다.

import React from 'react';

function App() {
    const name = '리액트';
    return (
        <Fragment>
        	<h1> {name} 안녕! </h1>
            <h2> 잘 동작하니? </h2>
        </Fragment>
    )
}

If 문 대신에 조건부 연산자를 이용해서 표현한다.

JSX 안의 javascript에서는 if문을 사용할 수 없으므로 밖에서 사전에 값을 설정하거나, { } 안에 삼항 연산자를 이용해 표현한다.

import React from 'react';

function App() {
    const name = '리액트';
    return (
        <div>
        	{name === '리액트' ? (
             	<h1>리액트입니다.</h1>
            ) : (
            	<h2>리액트가 아닙니다.</h2>
            )}
        </div>
    )
}

export default App;

AND 연산자(&&)를 사용해 조건부 렌더링을 한다.

특정 조건을 만족할 때에만 내용을 보여주는 경우 && 연산자를 사용하면 조건부 연산자 보다 효율적으로 표현이 가능하다. && 연산자로 조건부 렌더링을 할 수 있는 이유는 false와 null 을 렌더링하지 않기 때문이다. 주의해야할 점은 0은 화면에 보여진다는 것이다.

import React from 'react';

function App() {
    const name = '리액트';
    return <div>{name === '리액트' && <h1>리액트입니다.</h1>}</div>
}

undefined를 렌더링하지 않도록 해야한다.

리액트 컴포넌트에서 undefined만 반환해 렌더링하는 상황이 오면 Nothing was returned from render. 오류가 발생할 수 있다. JSX 내부에서 undefined를 반환하는 건 괜찮지만 아래와 같은 코드는 주의가 필요하다.

import React from 'react';
import './App.css';

function App() {
    const name = undefined;
    return name;
}

export default App;

인라인 스타일링은 객체 형태로 넣어 주어야한다.

리액트에서 DOM 요소에 스타일을 적용할 때에는 객체 형태로 넣어야한다. - 가 스타일 이름에 있는 경우는 카멜 케이스로 표기한다.

const style = {
    backgroundColor: 'black',
    color: 'aqua',
    fontSize: '48px'
}

return <div style={style}>{name}</div>;

class 대신 className을 사용한다.

JSX에서는 class가 아닌 className으로 설정해주어야 클래스 명을 적용할 수 있다.

<div className="react"></div>

03 컴포넌트

컴포넌트를 선언하는 방식은 함수형 컴포넌트클래스형 컴포넌트 두 가지가 있다.

클래스형 컴포넌트

클래스형 컴포넌트와 함수형 컴포넌트의 가장 큰 차이점은 __클래스형 컴포넌트에는 state라이프사이클을 사용할 수 있다는 것__이다. 또한 클래스형 컴포넌트에서는 클래스 내부에 임의 메서드를 정의할 수 있다.

import React, { Component } from 'react';

class App extends Component {
    render() {
        const name = 'react';
        return (
        	<div className="react">{name}</div>
        )
    }
}

export default App;

클래스형 컴포넌트에서는 render 함수에 JSX를 정의해 반환한다. 그러므로 컴포넌트를 렌더링하기 위해서는 render()함수를 꼭 작성해야 한다.

함수형 컴포넌트

함수형 컴포넌트는 클래스형 컴포넌트보다 선언하기 간편하고, 메모리 자원도 덜 사용한다. 또한, 빌드했을 때의 크기도 작다. 하지만, 함수형 컴포넌트에서는 state와 라이프사이클 API를 사용하지 못한다. 하지만, 리액트 v16.8 이후 Hooks 를 도입해 이 단점을 보완했다.

리액트 공식 매뉴얼에서는 컴포넌트를 새로 작성할 때에는 __함수형 컴포넌트와 Hooks__를 사용하도록 권장하고 있다.

Props

props는 properties를 줄인 표현으로 컴포넌트 속성을 설정할 떄 사용하는 요소이다. props 값은 해당 컴포넌트를 사용하는 부모 컴포넌트에서 설정할 수 있다.

JSX 내부에서 props 렌더링

// 부모 컴포넌트
import React from 'react';
import MyComponent from './MyComponent';

const App = () => {
    return <MyComponent name="React" />;
}

// 자식 컴포넌트
import React from 'react';

const MyComponent = (props) => {
    return <div>안녕하세요, 제 이름은 {props.name}입니다.</div>
}

// default Component 지정 (부모에서 전달 안하는 경우)
MyComponent.defaultProps = {
    name: '기본 이름'
}

export default MyComponent;

태그 사이의 내용을 보여주는 Children

children 은 태그 사이의 내용을 보여주는 props이다. 자식 요소를 보여준다.

// 부모 컴포넌트
import React from 'react';
import MyComponent from './MyComponent';

const App = () => {
    return <MyComponent>리액트</MyComponent>;
}

// 자식 컴포넌트
import React from 'react';

const MyComponent = (props) => {
    return (
        <div>안녕하세요, 제 이름은 {props.name}입니다.</div>
        <div>children 값은 {props.children}입니다.</div>
    );
}

// default Component 지정 (부모에서 전달 안하는 경우)
MyComponent.defaultProps = {
    name: '기본 이름'
}

export default MyComponent;

propTypes를 통한 props 검증

props의 필수값 여부, 타입 validation을 할 때에는 propTypes를 이용할 수 있다.

MyComponent.propTypes = {
    name: PropTypes.string
    favoriteNumber: PropTypes.number.isRequired
}

더 많은 PropTypes (git repository) : 15.5 부터 별도 패키지로 제공

https://github.com/facebook/prop-types

클래스형 컴포넌트에서 props 사용

import React, { Componet } from 'react'
import PropTypes from 'prop-types'

class MyComponent extends Component {
    render() {
        const {name} = this.props;
        
        return (
        	<div>안녕하세요, 제 이름은 {name}입니다.</div>
        )
    }
}

state

state는 컴포넌트가 가지고 있는 상태값이다. props는 부모가 전달해주는 값이므로 읽기전용으로만 사용이 가능하다. 하지만, state는 컴포넌트의 상태값이므로 조작이 가능한 값이다.

render 함수에서 state를 호출할 때에는 this.state 로 접근한다.

state는 객체이므로 내부에 여러 값을 넣을 수 있다.

import React, {Component} from 'react';

class Counter extends Component {
    constructor(props) {
        super(props);
        
        this.state = {
            number: 0,
            fixedNumber: 0
        };
    }
    
    render() {
        const {number, fixedNumber} = this.state;
        return (
            <div>
                <h1>{number}</h1>
                <h2>바뀌지 않는 값: {fixedNumber}</h2>
                <button
                    onClick={() => this.setState({number: number+1})}
                    > +1 </button>
            </div>
        )
    }
}

state는 constructor 밖에 선언해주어도 된다.

class Counter extends Component {
    state = {
        number: 0,
        fixedNumber: 0
    };

    constructor(props) {
        super(props);
    }
   
	// 생략
}

this.setState 에 객체 대신 함수를 인자로 넣어줄 수도 있다.

this.setState((prevState, props) => {
    return {
        // 업데이트 하고싶은 내용
    }
})

또한, this.setState 이 끝난 후 특정 작업을 실행할 수도 있다.

<button
    onClick={() => {
        this.setState({number: number + 1},
                     () => {
            console.log('작업끝');
            console.log(this.state);
        })
    }}

함수형 컴포넌트에서 useState 사용하기

기존에는 함수형 컴포넌트에서 state를 사용할 수 없었지만, 16.8부터 useState 함수를 이용해 함수형 컴포넌트에서도 state를 사용할 수 있게 되었다. useStateHooks 의 한 종류로 좀 더 다양한 Hooks가 있다.

import React, { useState } from 'react';

const Say = () => {
    const [message, setMessage] = useState('');
    const onClickEnter = () => setMessage('안녕하세요!');
    const onClickLeave = () => setMessage('안녕히가세요~');
    
    return (
    	<div>
        	<button onClick={onClickEnter}>입장</button>
            <button onClick={onClickLeave}>퇴장</button>
            <h1>{message}</h1>
        </div>
    )
}

useState 함수의 인자에는 상태의 초기값을 넣어준다. 클래스형 컴포넌트에서는 state 초기 값은 객체를 넣어서 초기화 해주지만, useState에서는 객체가 아니어도 된다. 함수를 호출하면 배열이 반환되는데 state의 현재 상태setter 를 반환한다. 또한, 하나의 컴포넌트에서 useState를 여러번 호출하면, 각 호출별로 state를 추가할 수 있다.

state를 사용할 때 주의사항

  • State 값을 변경할 때에는 setState나, useState로 전달받은 setter를 이용해야 한다.
  • 객체 값을 변경할 때에는 사본을 만들어 값을 변경한 후 setter, setState를 이용한다.


리액트를 다루는 기술 요약 정리
https://book.naver.com/bookdb/book_detail.nhn?bid=15372757