1.React
1. Why React? (왜 React를 사용해야할까?)
-
첫째, 대부분의 웹사이트들은 React를 사용한다는점
-
둘째, 더 나은 자바스크립트를 개발자로 만들어준다.
2. SET UP
-
기존에는, Webpack을 다운로드 , Babel을 다운로드 하고 , React 를 Compile을 해야하고 그런다음에 다른 파일에 넣어야하고 등등을 했어야 했지만 지금은 손쉽게 React App을 만들수 잇게 되엇다.
-
첫째, NPM 설치한다.
- node.js를 다운받으면 알아서 설치됨
-
둘째, yarn으로 NPX를 설치한다.
- yarn global add npx
-
셋째, react application을 만듬
- npx create-react-app 앱이름
-
넷째, react 앱을 설치한 곳으로 이동
- cd 앱이름
-
다섯째, yarn start 명령어로 react를 시작함
- yarn start
3. 부가적인 React의 장점
-
첫째, 웹 페이지가 자동으로 refresh 된다.
- 기존에는 우리가 Open with server를 사용하였거나, gulp를 이용할때는 watch라는걸 사용했어야했지만, react는 그런것 없이도 자동으로 refresh된다.
-
둘째, babel 없이도 js에서 import가 된다.
- javascript에서 최신의 skill인 import를 사용하기 위해서는 babel을 통해서 최신의 기술을 옛것의 기술로 변환해주는 babel을 사용해야했지만 , react에서는 babel 없이도 된다.
4. JSX , Component
-
1.JSX(javascript XML) 란 ?
- 자바스크립트 언어의 확장판이며, 리액트 컴포넌트(구성요소)를 만들 때 사용하는 언어 , Javascript와 HTML을 같이 묶어놓은 문법 , render()함수로 이를 구현하게 됩니다.
-
2.Component 란 ?
- HTML를 반환하는 함수
function App() { return ( <div> <h1>Hello</h1> </div> ); } export default App;
-
Component를 사용하는 방법
<App />
4.1 Custom Component
potato.js
import React from "react"; // 무조건 react를 import해야한다.
function Potato() {
return <h3>I love potato</h3>;
}
export default Potato;
index.js
- bad example
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Potato from "./potato";
ReactDOM.render(<App/><Potato />, document.getElementById("root"));
-
good example : react application이 하나의 component만을 rendering 해야하기 때문이다.
- 따라서 app 컴포넌트에 모든것이 들어가서 App으로 랜더링 해줘야한다.
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Potato from "./potato";
ReactDOM.render(<App />, document.getElementById("root"));
- available
import React from "react";
import Potato from "./potato";
function App() {
return (
<div>
<h1>Hello</h1>
<Potato />
</div>
);
}
4.2 React의 Component를 사용함으로써의 장점
- 첫번째, Component를 재사용 할수 있다.
import React from "react";
function Potato() {
return <h1>hihi</h1>;
}
function App() {
return (
<div className="App">
<Potato />
<Potato />
<Potato />
<Potato />
</div>
);
}
export default App;
-
두번째, Component의 props 를 통해 데이터를 보낼수 있다.(부모Component에서 자식Component로 데이터를 전달함)
-
props는 일반적으로 고정적인 데이터를 전달할때 사용
-
한개 이상의 component가 사용될때는 div,main 등 으로 감싸줘야한다.
-
import React from "react";
//same function
// function Food(props) {
// console.log(props) // object
// return <h1>I like {props.fav}</h1>;
// }
function Food({ fav }) {
return <h1>I like {fav}</h1>;
}
function App() {
return (
<div>
<h1>Hello</h1>
<Food fav="kimchi" />
<Food fav="ramen" />
<Food fav="pizza" />
</div>
// div로 감싸주지 않으면 오류 발생
);
}
-
tip) ES6 기능중 하나로 {}를 사용하여 object 내부의 키값을 바로 받을수 있다.
-
bad example : ES6의 {}를 사용할경우, object의 내부의 키를 받을수 있다고 했는데, 전달되는 데이터의 키는 favorite인데 내가 받는 키는 fav 이므로 키값을 제대로 받을수 없다.
function Food({ fav }) {
return <h1>I like {fav}</h1>;
}
function App() {
return (
<div>
<h1>Hello</h1>
<Food favorite="kimchi" />
<Food favorite="ramen" />
<Food favorite="pizza" />
</div>
);
}
- good example
function Food({ favorite }) {
return <h1>I like {favorite}</h1>;
}
// function Fodd(props){
// return <h1>I like {props.favorite}</h1>;
// }
function App() {
return (
<div>
<h1>Hello</h1>
<Food favorite="kimchi" />
<Food favorite="ramen" />
<Food favorite="pizza" />
</div>
);
}
- 세번째, props로 boolean , 문자열 , 배열등 다양한 형태의 정보 props를 전달할수 있다.
import React from "react";
function Potato({ somthing }) {
return (
<div>
<div>{somthing[0]}</div>
{/* 결과값 1 */}
<div>{somthing}</div>
{/* 결과값 12345 */}
</div>
);
}
function App() {
return (
<div className="App">
<Potato fav="안녕" somthing={[1, 2, 3, 4, 5]} />
</div>
);
}
4.3. Component 의 효율적인 사용방법
- Inefficient(효율적이지않은) example , 이런 방식으로는 외부에서 오는 동적인 데이터들을 모두 받을수 없다.
function Food({ favorite }) {
return <h1>I like {favorite}</h1>;
}
function App() {
return (
<div>
<h1>Hello</h1>
<Food favorite="kimchi" />
<Food favorite="ramen" />
<Food favorite="pizza" />
</div>
);
}
- good example : 동적 데이터 추가방법
import React from "react";
//img 테그는 항상 src 와 alt 속성을 필요로 하므로 alt를 꼭 써줘야한다.
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 Food({ name, picture }) {
return (
<div>
<h2>I like {name}</h2>
<img src={picture} alt={name} />
</div>
);
}
function App() {
// ES6 : arrow function은 return을 생략할수 잇다.
return (
<div>
{foodILike.map((dish) => (
<Food name={dish.name} picture={dish.image} />
))}
</div>
);
// ES5 : arrow function을 쓰지 않았기 때문에 map 함수 사용시 return을 반드시 사용해야함
// <div>
// <div>
// {foodILike.map(function (dish) {
// return <Food name={dish.name} picture={dish.image} />;
// })}
// </div>
// </div>
}
export default App;
tip)
-
JSX 에서 {}는 자바스크립트를 시작하겠다는 의미임. 따라서 foodLike.map()이 가능하 다.
-
img 테그는 꼭 src 와 alt 속성을 필요로 한다. (만약 안써넣어줄경우 , JSX에서 알려준다.—-> react의 장점)
-
두개의 데이터(파라미터) 를 보내기 위해서는, ({}) 안에 전달되는 데이터의 값을 적어준다.
-
Es6를 사용할경우 return이 생략됨을 기억하고 , Es5를 사용할시에는 return을 써줘야한다.
4.4 Components 의 유일성
-
1.Component의 에러 발생
-
5번처럼 코드를 진행할경우 다음과 같은 오류가 발생한다
-
index.js:1446 Warning: Each child in a list should have a unique “key” prop. Check the render method of
App
. See https://fb.me/react-warning-keys for more information.
-
-
2.문제원인
- 왜냐하면 react의 모든 element들은 다르게 보일 필요가 있기 때문이다.
-
3.해결방법
- 각 component에 key 속성을 부여한다.
import React from "react";
function Food({ name, picture }) {
return (
<div>
<h2>I like {name}</h2>
<img src={picture} alt={name} />
</div>
);
}
const foodILike = [
{
id: "1",
name: "Kimchi",
image:
"http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg",
},
{
id: "2",
name: "Samgyeopsal",
image:
"https://3.bp.blogspot.com/-hKwIBxIVcQw/WfsewX3fhJI/AAAAAAAAALk/yHxnxFXcfx4ZKSfHS_RQNKjw3bAC03AnACLcBGAs/s400/DSC07624.jpg",
},
{
id: "3",
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",
},
{
id: "4",
name: "Doncasu",
image:
"https://s3-media3.fl.yelpcdn.com/bphoto/7F9eTTQ_yxaWIRytAu5feA/ls.jpg",
},
{
id: "5",
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} key={dish.id} />
))}
</div>
);
}
export default App;
- 여기서 특징은 key prop 는 Food 함수로 전달되지 않는다, 이걸 사용하는것 아니라 기본적으로 react내부에서 사용하기 위한것이기 때문이다.
4.5. Components 의 속성(props)에서 발생할수 있는 문제를 사전에 해결해주는 PropTypes
- 발생할수 있는 문제 : props의 값을 잘못 보냈을때(image가 아닌 boolean값을 보냄), 또는 원하지 않는 props를 보냈을때(rating을 props로 보내야하는데 score을 보냄)
import React from "react";
function Food({ name, picture, rating }) {
return (
<div>
<h2>I like {name}</h2>
<h4>{rating}/5.0</h4>
<img src={picture} alt={name} />
</div>
);
}
const foodILike = [
{
id: 1,
name: "Kimchi",
image:
"http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg",
},
{
id: 2,
name: "Samgyeopsal",
image:
"https://3.bp.blogspot.com/-hKwIBxIVcQw/WfsewX3fhJI/AAAAAAAAALk/yHxnxFXcfx4ZKSfHS_RQNKjw3bAC03AnACLcBGAs/s400/DSC07624.jpg",
rating: 4.9,
},
{
id: 3,
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",
rating: 4.8,
},
{
id: 4,
name: "Doncasu",
image:
"https://s3-media3.fl.yelpcdn.com/bphoto/7F9eTTQ_yxaWIRytAu5feA/ls.jpg",
rating: 5.5,
},
{
id: 5,
name: "Kimbap",
image:
"http://cdn2.koreanbapsang.com/wp-content/uploads/2012/05/DSC_1238r-e1454170512295.jpg",
rating: 4.7,
},
];
function App() {
return (
<div>
{foodILike.map((dish) => (
<Food
name={dish.name}
picture={true}
key={dish.id}
score={dish.rating}
/>
))}
</div>
);
}
export default App;
- 해결방법 : propsTypes 를 사용한다.
-
설치 : npm i prop-types
-
받아오기 : import PropTypes from “prop-types”;
-
역할 : 내가 전달받은 props가 내가 원하는 props인지를 확인해줌 또한 내가 전달받은 데이터의 숫자인지 문자인지도 파악해줌
import React from "react";
import PropTypes from "prop-types";
const foodILike = [
{
id: 1,
name: "Kimchi",
image:
"http://aeriskitchen.com/wp-content/uploads/2008/09/kimchi_bokkeumbap_02-.jpg",
},
{
id: 2,
name: "Samgyeopsal",
image:
"https://3.bp.blogspot.com/-hKwIBxIVcQw/WfsewX3fhJI/AAAAAAAAALk/yHxnxFXcfx4ZKSfHS_RQNKjw3bAC03AnACLcBGAs/s400/DSC07624.jpg",
rating: 4.9,
},
{
id: 3,
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",
rating: 4.8,
},
{
id: 4,
name: "Doncasu",
image:
"https://s3-media3.fl.yelpcdn.com/bphoto/7F9eTTQ_yxaWIRytAu5feA/ls.jpg",
rating: 5.5,
},
{
id: 5,
name: "Kimbap",
image:
"http://cdn2.koreanbapsang.com/wp-content/uploads/2012/05/DSC_1238r-e1454170512295.jpg",
rating: 4.7,
},
];
function Food({ name, picture, rating }) {
return (
<div>
<h2>I like {name}</h2>
<h4>{rating}/5.0</h4>
<img src={picture} alt={name} />
</div>
);
}
Food.propTypes = {
name: PropTypes.string.isRequired,
picture: PropTypes.string.isRequired,
rating: PropTypes.number,
//내가 받을 rating은 number 또는 undefined(정의되지않음) 이어야 오류가 발생안해
};
function App() {
return (
<div>
{foodILike.map((dish) => (
<Food
key={dish.id}
name={dish.name}
picture={dish.image}
rating={dish.rating}
/>
))}
</div>
);
}
export default App;
- TIP
Food.propTypes = {
name: PropTypes.string.isRequired,
picture: PropTypes.string.isRequired,
rating: PropTypes.number,
};
isRequred : 무조건 앞에 정의된 속성만 가능 isRequired X : 정의된 속성이거나 또는 undefined(정의되지 않는) 일때도 가능
따라서 만약 데이터의 rating이 속성이 없더라도, 오류가 발생하지 않음.
4.6 함수형 Component와 Class형 Component 그리고 일반함수
-
함수형 Component : state 나 라이프사이클 API 를 전혀 사용하지 않을 때, 그리고 해당 컴포넌트는 자체 기능은 따로 없고 props 가 들어가면 뷰가 나온다는 것을 명시하기 위해 사용합니다.
- 함수형 Component는 명시적으로 render() 가 생략되어 보이지 않은것일뿐 원래는 있는것이다.
-
Class형 Component : 라이프 사이클을 사용할때나 , 기능이나 state를 사용할때(즉 동적인 데이터를 전달할때) Class형 Component를 사용한다.
-
함수 vs 함수형 Component
-
함수 : 함수는 어떠한 하나의 기능을 사용하기 위함
-
함수형 Component : 화면
-
5. State
5.1 클래스 컴포넌트와 state
-
component는 함수형(function component)과 클래스형(class component)이 있다. 우리가 앞에서 배웠더것은 함수형 Componenent 이고 지금은 Class Component에 대해서 배워볼것 이다.
-
Class Componenent 는 항상 extends React.Component를 상속받아야한다.
-
react는 자동적으로 class component의 render method를 자동으로 실행한다.
-
class component를 사용하는 이유는 state를 사용하기 위함이며 state는 객체이고 component의 data를 넣을공간이 있고 그 데이터는 동적 데이터이다. (즉 변하는 데이터)
-
클래스형 컴포넌트는 내부적으로 props를 가지고 있기 때문에, 생성자를 쓰려면 super(props)를 해줘야한다.
import React from "react";
import PropTypes from "prop-types";
class App extends React.Component {
state = {
count: 0,
};
add = () => {
console.log("add");
};
minus = () => {
console.log("minus");
};
render() {
return (
<div>
<h1>The number is: {this.state.count}</h1>
<button onClick={this.add}>Add</button>
<button onClick={this.minus}>Minus</button>
</div>
);
}
}
export default App;
- TIP
- this.add() 라고 할경우 즉시 실행된다. 우리는 눌렀을때 이 함수가 동작하기를 원하므로 this.add라고 해준다.
5.2 State에 대한 모든것
-
state를 건들기 위해 즉 데이터를 변화시키기 위해서는 this.state 를 쓰는게 아니라 this.setState() 를 사용한다.
-
props는 고정적인 데이터를 전달할때 사용한다면, state는 변경될수 있는 데이터를 처리할 떄 효율적으로 사용된다.
-
react는 state의 변화가 일어났을때, 변화된 state와 함께 render()을 다시 동작시킨다.
-
따라서 props만 이용하고자 한다면(고정적인 데이터 전달) 함수형 컴포넌트를, 값이 변경될 여지가 잇다면(동적인 데이터 전달) 클래스형 컴포넌트로 state를 사용해야한다.
- bad example : 함수 컴포넌트를 이용하여 동적인 데이터를 전달하여 화면에 보여주는 방법,
function tick() {
const element = (
<h3>현재 시각은 [{new Date().toLocaleTimeString()}] 입니다.</h3>
);
ReactDOM.render(element, documentgetElementById(root));
}
setInterval(tick, 1000);
- good example : react를 사용한뒤 동적인 데이터를 전달하여 화면에 보여주는 방법
class Clock extends React.Component{
state = {
date : new Date();
}
tick(){
this.setState({
date: new Date();
})
}
componentDidMount(){
this.timerID = setInterval(()=>this.tick(),1000);
}
render(){
return(
<h3>현재시각은 [{this.state.date.toLocaleTimeString()}]</h3>
)
}
}
ReactDOM.render(<Clock/>, documentgetElementById(root));
- state를 바꾸기 위해서는 setState()함수를 사용해야한다.
- bad example : 이렇게 할경우 state가 증가할수는 있겠다, 하지만 우리는 이 증가된것을 화면에 보여주기위해 render()를 다시 실행해야하는데 그러지 못하므로 좋은방법이 아니다.
import React from "react";
class App extends React.Component {
state = {
count: 0,
};
add = () => {
this.state.count += 1;
};
minus = () => {
this.state.count -= 1;
};
render() {
return (
<div>
<h1>The number is {this.state.count}</h1>
<button onClick={this.add}>Add</button>
<button onClick={this.minus}>Minus</button>
</div>
);
}
}
export default App;
- good example : setState가 실행 되면 react가 알아서 state의 변화가 일어났을때, 변화된 state와 함께 render()을 다시 동작시킨다.(render() -> setState로 state변화시 -> 변화된 state와 함께 render() 가 다시 동작)
import React from "react";
class App extends React.Component {
state = {
count: 0,
};
add = () => {
this.setState({
count: this.state.count + 1, //좋은 코딩은 아니다. why? 외부에서 state를 의존하기 때문에 좋은방법이 아니다.
});
};
minus = () => {
//current = this.state
this.setState((current) => ({
count: current.count - 1, //좋은 코딩이다.
}));
};
render() {
return (
<div>
<h1>The number is {this.state.count}</h1>
<button onClick={this.add}>Add</button>
<button onClick={this.minus}>Minus</button>
</div>
);
}
}
export default App;
5.3 Component의 라이프 사이클
-
React.Component 로 부터 상속받을때, render()말고도 다양한 함수들이 있는데, 그중 대표적인 것들을 살펴보겠다.
-
Mounting : React 컴포넌트 객체가 DOM에 실제로 삽입되기 전까지의 과정, 순서대로 호출됩니다.
- constructor() : 가장 먼저 호출되는 function()으로, javascript에서 class를 만들 떄 호출되는거임
- render()
- componentDidMount() : component가 처음 render 됐는지 알려줌, 따라서 첫 render 때만 동작하고 , 다음 render때는 동작안함
-
Updating : Props 또는 state가 변경되면 갱신이 발생합니다. 아래 메서드들은 컴포넌트가 다시 렌더링될 때 순서대로 호출됩니다.
- render()
- componentDidUpdate() : render()가 실행되고 나서 Props나 state가 변경될때 실행됨
-
Unmounting : 아래 메서드는 컴포넌트가 DOM 상에서 제거될 때에 호출됩니다. 즉 페이지가 바뀔때 실행되는 함수들
- componentWillUnmount()
-
이 순서들 사이에도 다른함수들이 호출되긴하지만, 우리가 알아두어야할것들만 적은것이다. 더 자세하게 보고싶으면 아래 사이트 참조
5.4 라이프 사이클에 따른 API 호출
-
constructor()
- 가장 먼저 호출되는 function()으로, javascript에서 class를 만들 떄 호출되는거임
class App extends React.Component {
state = {
data: "",
};
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
date: "",
};
}
// 클래스형 컴포넌트는 내부적으로 props를 가지고 있기 때문에, super(props)를 해줘야한다.
}
같은 결과를 내지만 둘의 차이는 무엇일까??
-
componentDidMount()
- 기본적으로 컴포넌트가 모두 구성된 직후인 componentDidMount()함수에서 API 호출을 수행하면 효과적이다.
import React from "react"; // import axios from "axios"; class App extends React.Component { constructor(props) { super(props); this.state = { date: "", }; } callApi = () => { fetch("https://jsonplaceholder.typicode.com/todos/1") .then((response) => response.json()) .then((json) => this.setState({ date: json.title, }) ); }; componentDidMount() { this.callApi(); } render() { return ( <h3> {this.state.data ? this.state.data : "데이터를 불러오는 중입니다"} </h3> ); } } export default App;
-
componentDidUpdate()
- 화면에 출력되는 화면 구성을 변경하고자 할때는 ComponentDidUpdate()를 많이 사용한다.
-
componentWillUnmount()
- 컴포넌트의 동작을 위해 사용되었던 메소드들의 리소스를 제거할때 사용한다.
6. React의 이벤트 처리
- 특징으로는 함수를 camelCase를 사용해야한다. 또한 이벤트역시 camelCase를 사용한다. (소문자로 진행하다가 띄어지는 부분에 대문자)
- javascript
<buttion onclick = "activateLaser()">
- react
<buttion onClick = "activateLaser()">
- JSX에 문자열 대신 함수를 전달합니다.
- javascript
<buttion onclick = "activateLaser()">
- react
<button onClick = {activateLaser} >
- javascript는 기본적으로 binding을 제공하지 않으므로, 함수를 binding 처리를 해줘야한다. (binding에 대한 개념은 추후에 알아보자)
- 방법 1 : arrow function 을 사용하면 된다.
-
방법 2 : this.바인딩할 함수 = this.바인딩할함수.bind(this)
- bad example
import React from "react";
// import axios from "axios";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true,
};
// this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((state) => ({
isToggleOn: !this.state.isToggleOn,
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? "On" : "Off"}버튼
</button>
);
}
}
export default App;
- good example
import React from "react";
// import axios from "axios";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true,
};
// this.handleClick = this.handleClick.bind(this)
}
handleClick = () => {
this.setState((state) => ({
isToggleOn: !this.state.isToggleOn,
}));
};
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? "On" : "Off"}버튼
</button>
);
}
}
export default App;
Leave a comment