[노마드코더] ReatJS 웹서비스 만들기
#0 INTRODUCTION
#0.1 Requirements
- node, npm, npx 설치확인
- VSC(Visual Studio Code)
- git
#0.2 Theory Requirements
html, css, 바닐라JS
const, let, function, argument,
#0.3 Why React
리액트를 사용하기위해 별도로 프레임워크 지식을 배워야하는 것이 아니기 때문에 자바스크립트 지식만 안다면 사용할 수 있다. 앵귤러는 앵귤러 프레임워크를 사용하기위해 앵귤러를 배워야한다.
#1.0 Creating your first React App
React로 만든 코드를 브라우저가 이해하지 못하기 때문에 이 코드를 변환해줘야 한다.
이런 설정을 하기 위해 몇가지 단계가 필요하다. Webpack
을 다운로드 해야하고 Babel
을 다운로드 해야한다.
그리고 React코드를 Compile 하고, 다른 파일에 넣어야하고….. 등등등
⇒ create-react-app
을 이용해서 이러한 과정을 할필요가 없어졌다.
기본적으로 하나의 명령을 실행해서 React Web App을 Set up할 수 있게 해준다.
- create-react-app 으로 프로젝트 생성
npx create-react-app 프로젝트폴더명지정
- VScode로 프로젝트 열기
code 프로젝트폴더명
※ Visual Studio Code 터미널에서 실행하기
Visual Studio Code 를 켠다.
Command Palette 를 연다. [⇧⌘P ]
Shell Command 를 입력해 [ Shell Command: Install ‘code’ command in PATH command ] 를 실행한다.
터미널에서 원하는 [ code . ] 으로 실행이 가능
package.json
우리가 신경써야할 두가지 항목
-
"start": "react-scripts start"
-
"build": "react-scripts build"
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
yarn.lock
기본적으로 따라오는 파일
(우리는 현재 쓰지 않으므로 삭제)
- 애플리케이션 실행
npm start
접속 주소 확인 가능
#1 SETUP
#1.1 Creating a Github Repository
Github에 프로젝트명과 동일한 Repository생성 후 연동
git init #git 저장소 초기화
git remote add origin https://github.com/blossun/movie_app #원격 저장소 연결
커밋 후 업로드
git add .
git commit -m "#1.0 Creating your first React App"
git push origin master
#1.2 How does React work?
src 디렉토리에 App.js
와 index.js
만 남겨두고 삭제
import React from 'react';
function App() {
return (
<div> Hello!!! </div>
);
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(<App />, document.getElementById('root'));
index.js에는 “Hello!!!”라는 문자열이 없지만 브라우저에서 소스코드보기 개발자도구 보기
를 하면 다음과 같이 만들어져 있다.
( * 소스코드에는 어느 부분에도 “Hello!!!”라는 문자열이 없다. )
<div id="root"><div> Hello!!! </div></div>
react는 우리가 쓰는 모든 요소를 생성한다는 것이다. 자바스크립트와 함께 만들어서 HTML에 넣는 것이다.
React는 이곳(xxxxx)에 element를 넣는 역할을 담당한다.
<div id="root"><div> xxxxx </div></div>
ReactDOM.render()
에서 그 역할을 해준다.
<App />
→import App from './App';
을 랜더링한다.document.getElementById('root')
→ Element의 Id 값이 ‘root’인 부분에
⇒ app.js component를 ElementById 내부에 넣어준다.
소스코드에는 어느 부분에도 “Hello!!!”라는 문자열이 없다. 소스코드에서 우리가 보는 것은 기본적으로 빈 index.js 파일이다.
이게 바로 react를 빠르게하는 이유이다.
react는 소스코드에 처음부터 HTML을 넣지 않고, HTML에서 HTML을 추가하거나 제거하는 법을 알고 있다.
그래서 애플리케이션을 실행하면 빈 HTML을 로드한 다음에 react가 우리가 component에 작성했던 것들을 HTML에 밀어넣게 된다.
※ virtual DOM (virtual document object model)
virtual : “존재하지 않는다.”, 소스코드에 존재하지 않는다는 의미
react 가 만들어낸다.
#2.0 Creating your first React Component
index.js 의 코드를 보자.
<App />
는 HTML 코드가 아니다. 기본적으로 이것을 component
라고 부른다.
react는 component와 함께 동작한다. 모든 것은 component이다. 우리는 component를 만들고 component가 data를 보여주게 할 것이다.
ReactDOM.render(<App />, document.getElementById('root'));
※ Component 란?
-
HTML을 반환하는 함수
-
대문자로 시작
function App() { return ( <div> <h3>하하하하</h3></div> ); }
※ JSX
- react는 component를 사용해서 HTML처럼 작성하려는 경우에 필요하다.
- javascript와 HTML 사이의 조합을 jsx라고 한다.
- react에서 나온 custom한 유일한 개념이다. (나머지 개념은 모두 Javascript)
- Component를 생성해보자
-
component를 작성할 때마다 추가해야함. 그래야 react가 jsx가 있는 component를 사용하는 것을 이해한다.
import React from 'react';
import React from 'react'; //component를 작성할 때마다 추가해야함.
function Potato() {
return (
<h3>I love potato</h3>
)
}
export default Potato;
- 생성한 Component를 사용해보자
잘못된 사용방식 - react application이 하나의 component만을 rendering해야하기 때문이다.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import Potato from './Potato';
ReactDOM.render(
<App /><Potato />,
document.getElementById('root')
);
Potato 컴포넌트를 App 컴포넌트 내에 존재하도록 수정
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
import React from 'react';
import Potato from "./Potato";
function App() {
return (
<div>
<h3>하하하하</h3>
<Potato /> //<-- 여기에 넣어줌
</div>
);
}
export default App;
import React from 'react'; //component를 작성할 때마다 추가해야함.
function Potato() {
return (
<h3>I love potato</h3>
)
}
export default Potato;
결론
- react application은 한 번에 하나의 component만 rendering할 수 있다.
- 따라서 모든 것은 application 안에 들어가야한다.
- application 안에 많은 component를 넣을 수 있다.
#2 JSX & PROPS
#2.1 Reusable Components with JSX + Props
같은 파일 내에서 컴포넌트 생성 및 사용
import React from 'react';
function Potato() {
return <h1>I like Potato</h1>;
}
function App() {
return (
<div>
<h3>하하하하</h3>
<Potato />
</div>
);
}
export default App;
※ props(property)
-
component에 정보를 보낼 수 있다.
-
component를 재사용한다.
-
component에서 component(children component)로 정보를 보내는 방법
application에서 food component로 정보를 보내고, 그 후 food component에서 그 정보를 어떻게 사용하는지에 대해 배워보자
Food 컴포넌트에 “fav”라는 이름의 prop(property)를 “kimchi”라는 value로 준 것
<Food fav="kimchi" />
react는 우리가 전달한 props를 가져가서 일을 처리한다.
father component에서 children component로 원하는 많은 props를 보낼 수 있다.
<Food
fav="kimchi"
somethine={true}
papapa={["hello", 1, 2, 3, 4, true]}
/>
food function component의 argument에 담아서 받는다.
function Food(props) {
console.log(props);
return <h1>I like Potato</h1>;
}
콘솔창에 component로 전달된 모든 props를 확인할 수 있다.
다음과 같이 특정 prop를 꺼내서 사용하거나 받을 수 있다.
function Food(props) {
console.log(props.fav);
return <h1>I like {props.fav}</h1>;
}
function Food({fav}) {
console.log(fav);
return <h1>I like {fav}</h1>;
}
여러번 호출
import React from 'react';
function Food(props) {
console.log(props.fav);
return <h1>I like {props.fav}</h1>;
}
function App() {
return (
<div>
<h3>하하하하</h3>
<Food fav="kimchi"/>
<Food fav="ramen"/>
<Food fav="cheeze"/>
<Food fav="pasta"/>
</div>
);
}
export default App;
#2.2 Dynamic Component Generation
웹사이트에 동적 데이터 추가
(API로 부터 데이터를 전달받았다고 가정)
javascript로만 object의 list를 가져옴
map()
- 함수내에 각 item에 대해 실행되는 function을 정해준다.
- array에 대해 우리가 정의한 함수를 실행한 결과로 array를 반환한다.
순수 javascript의 map을 이용해서 컴포넌트에 값을 전달해보자
import React from 'react';
function Food({name, picture}) {
console.log(name, picture);
return <div>
<h1>I like {name}</h1>
<img src={picture} />
</div>
}
const foodILike = [{
name: "Kimchi",
image:
"http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg"
},
{
name: "Samgyeopsal",
image:
"https://3.bp.blogspot.com/-hKwIBxIVcQw/WfsewX3fhJI/AAAAAAAAALk/yHxnxFXcfx4ZKSfHS_RQNKjw3bAC03AnACLcBGAs/s400/DSC07624.jpg"
},
{
name: "Bibimbap",
image:
"http://cdn-image.myrecipes.com/sites/default/files/styles/4_3_horizontal_-_1200x900/public/image/recipes/ck/12/03/bibimbop-ck-x.jpg?itok=RoXlp6Xb"
},
{
name: "Doncasu",
image:
"https://s3-media3.fl.yelpcdn.com/bphoto/7F9eTTQ_yxaWIRytAu5feA/ls.jpg"
},
{
name: "Kimbap",
image:
"http://cdn2.koreanbapsang.com/wp-content/uploads/2012/05/DSC_1238r-e1454170512295.jpg"
}];
function App() {
return (
<div>
{foodILike.map(dish => <Food name={dish.name} picture={dish.image}/>)}
</div>
);
}
export default App;
-
dish
는 현재 Object를 의미한다. Food 컴포넌트에 현재 Object에 있는 name, image 2개의 prop중에 name을 선택해서 넘겨주고 있다.{foodILike.map(dish => <Food name={dish.name} picture={dish.image}/>)}
-
이 요소하나하나가 Object이다.
{ name: "Kimchi", image: "http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg" }
#2.3 map Recap
함수로 추출해서 사용할 수 있다만 보고 넘어감
function renderFood(dish) {
console.log(dish);
return <Food name={dish.name} picture={dish.image} />
}
function App() {
return (
<div>
{console.log(foodILike.map(renderFood))} //-> 각각의 component
{foodILike.map(renderFood)}
</div>
);
}
- 에러메시지 확인
각각 list 내의 child는 unique한 key prop를 가져야 한다.
react의 element는 유일해야한다. 하지만 우리가 list 안으로 집어넣을 때, 유일성을 잃는다.
⇒ 따라서 각 item들에 id값을 부여한다.
(에러해결)
const foodILike = [{
id: 1,
name: "Kimchi",
image:
"http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg"
},
...
}];
function App() {
return (
<div>
{foodILike.map(dish => <Food key={dish.id} name={dish.name} picture={dish.image}/>)}
</div>
);
}
“Food 컴포넌트에서 id 값을 사용하진 않지만 기본적으로 react 내부에서 사용하기 위한 값이다.”
- img 태그는 alt prop가 반드시 있어야한다.고 코드 스타일 알람이 뜬다.
시각장애인을 위한 정보표시
#2.4 Protection with PropTypes
목표 : father component로 부터 전달받은 props를 체크
-
foodILike Object에 rating(평점) prop 추가
const foodILike = [{ id: 1, name: "Kimchi", image: "http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg", rating: 5 },...]; function Food({ name, picture, rating }) { console.log(name, picture, rating); return <div> <h2>I like {name}</h2> <h4>{rating}/5.0</h4> <img src={picture} alt={name} /> </div> }
-
prop-types 설치
npm i prop-types
→ 설치확인 :
package.json
파일의dependencies
에prop-t]ypes
가 있으면 성공
- prop-types : 내가 전달받은 props가 내가 원하는 props인지를 확인
- prop-types 적용
(1) prop-types import
import PropTypes from "prop-types";
(2) 내가 얻고 싶은 props에 대한 설명을 적는다.
Object.propTypes
로 이름을 지어야 react가 인식한다.
Food.propTypes = {
// 내가 얻고 싶은 props에 대한 설명을 적는다.
name: PropTypes.string.isRequired,
picture: PropTypes.string.isRequired,
rating: PropTypes.string.isRequired
}
- prop-types 으로 찾아진 오류 확인
웹브라우저 페이지 상에는 보이지 않지만 콘솔을 확인하면 에러메시지를 던져주고 있다.
→ “rating의 type은 숫자로 제공됐지만 우리는 String을 기대한다.”
⇒ rating의 요구되는 타입을 number로 수정하면 에러코드 삭제됨
Food.propTypes = {
// 내가 얻고 싶은 props에 대한 설명을 적는다.
name: PropTypes.string.isRequired,
picture: PropTypes.string.isRequired,
rating: PropTypes.number //<-- numbertype OR undefined
}
#3 STATE
state는 보통 우리가 동적 데이터와 함께 작업할 때 만들어진다.
-
dynamic data
변하는 데이터, 존재하지 않는 데이터, 생기고 사라지거나 변경된 데이터, 하나 또는 두개가 되고 0이 되는 종류의 데이터
- Function component를 Class component로 변경
React.Component
를 상속받아서render
메서드 구현
import React from 'react';
import PropTypes from "prop-types";
class App extends React.Component { //class component
render() {
return <h1>Im a class component</h1>
}
}
export default App;
-
react는 자동적으로 모든 class component의 render method를 실행한다.
-
state는 object이고 component의 data를 넣을 공간이다. 그리고 이 state 값은 변경된다.
import React from 'react';
import PropTypes from "prop-types";
class App extends React.Component { //class component
state = {
count : 0
}
render() {
return <h1>This number is : {this.state.count}</h1>
}
}
export default App;
ES6 Javascript 코드 문법이다.
add = () => {};
minus = () => {};
react에서는 자동적으로 주어진 onClick이 있다.
import React from 'react';
import PropTypes from "prop-types";
class App extends React.Component { //class component
state = {
count : 0
}
add = () => {
console.log("add");
};
minus = () => {
console.log("minus");
};
render() {
return (
<div>
<h1>This number is : {this.state.count}</h1>
<button onClick={this.add}>Add</button>
<button onClick={this.minus}>Minus</button>
</div>
)
}
}
export default App;
#3.1 All you need to know about State
★ “절대 state를 직접 변경하지 마세요”
잘못된 코드 )
- state가 동작하지 않는다. 에러메시지에 setState()를 쓰라고 나온다.
- why? react는 render function을 refresh하지 않기 때문이다.
add = () => {
this.state.count = 1;
};
⇒ 우리가 setState()를 호출하면, react는 우리가 언제 setState()를 호출할지를 알고, 내가 view를 refresh하길 원하는 것을 알기 때문에 render function을 재호출해준다.
- react는 새로운 state와 함께 render function을 호출한다.
add = () => {
this.setState({ count: 1});
};
⇒ react는 변화가 있는 부분만 업데이트한다.
react는 모든걸 다시 칠하지만 virtual DOM을 가지고있기 때문에 매우 빠르게 변경할 수 있고, 깜밖거리지도 않는다.
권장되지 않는 코드 )
다음 코드와 같이 수정했지만 좋은 코드는 아니다.
why? state
에 의존하고 있기 때문에. (추후에 몇가지 성능 문제가 있다.)
import React from 'react';
import PropTypes from "prop-types";
class App extends React.Component { //class component
state = {
count : 0
}
add = () => {
this.setState({ count: this.state.count + 1});
};
minus = () => {
this.setState({ count: this.state.count -1});
};
render() {
return (
<div>
<h1>This number is : {this.state.count}</h1>
<button onClick={this.add}>Add</button>
<button onClick={this.minus}>Minus</button>
</div>
)
}
}
export default App;
- (react) function방식으로 현재의 state를 가져오는 기능을 제공한다.
권장하는 코드)
state를 set할 때, react에서 외부의 상태에 의존하지않는 가장 좋은 방법이다.
add = () => {
this.setState(current => ({ count: current.count + 1}));
};
우리는 현재 state를 얻고 싶고, react가 그것을 줄 것이다. 따라서 훨씬 더 나은 새로운 state를 사용할 수 있다.
#3.2 Component Life Cycle
React.Component는 life cycle method를 가지는데, life cycle method는 react가 component를 생성하고 삭제하는 방법이다.
Mounting
- 생성
constructor()
: Javascript에서 class를 만들 때 호출- component가 mount될 때, component가 screen에 표시될 때, component가 Website에 갈 때, constructor를 호출한다.
- 그 이후에 render()가 호출된다.
- component가 render할 때,
componentDidMount()
우리에게 알려준다. “이 component는 처음 render됐다.”
Updating
- 업데이트
- 컴포넌트가 업데이트 될 때 호출되는 많은 function들이 있다.
- render()이후 ….
componentDidUpdate()
실행 - (버튼을 클릭하는 등) setState를 호출하면, component를 호출하고, 먼저 render를 호출 → 업데이트가 완료되었다고 말하면 componentDidUpdate가 실행된다.
Unmounting
-
소멸
-
어떻게 Component가 죽나?
다양한 방법이 있다. 페이지 전환, state를 사용해서 component를 교체
-
componentWillUnmount()
#3.3 Planning the Movie Component
페이지가 로딩되면 isLoading은 무조건 true이다.
import React from 'react';
class App extends React.Component { //class component
state = {
isLoading: true
};
//setTimeout을 입력
com
render() {
const {isLoading} = this.state;
return <did>{isLoading ? "Loading...": "We ard ready"}</did>;
}
}
export default App;
퀴즈 ) 처음에 render를 하면 호출되는 life cycle method는 무엇일까?
componentDidMount()
처음 render할 때는 “Loading…“을 찍고, 6초뒤에 “We ard ready”를 출력해보자
class App extends React.Component { //class component
state = {
isLoading: true
};
// render이후 처음 호출되는 life cycle function
// 6초후에 isLoading의 값을 false로 변경
componentDidMount() {
setTimeout(() => {
this.setState({isLoading: false})
}, 6000);
}
render() {
const {isLoading} = this.state;
return <did>{isLoading ? "Loading...": "We ard ready"}</did>;
}
}
export default App;
우리가 할 일 : componentDidMount엣서 data를 fetch하는 것
API로 부터 data fetching이 완료되면 “We ard ready”를 출력하는 대신에 movie를 Render하고 map을 만들고 movie를 render하는 것이다.
#4 MAKING THE MOVIE APP
#4.0 Fetching Movies from API
- fetch 대신
Axios(악시우스)
사용 - YTS에서 만든 API 사용
Axios
- fetch위에 있는 작은 layer
- Axios 설치
npm install axios
예제소스 - api 링크, movie list api - json 불법사이트라 url이 매번 바뀌기 때문에 nomad coder의 YTS Proxy를 이용하자
API url - https://yts.mx/api/v2/list_movies.json
import React from 'react';
import axios from "axios"; //추가
class App extends React.Component { //class component
state = {
isLoading: true,
movies: []//movie state 생성
};
componentDidMount() {
axios.get("https://yts.mx/api/v2/list_movies.json"); //추가
}
render() {
const {isLoading} = this.state;
return <did>{isLoading ? "Loading...": "We ard ready"}</did>;
}
}
export default App;
페이지 reload 후, 네트워크 탭 확인
axios가 요청한 데이터 확인
axios로 요청하여 받아온 데이터를 사용해보자
axios가 느리기 때문에, 우리는 javascript에게 componentDidMount 함수가 끝날 때까지 약간 시간이 걸릴 수 있다고 말해야한다. 그러기 위해서 우리는 componentDidMount앞에 async
를 넣는다.
import React from 'react';
import axios from "axios";
class App extends React.Component { //class component
state = {
isLoading: true,
movies: []//movie state 생성
};
getMovies = async () => {
const movies = await axios.get("https://yts-proxy.now.sh/list_movies.json");
};
componentDidMount() {
this.getMovies();
}
render() {
const {isLoading} = this.state;
return <did>{isLoading ? "Loading...": "We ard ready"}</did>;
}
}
export default App;
async
와 await
로
“이 함수가 비동기 함수다. 너는 이걸 기다려야 해”라고 알려주는 것
( ⇒ ES6 수업 참고 )
#4.1 Rendering the Movies
movies 목록을 가져와서 콘솔에 출력
import React from 'react';
import axios from "axios";
class App extends React.Component { //class component
state = {
isLoading: true,
movies: []//movie state 생성
};
getMovies = async () => {
const movies = await axios.get("https://yts-proxy.now.sh/list_movies.json");
console.log(movies.data.data.movies); //object구조에 따라 data내의 data내의 movies 목록을 가져옴
};
componentDidMount() {
this.getMovies();
}
render() {
const {isLoading} = this.state;
return <did>{isLoading ? "Loading...": "We ard ready"}</did>;
}
}
export default App;
⇒ ES6 문법에 따라 다음과 같이 줄일 수 있다.
getMovies = async () => {
const {data: {data: {movies}}} = await axios.get("https://yts-proxy.now.sh/list_movies.json");
console.log(movies); //object구조에 따라 data내의 data내의 movies 목록을 가져옴
};
가져온 movies 목록을 state의 movies 배열에 넣어준다.
//앞의 movies는 state의 movies이고, 뒤의 movies는 axios로 부터 받아온 movies이다.
this.setState({movies:movies})
this.setState({movies}) //위와 동일. 단축해서 쓸 수 있다.
Loading의 상태도 변경해준다.
this.setState({movies, isLoading: false}) //위와 동일. 단축해서 쓸 수 있다.
movies 를 랜더링
Movie.js는 state(상태)가 필요없기 때문에 function component로 만든다.
중요한 건 우리가 얻어 올 props를 찾기 시작하는 것이다.
import React from "react";
import PropTypes from "prop-types";
function Movie({id, year, title, summary, poster}) { // props로 바로 받는게 아니라 그 안의 값을 받는 것이므로 {}로 한번 더 묶어줘야 한다.
return <h5>{title}</h5>;
}
Movie.propTypes = {
id: PropTypes.number.isRequired,
year: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired
};
export default Movie;
render() {
const {isLoading, movies} = this.state;
return (
<div>
{isLoading
? "Loading..."
: movies.map(movie => {
console.log(movie);
return <Movie
key={movie.key} //child는 유일한 key값을 가지고 있어야한다. react가 식별하기 위한
id={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
/>
})}
</div>
);
}
#4.2 Styling the Movies
방법1) 태그에 style 속성지정
<h3 class="movie__title" style=>{title}</h3>
방법2) css 파일생성
모든 component에 대한 css파일을 만들 수 있고, 하나의 css파일에 모든 설정을 넣을 수도 있다.
#4.3 Adding Genres
⇒ 유효하지 않은 DOM property class
HTML처럼 보이지만 이건 JSX, javascript 이기 때문이다. 우리는 class가 아니라 className
이라고 써야한다.
render() {
const {isLoading, movies} = this.state;
return (
<section class="container">
{isLoading ? (
<div class="loader">
<span class="loader__text">Loading...</span>
</div>
) : (
<div class="movies">
{movies.map(movie => (
<Movie
key={movie.key} //child는 유일한 key값을 가지고 있어야한다. react가 식별하기 위한
id={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
genres={movie.genres}
/>
))}
</div>
)}
</section>
);
}
#5.0 Deploying to Github Pages
- gh-pages 설치
-
github에 업로드하는 것을 허가해주는 모듈
- gh-pages : 웹사이트를 github의 github page 도메인에 나타나게 해준다.
- github에서 무료 웹사이트를 제공해준다.
npm -i gh-pages
- package.json 설정
- homepage추가
"homepage": "http://blossun.github.io/movie_app"
git remote -v
으로 username, project명 확인
https://{you github username}.github.io/{the name of your project in github}
- deploy scripts 작성
gh-pages 호출해서 우리의 빌드된 폴더를 업로드
- 빌드 경로 확인
npm run build
→ build 디렉토리가 생성된다.
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"deploy": "gh-pages -d build", //<-- build 경로와 함께 depoly 추가. build 폴더를 업로드
"predeploy": "npm run build"
}
매 순간 우리가 deploy를 호출할 때마다 predeploy를 호출한다.
predploy로 build를 실행하고, build에서 build를 위한 스크립트를 실행한다. 그리고 생성된 build를 deploy(배포)한다.
- deploy 실행
npm run deploy