[REACT] ReactJS로 영화 웹 서비스 만들기
(((((((((((기이이인 글 주의. 한 강의를 한 포스트에 정리함)))))))))))

당장 금요일부터 리액트를 써야한다.
진짜 .. 큰일났다.
이 글은 강의의 목차를 그대로 가져와서 그 순서대로 정리할 예정이다. 목차는 다음과 같다.
러닝커브가 높은 react이기 때문에 오히려 기초를 확실히 정리해두어야 할 것 같다.
그게 바로 오늘이 되길 바라면서 시작해본다.
11 STATE
#11.0 Class Components and State (10:11)
#11.1 All you need to know about State (07:55)
#11.2 Component Life Cycle (08:07)
#11.3 Planning the Movie Component (05:02)
1 [2021 UPDATE] INTRODUCTION
#1.1 ❤️ 무료 강의 ❤️
페이스북은 여전히 react js 사용하고 있으며 여전히 살아남았다. 프론트앤드 개발자로서 좋은 도구가 될 것!
#1.2 Why React
왜 react js를 배우는 것이 중요한지, 그리고 얼마나 안전한지
처음엔 매우 위험했지만 오늘날엔 아주 좋은 선택.
1. 신기술을 배울땐 누가 이 기술을 사용하는 지, 얼마나 규모가 큰 지 봐야한다. 상위 1만개의 웹 사이트중 44.76%가 리액트를 사용한다. 가장 많이 사용한다고 보면 됨.
에어비앤비, 인스타그램, 페이스북, 넷플릭스가 사용중
2.이런 거대한 회사에서 연구하는 사람은 프론트엔드를 크게 바꾸고 싶지 않아한다. 자주 수정하거나 그럴 시간이 없음.
안정적인것을 원한다. 이런 사람들이 이용하는 것을 배우는 것은 아주 좋은 선택이 될 것.
3. 큰 커뮤니티를 갖고 있음. 자바스크립트랑 매우 비슷하기 때문. 대부분 자바스크립트 작업임.
이후 리액트 네이티브에 쓸 수 있음.
#1.3 Requirements
자바스크립트 기초가 있어야함. 바닐라 js 로 크롬앱 만들기 해오라고 했지만 이미 해봤기도 하고.. pass
2 [2021 UPDATE] THE BASICS OF REACT
리액트가 해결하려고 하는 문제는?
REACT JS는 UI를 interactive 하게 만들어줌.
바닐라js와 react 코드를 비교

1. html 만들기
2. javascript 가져오기
3. event 감지
4. 데이터 업데이트
5. html업데이트
이대로 작업하다간 아주 혼란스러워질 수 있음
더 좋은 방법 : REACT
reactdhk react-dom 스크립트 추가 먼저 하자.
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>

<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script>
const root = document.getElementById("root");
const span = React.createElement("span", [content]);
ReactDOM.render(span, root)
</script>
</html>

const root = document.getElementById("root");
const span = React.createElement(
"h3", {
id: "prettyspan",
style: {color: "blue"},
onMouseEnter: () => console.log("mouse enter!")
}, "Hello I'm a span");
const btn = React.createElement(
"button", {
onClick: () => console.log("i'm clicked")
}, "Click me");
const container = React.createElement("div", null, [span, btn]);
ReactDOM.render(container, root)


const span = React.createElement(
"h3", {
id: "prettyspan",
style: {color: "blue"},
onMouseEnter: () => {
count++;
console.log("mouse enter! ");
console.log(count);
}
}, "Hello I'm a span");
const btn = React.createElement(
"button", {
onClick: () => console.log("i'm clicked")
}, "Click me");
const Title = (
<h3 id="title" onMouseEnter={() => console.log("mouse enter!")}>
"Hello I'm a span"
</h3>
);
const Button = (
<button
style={{
backgroundColor: "tomato"
}}
onClick={() => console.log("i'm clicked")}
>
click me!
</button>
);
button element도 마찬가지로 만들어주었다.
이대로 live server를 돌리면 에러가 뜨는데,


우리는 지금 배우고 있어서 이 방식으로 진행하는 거지, 혼자할 때는 절대 이렇게 할 일 없을것 느리기때문. 더 나은 방식이 있음

babel을 import해주고 script 안에 type을 지정해주면 이렇게 잘 뜨는 것을 확인할 수 있다.
function Title() {
return(
<h3 id="title" onMouseEnter={() => console.log("mouse enter!")}>
"Hello I'm a span"
</h3>
);
};
const Button = ()=> (
<button
style={{
backgroundColor: "tomato"
}}
onClick={() => console.log("i'm clicked")}
>
click me!
</button>
);
const Container = (
<div>
<Title />
<Button />
</div>
);
<script type="text/babel">
const root = document.getElementById("root");
let counter = 0;
function countUp(){
counter = counter+1;
}
const Container = () => (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={countUp}> click me!</button>
</div>
);
ReactDOM.render(<Container />, root)
</script>

우리가 countUp 함수가 실행될때마다 render을 다시 실행해주어야 한다.
<script type="text/babel">
const root = document.getElementById("root");
let counter = 0;
function countUp(){
counter = counter+1;
render()
}
function render(){
ReactDOM.render(<Container />, root)
}
const Container = () => (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={countUp}> click me!</button>
</div>
);
render();
</script>
이렇게 render함수를 만들어주면 매번 ui가 변경된다.
하지만 이건 best가 아니다.

<script type="text/babel">
const root = document.getElementById("root");
function Container (){
return (
<div>
<h3>Total clicks: 0</h3>
<button> click me!</button>
</div>
);
}
ReactDOM.render(<Container />, root)
</script>
const [counter, modifier] = React.useState(0);
<script type="text/babel">
const root = document.getElementById("root");
function App (){
const [counter, setCounter] = React.useState(0);
return (
<div>
<h3>Total clicks: {counter}</h3>
<button onClick={()=>{setCounter(counter+1)}}> click me!</button>
</div>
);
}
ReactDOM.render(<App />, root)
</script>
setCounter(counter+1);
setCounter((current)=>current+1);
<script type="text/babel">
const root = document.getElementById("root");
function App (){
const [minutes, setMinutes] = React.useState();
const onChange = (event) => {
setMinutes(event.target.value);
}
return (
<div>
<label for="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}/>
<h4>You want to convert {minutes}</h4>
<label>Hours</label>
<input placeholder="Hours" type="number"/>
</div>
);
}
ReactDOM.render(<App />, root)
</script>

근데 여기서 onChange property를 지워주면 아무리 입력하려 해도 디폴트값으로 그대로 고정됨. 그래서 value와 onchange 둘다 필요
#3.6 State Practice part One
<script type="text/babel">
const root = document.getElementById("root");
function App (){
const [minutes, setMinutes] = React.useState();
const onChange = (event) => {
setMinutes(event.target.value);
}
return (
<div>
<h1>Super Converter</h1>
<label for="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange}/>
<label for="hours">Hours</label>
<input value={minutes/60} placeholder="Hours" type="number" disabled/>
<button onClick={()=>{setMinutes(0)}}>reset</button>
</div>
);
}
ReactDOM.render(<App />, root)
</script>
minute을 hour로 변환

<script type="text/babel">
const root = document.getElementById("root");
function App (){
const [minutes, setMinutes] = React.useState(0);
const [hours, setHours] = React.useState(0);
const [flipped, setFlipped] = React.useState(false);
const onChange = (event) => {
if(!flipped) {
setMinutes(event.target.value);
setHours(event.target.value/60);
}
else {
setHours(event.target.value);
setMinutes(event.target.value*60);
}
};
const reset = () => {
setMinutes(0);
setHours(0);
};
const onFlip = () => {
setFlipped((current)=> !current);
reset();
};
return (
<div>
<h1>Super Converter</h1>
<label for="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled ={flipped}/>
<label for="hours">Hours</label>
<input value={hours} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled ={!flipped} />
<button onClick={reset}>reset</button>
<button onClick={onFlip}>flip</button>
</div>
);
}
ReactDOM.render(<App />, root)
</script>
이렇게 하면 버튼 더 user-friendly
<button onClick={onFlip}>{flipped? "분->시로 바꾸기" : "시->분으로 바꾸기"}</button>
const root = document.getElementById("root");
function MinutesToHours(){
const [minutes, setMinutes] = React.useState(0);
const [hours, setHours] = React.useState(0);
const [flipped, setFlipped] = React.useState(false);
const onChange = (event) => {
if(!flipped) {
setMinutes(event.target.value);
setHours(event.target.value/60);
}
else {
setHours(event.target.value);
setMinutes(event.target.value*60);
}
};
const reset = () => {
setMinutes(0);
setHours(0);
};
const onFlip = () => {
setFlipped((current)=> !current);
reset();
};
return (
<div>
<label for="minutes">Minutes</label>
<input value={minutes} id="minutes" placeholder="Minutes" type="number" onChange={onChange} disabled ={flipped}/>
<label for="hours">Hours</label>
<input value={hours} id="hours" placeholder="Hours" type="number" onChange={onChange} disabled ={!flipped} />
<button onClick={reset}>reset</button>
<button onClick={onFlip}>{flipped? "분->시로 바꾸기" : "시->분으로 바꾸기"}</button>
</div>
);
};
function KmToMiles(){
const [km, setKm] = React.useState(0);
const [miles, setMiles] = React.useState(0);
const [flipped, setFlipped] = React.useState(false);
const onChange = (event) => {
if(!flipped) {
setKm(event.target.value);
setMiles(event.target.value/1.609344);
}
else {
setMiles(event.target.value);
setKm(event.target.value*1.609344);
}
};
const reset = () => {
setKm(0);
setMiles(0);
};
const onFlip = () => {
setFlipped((current)=> !current);
reset();
};
return (
<div>
<label htmlFor="km">KM</label>
<input value={km} id="km" placeholder="km" type="number" onChange={onChange} disabled ={flipped}/>
<label htmlFor="miles">miles</label>
<input value={miles} id="miles" placeholder="miles" type="number" onChange={onChange} disabled ={!flipped} />
<button onClick={reset}>reset</button>
<button onClick={onFlip}>{flipped? "km->miles로 바꾸기":"miles->km로 바꾸기"}</button>
</div>
);
};
function App (){
const [index, setIndex]=React.useState("xx");
function onSelected (event){
setIndex(event.target.value);
}
return (
<div>
<h1>Super Converter</h1>
<select value={index} onChange={onSelected}>
<option value="xx">
Selet
</option>
<option value="0">
Minutes to Hours
</option>
<option value="1">
Km to Miles
</option>
</select>
{index==="0" ? <MinutesToHours /> : null}
{index==="1" ?<KmToMiles /> : null}
</div>
);
};
ReactDOM.render(<App />, root)



const root = document.getElementById("root");
function Btn({bgcolor, text, big}){
return <button style={{
backgroundColor: bgcolor,
color: "tomato",
padding:"10px 20px",
margin: "10px 5px",
border: "1px solid black" ,
borderRadius: 10,
fontSize: big ? 18 : 16
}}> {text} </button>;
}
function App (){
return (
<div>
<Btn text="Save Changes" bgcolor="white" big={true}/>
<Btn text="Continue" bgcolor="black" big={false}/>
</div>
);
}
ReactDOM.render(<App />, root)

const MemorizedBtn = React.memo(Btn);
function App (){
const [value, setValue] = React.useState("Save Changes");
function changeValue(){
setValue("Revert Changes");
}
return (
<div>
<MemorizedBtn text={value} onClick={changeValue} />
<MemorizedBtn text="안녕" />
</div>
);
}
<script type="text/babel">
const root = document.getElementById("root");
function Btn({text,onClick}){
console.log(text," was rendered");
return <button
onClick={onClick}
style={{
backgroundColor: "black",
color: "tomato",
padding:"10px 20px",
margin: "10px 5px",
border: "1px solid black" ,
borderRadius: 10,
}}
> {text} </button>;
}
const MemorizedBtn = React.memo(Btn);
MemorizedBtn.propTypes={
text: PropTypes.string.isRequired,
onClick: PropTypes.func,
};
function App (){
const [value, setValue] = React.useState("Save Changes");
function changeValue(){
setValue("Revert Changes");
}
return (
<div>
<MemorizedBtn text={value} onClick={changeValue} />
<MemorizedBtn text="안녕" />
</div>
);
}
ReactDOM.render(<App />, root)
</script>
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// prop가 특정 JS 형식임을 선언할 수 있습니다.
// 이것들은 기본적으로 모두 선택 사항입니다.
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 랜더링 될 수 있는 것들은 다음과 같습니다.
// 숫자(numbers), 문자(strings), 엘리먼트(elements), 또는 이러한 타입들(types)을 포함하고 있는 배열(array) (혹은 배열의 fragment)
optionalNode: PropTypes.node,
// React 엘리먼트.
optionalElement: PropTypes.element,
// React 엘리먼트 타입 (ie. MyComponent)
optionalElementType: PropTypes.elementType,
// prop가 클래스의 인스턴스임을 선언할 수 있습니다.
// 이 경우 JavaScript의 instanceof 연산자를 사용합니다.
optionalMessage: PropTypes.instanceOf(Message),
// 열거형(enum)으로 처리하여 prop가 특정 값들로 제한되도록 할 수 있습니다.
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 여러 종류중 하나의 종류가 될 수 있는 객체
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 특정 타입의 행렬
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 특정 타입의 프로퍼티 값들을 갖는 객체
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 특정 형태를 갖는 객체
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 추가 프로퍼티에 대한 경고가 있는 객체
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 위에 있는 것 모두 `isRequired`와 연결하여 prop가 제공되지 않았을 때
// 경고가 보이도록 할 수 있습니다.
requiredFunc: PropTypes.func.isRequired,
// 모든 데이터 타입이 가능한 필수값
requiredAny: PropTypes.any.isRequired,
// 사용자 정의 유효성 검사기를 지정할 수도 있습니다.
// 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
// `oneOfType`안에서는 작동하지 않으므로 `console.warn` 혹은 throw 하지 마세요.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// `arrayOf` 와 `objectOf 에 사용자 정의 유효성 검사기를 적용할 수 있습니다.
// 검사 실패 시에는 에러(Error) 객체를 반환해야 합니다.
// 유효성 검사기는 배열(array) 혹은 객체의 각 키(key)에 대하여 호출될 것입니다.
// 유효성 검사기의 첫 두 개의 변수는 배열 혹은 객체 자신과 현재 아이템의 키입니다.
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
Button.js
import PropTypes from "prop-types";
import style from "./Button.module.css"
function Button({text}){
return (
<button className={style.btn}>{text}</button>
);
}
Button.propTypes = {
text : PropTypes.string.isRequired,
};
export default Button;

.title{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 18px;
}
Dependency
import {useState, useEffect} from "react";
function App() {
const [counter, setValue] = useState(0);
const [keyword, setKeyword] = useState("");
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(()=>{
console.log("Search for", keyword);
}, [keyword]);
return (
<div>
<input
value={keyword}
onChange={onChange}
type="text"
placeholder="Search here..."
/>
<h1> {counter} </h1>
<button onClick={onClick} >click</button>
</div>
);
}
export default App;

useEffect(()=>{
if(keyword.length>5)
console.log("Search for", keyword);
}, [keyword]);
이렇게 조건도 추가할 수 있다.

[] 이 안의 것을 리액트가 지켜보고 있다고 생각하면 쉽다. 아무것도 없는 건 지켜봐도 아무일이 없으니까 처음 실행될 때만 콘솔로그가 작동하는 것이다. 리스트이기 때문에 [counter, keyword] 이렇게 넣어도 된다. 둘 중 하나가 실행될 때 코드가 작동한다.
import {useState, useEffect} from "react";
function Hello(){
useEffect(()=>{
console.log("I'm here :)");
return () => console.log("destroyed :(")
},[]);
return <h1>Hello</h1>
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev)=>!prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing?"hide":"show"}</button>
</div>
);
}
export default App;

import {useState, useEffect} from "react";
function Hello(){
function effectFn(){
console.log("created :)");
return byeFn
}
function byeFn(){
console.log("bye~ .. ")
}
useEffect(effectFn,[]);
return <h1>Hello</h1>
}
function App() {
const [showing, setShowing] = useState(false);
const onClick = () => setShowing((prev)=>!prev);
return (
<div>
{showing ? <Hello /> : null}
<button onClick={onClick}>{showing?"hide":"show"}</button>
</div>
);
}
export default App;
import { 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>
</div>
);
}
export default App;
setToDos((currentArray) => [toDo, ...currentArray]);
setToDo("");
import { 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 />
{toDos.map((item, index)=>
<li key={index}>{item}</li>
)}
</div>
);
}
export default App;

import { useState, useEffect } from "react";
function App() {
const [loading, setLoading]= useState(true);
const [coins, setCoins] = useState([])
useEffect(() => {
fetch("http://api.coinpaprika.com/v1/tickers")
.then((response) => response.json())
.then((json) => {
setCoins(json);
setLoading(false);
});
}, []);
return (
<div>
<h1>The Coins!({coins.length})</h1>
{loading ? <strong>Loading...</strong>:
<ul>
{coins.map((coin)=> (
<li>
{coin.name} ({coin.symbol}): ({coin.quotes.USD.price}) USD
</li>
))}
</ul>
}
</div>
);
}
export default App;
import { useState, useEffect } from "react";
function App() {
const [coins, setCoins] = useState([])
const [input, setInput] = useState(0);
const [selected, setSelected] = useState(0);
function onChanged(event){
setInput(event.target.value);
console.log(input);
}
function onSelected(event){
setSelected(event.target.value);
console.log(selected);
}
useEffect(() => {
fetch("http://api.coinpaprika.com/v1/tickers")
.then((response) => response.json())
.then((json) => {
setCoins(json);
});
}, []);
return (
<div>
<h1>The Coins!({coins.length})</h1>
<form>
<input value={input} onChange={onChanged} type="number" placeholder="input money"></input>
</form>
{input!==0 ?
<div>
<select value={selected} onChange={onSelected}>
{coins.map((coin, index)=> (
<option key={index} value={coin.quotes.USD.price}>
{coin.name} ({coin.symbol}): ({coin.quotes.USD.price}) USD
</option>
))}
</select>
<h1>{input/selected}</h1>
</div>
: null
}
</div>
);
}
export default App;
import { useState, useEffect } from "react";
function App() {
const [movies, setMovies] = useState([])
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")
.then((response) => response.json())
.then((json) => {
console.log(json);
});
}, []);
return (
<div>
{loading ? <h1>Loading...</h1> : null}
</div>
);
}
export default App;

useEffect(() => {
fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")
.then((response) => response.json())
.then((json) => {
setMovies(json.data.movies);
console.log(movies);
setLoading((cur)=>!cur);
});
}, []);

import { useState, useEffect } from "react";
function App() {
const [movies, setMovies] = useState([])
const [loading, setLoading] = useState(true);
const getMovies = async() => {
const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
return (
<div>
{loading ? <h1>Loading...</h1> : null}
</div>
);
}
export default App;
import { useState, useEffect } from "react";
function App() {
const [movies, setMovies] = useState([])
const [loading, setLoading] = useState(true);
const getMovies = async() => {
const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading
? <h1>Loading...</h1>
: <div>
{movies.map(movie=>
<div key={movie.id}>
<img src={movie.medium_cover_image}></img>
<h2>{movie.title}</h2>
<p>{movie.summary}</p>
<ul>
{movie.genres.map((g)=>
<li key={g}>{g}</li>)}
</ul>
</div>
)}
</div>
}
</div>
);
}
export default App;
import { useState, useEffect } from "react";
import Movie from "./Movie";
function App() {
const [movies, setMovies] = useState([])
const [loading, setLoading] = useState(true);
const getMovies = async() => {
const json = await(await fetch("https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year")).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
getMovies();
}, []);
console.log(movies);
return (
<div>
{loading ? <h1>Loading...</h1>
: <div>
{movies.map(movie=>
<Movie
key={movie.id}
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
)}
</div>
}
</div>
);
}
export default App;
import PropTypes from "prop-types";
function Movie({ coverImg, title, summary, genres}){
return(
<div>
<img src={coverImg} alt={title}></img>
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((g)=>
<li key={g}>{g}</li>)}
</ul>
</div>
);
}
Movie.propTypes={
coverImg: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired
};
export default Movie;
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
return <Router>
<Switch>
<Route path="/movie">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>;
}
export default App;
import {
BrowserRouter as Router,
Route,
Routes,
} from "react-router-dom";
import HomePage from "./routes/HomePage";
import AboutPage from "./routes/AboutPage";
function App() {
return <Router>
<Routes>
<Route path="/" element={<HomePage />}>
</Route>
<Route path="/about" element={<AboutPage />}>
</Route>
</Routes>
</Router>;
}
export default App;

import PropTypes from "prop-types";
import {Link} from "react-router-dom";
function Movie({coverImg, title, summary, genres}){
return(
<div>
<img src={coverImg} alt={title}></img>
<h2>
<Link to="/movie">{title}</Link>
</h2>
<p>{summary}</p>
<ul>
{genres.map((g)=>
<li key={g}>{g}</li>)}
</ul>
</div>
);
}
<Link to={`/movie/${id}`}>{title}</Link>

<Route path="/movie/:id">
<Detail />
</Route>
GitHub - chamroro/ReactMovieService
Contribute to chamroro/ReactMovieService development by creating an account on GitHub.
github.com
]
},
"homepage":"https://chamroro.github.io/ReactMovieService"
}
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"deploy": "gh-pages -d build",
"predeploy": "npm run build"
},
위와 같이 추가하면 build 폴더를 삭제하고 npm run deploy이렇게 하면 predeploy 가 된 후에 buld를 한다.



React App
chamroro.github.io