리액트를 다루는 기술 1~3장 스터디
by ag-lee
리액트를 다루는 기술 1~3장 정리
01 리액트 시작
###리액트 이해
리액트는 뷰(View)만 신경쓰는 라이브러리이다.
리액트는 컴포넌트를 최초로 실행한 초기렌더링
과 컴포넌트의 변경으로 다시 실행되는 리렌더링
을 통해 뷰를 관리한다. 여기서 컴포넌트
란 생김새와 작동 방식을 정의하고, 재사용이 가능한 API를 통해 컴포넌트에 대한 기능을 제공하는 리액트 프로젝트의 선언체를 의미한다.
리액트에서는 컴포넌트 내의 render
함수를 통해 초기 렌더링 을 다루는 데, 이 함수는 __컴포넌트의 생김새를 정의__하는 역할을 한다. 이 함수는 뷰의 생김새와 동작을 정의하는 객체를 반환하는 데 컴포넌트의 내부에 또 다른 컴포넌트를 넣는 경우에는 그 내부에 있는 컴포넌트들도 재귀적으로 렌더링한다. 이 과정을 진행하며 최상위의 컴포넌트의 렌더링이 완료되면 HTML 마크업을 실제 DOM에 주입한다.
리액트의 업데이트
컴포넌트에서 데이터의 변화가 있을 때 뷰가 변형되는 것처럼 보이지만, 실제로는 새로운 요소로 변경하는 과정을 거친다. 이 부분도 render 함수
가 진행하는데 컴포넌트는 데이터를 업데이트할 때 업데이트 값을 수정하는 것이 아닌, __새로운 데이터로 render 함수를 다시 호출__한다. 그리고 이전에 render 함수가 만들었던 컴포넌트 정보와 render 함수가 만든 __컴포넌트 정보를 비교해 DOM 트리를 업데이트__한다.
리액트의 특징
Virtual DOM
Virtual DOM을 사용하면 DOM을 추상화한 자바스크립트 객체를 구성해 데이터 변화를 반영한다.
리액트가 실제 DOM을 업데이트 하는 과정
- 데이터를 업데이트하면 전체 UI를 Virtural DOM에 리렌더링한다.
- 이전 Virtural DOM과 현재 내용을 비교한다.
- 바뀐 부분만 실제 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의 장점
-
보기 쉽고 익숙하다.
-
높은 활용도
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를 사용할 수 있게 되었다. useState
는 Hooks 의 한 종류로 좀 더 다양한 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