Nomad Coder의 React.js로 영화 웹 서비스 만들기를 시작해보자!
그동안 그래도 어느 정도 이론적인 요소를 배웠으니 직접 사용해보는 강의를 들어야겠다 싶어서 아마 오늘과 내일로 2일간 마무리 짓으면 될 것 같다.
우선적으로 컴포넌트에 대해서 알아가보자. 컴포넌트는 클래스 컴포넌트와 함수 컴포넌트가 있다. 기능적으로 봤을 때 클래스 컴포넌트가 뛰어났다. 왜냐하면 클래스 컴포넌트는 해당 값이 변경되면 재랜더링을 도와주는 state와 라이프사이클 API를 사용할 수 있었지만, 함수 컴포넌트는 그렇지 못하였다. 그래서 클래스 컴포넌트를 기능적인 면에서 우수했다. 하지만 현재는 다르다. 훅(Hook)을 통해 함수 컴포넌트도 state와 라이프 사이클 API의 기능을 사용할 수 있어서 이제 기능적인 면과 간결한 면에서 함수 컴포넌트를 더 많이 쓰이고 있다.
함수 컴포넌트는 훅을 사용한다. 훅은 use
를 사용하여야 한다. 그래서 여기서 나오는 개념이 바로 useState이다. useState는 길이가 2인 배열이다. 여기서 무슨 요소가 들어가는지 아는 게 중요하다. 첫 번쨰 요소는 우리가 정하고 싶은 state 값이며, 두 번째 요소는 state를 변경하는 함수가 들어간다.
function APP() {
let [counter, modifier] = useState();
let onClick = () => {
modifier( counter + 1 ); // modifier((current) => current +1);
}
return(
<button onClick={onClick}>Click me</button>
);
}
Component가 내부적으로 자신의 상태를 바꾸고 관리하는 데 사용하기 위해 state를 사용한다.
이제 Inputs과 state를 가지고 단위 변환기(unit converter)를 만들어보자.
import react, { useState } from 'react';
function App() {
const [amount, setAmount] = useState(0);
const [flipped, setFlipped] = useState(false);
const onChange = (e) => {
setAmount(e.target.value); // 입력값으로 state 값 변경
console.log(e.target.value); //현재값 출력
const reset = () => setAmount(0);
const onFlip = () => setFlipped((current) => !current);
return(
<div>
<h1 className="hi">Super Converter</h1>
<input
value={flipped? amount*60 : amount} // 삼항연산자 사용
id=minutes
placeholder="Minutes"
type=number
onChange={onChange} //이벤트 리스너
disabled={flipped === true}
/>
<h4>You want to convert {amount}</h4>
<input
value={flipped ? amount : Math.round(amount / 60)}
id=hours
placeholder="Hours"
type=number
disabled={flipped === false}
/>
<button onClick={reset}>Reset</button>
<button onClick={onFlip}>Flipped</button>
</div>
);
}
props : 부모 C => 자식 C
props는 쉽게 말하자면 부모 컴포넌트가 자식 컴포넌트에 데이터를 보낼 수 있게 해주는 방법이다. 그리고 props를 사용하는 이유는 같은 컴포넌트끼리 구별짓기 위해 컴포넌트에 속성을 넣기 위해서다.
React는 state가 바뀌면 재렌더링이 되는 게 일반적이다. 하지만 때때로 특정 코드(API 호출 ... 등등)만 component가 맨 처음이나 특정 시기에 render되길 원한다면, 우리는 무엇이 필요할까?
바로 useEffect이다.
우선 useEffect는 요소가 2개인 배열이다. 첫 번째 인수는 우리가 딱 한 번만 실행하고 싶은 코드이다. 두 번째 인수는 특정 시기를 정하는 state 값을 넣으면 된다.
useEffect( f , [state1, state2]);
import react, {useState, useEffect } from 'react";
function App() {
const [counter, seValue] = useState(0);
const onClick = () => setValue((prev) => prev +1);
const onChange = (event) => setKeyword(event.target.value); // 타이핑 값 출력
console.log("i run all the time");
useEffect(() => {
console.log("CALL THE API..."); // 처음 한 번만 실행
}, []);
useEffect(() => {
if (keyword !== "" && keyword.length > 5) { // 특정 시기
console.log("SEARCH FOR", keyword);
}
}, [keyword]); // keyword가 바뀔 때마다 실행이 돼!
return(
<div>
<input
value={keyword}
onChange={onChange}
placeholder = "Search here..."
></input>
<h1>{counter}</h1>
<button onClick={onClick}>click me</button>
</div>
);
}
useEffect에는 cleanup 기능
이 있다. 이 기능은 바로 컴포넌트가 없어질 때 호출할 수 있도록 하는 기능이다. 이 클린업 기능을 사용하기 위해서는 return
을 사용하면 된다. (이건 익숙지 않아서 검색이 필요하다.)
function Hello() {
useEffect(() => {
console.log("hi");
return () => console.log("bye!"); // clean up 기능
}, []);
}
import react, { useState } from "react";
function App() {
const [toDo, setToDo] = useState("");
const [toDos, setToDos] = useState([]);
const onChange = (event) => setToDo(event.target.value); // 입력 타이핑
const onSubmit = (event) => {
event.preventDefault();
if(toDo ==="") { //아무것도 안 입력할 경우
return;
}
setToDos(currentArray => [toDo, ...currentArray]); //입력값을 배열로 받아온다. (...) 전개연산자 사용
setToDo("");
};
return (
<div>
<h1>My To Dos ({toDos.length})</h1>
<form onSubmit={onSubmit}>
<input
onChange={onChange}
value={toDo}
type="text"
placeholder="Write your to do..."
/>
<button>Add To Do</button>
</form>
<hr />
<ul>
{toDos.map((item)=> <li>{item}</li>)}
</ul>
</div>
);
}
export default App;
import React, { useState, useEffect } from "react";
function CoinTracker() {
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
useEffect(() => {
fetch("https://api.coinpaprika.com/v1/tickers") //암호화폐 API
.then((response) => response.json())
.then((json) =>
setCoins(json));
setLoading(false); //로딩 풀기
}, [])
return(
<div>
<h1>The Coins!</h1>
{loading ? <strong>Loading...</strong> : null}
<ul>
{coins.map((coin) => <li>{coin.name} ({coin.symbol}): ${coin.quotes.USD.price} </li>)}
</ul>
</div>
);
}
export default CoinTracker;
ul & li 대신에 select & option 을 사용하면 아래 이미지와 같이 변한다.