TAB 2022 2학기 학회활동 [팀 스터디]
TIL 포스팅입니다.
작성자 40기_박지민
노마드 코더 - ReactJS로 영화 웹 서비스 만들기
▶Todo 프로그램 예제
import {useState,useEffect} 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 TODOSSS ({toDOs.length})</h1>
<form onSubmit={onSubmit}>
<input
onChange={onChange}
value ={toDo}
type = "text"
placeholder="Write your to do..."/>
</form>
<hr />
<ul>
{toDos.map((item,index)=>(
<li key = {index}>{item}</li>
))}
</ul>
</div>
);
}
export default App;
▷ OnSubmit
- toDo가 비어있게 될 경우를 대비해 onSubmit 내에 if(toDo === ""){return;}을 추가해 toDo가 비었을 때 함수가 작동하지 않도록 한다.
- OnSubmit 실행 시마다 input의 값을 비워주고 싶기 때문에 setTodo("");를 이용해 setTodo함수를 불러온다.
※setTodo는 toDo값을 수정하는 함수고 그 값은 input과 연결되어 있기 때문에 toDo의 값을 변경하면 input 값도 변경!
- toDos는 배열이지만 State이기 때문에 일반적인 방법인 toDos.push()나 toDos = ' ' 로는 수정할 수 없다.
toDos를 수정하는 함수를 따로 만들어줘야 함.
이 때 setToDos(currentArray => ~) 는 setToDos(funciton(currentArray{ return ~ })와 같은 내용를 나타낸다.
▷새 요소에 기존 배열의 요소 추가
- ... 배열명 으로 추가가 가능
Ex) num = [1,2,3,4] , 새 요소 = 5
newNum = [5, num ] → [5,num(4)]
newNum = [5, ...num ] → [5,1,2,3,4]
▷ map()
- 자바스크립트 함수. 배열 각각의 요소들을 바꾸고 싶을 때 사용. 배열의 모든 항목에 대해 실행되며 리턴한 값이 새 배열에 들어간다.
- 배열.map((item(이름변경가능)=>변경내용) 형태로 사용
Ex)
| ['I','am','so',sleepy'].map(()=>":<"); | [ ':<' , ':<' , ':<' , ':<' ] |
| ['I','am','so',sleepy'].map((item)=>item.toUpperCase()); | ['I','AM','SO','SLEEPY'] |
※ {toDos.map((item)=>( <li>{item}</li>))} 의 문제.
console에서 문제를 확인가능. 같은 컴포넌트의 list를 렌더링할 때 key라는 prop가 필요하다는 내용이 뜬다.
리액트가 기본적으로 리스트에 있는 모든 아이템을 인식하기 떄문에 발생하는 문제.
map함수의 인자를 첫번째는 value(toDo), 두번째를 인덱스로 두고 key값을 {index}로 하면 해결된다.
∴ {toDos.map((item,index)=>(
<li key = {index}>{item}</li>
))}
+ 이후 console.log로 만들어진 배열을 확인할 수 있다.
문자열로 구성된 배열 : console.log(toDos)
리액트 요소로서의 배열 : console.log(toDos.map((item,index)=><li key= {index}>{item}</li>))
▶코인트래커 예제
- 암호화폐들과 그 가격을 나열하는 프로그램
import { useEffect, useState } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
useEffect(() => {
fetch("https://api.coinpaprika.com/v1/tickers")
.then((response) => response.json())
.then((json) => {
setCoins(json);
setLoading(false);
});
}, []);
return (
<div>
<h1>The Coins {loading ? "" : `(${coins.length})`} </h1>
{loading ? (
<strong>Loding...</strong>
) : (
<select>
{coins.map((coin) => (
<option>
{coin.name} ({coin.symbol}): {coin.quotes.USD.price} USD
</option>
))}
</select>
)}
</div>
);
}
export default App;
- useEffect
: 해당 함수 내의 내용을 한 번만 사용하기 위해 실행 내용 뒤는 빈 배열로 남겨둔다.
fetch 안 링크에는 코인들의 정보가 담겨있음
response에서 json을 받아오고 setCoins를 통해 coins의 state로 만든다.
이 때 로딩이 끝났으니 loading의 값은 false로
- map()
: map()를 통해 coins 배열의 각 요소들을 name(symbol):USDprice 형태로 한다.
※ coins의 초기값이 []인 이유
- 본래 coins의 초기값은 undefined이기 때문에 길이를 표현하는 coins.length에서 오류 발생.
useState([])를 통해 기본값을 빈 배열로 지정해줘야 한다.
- $ : string(문자열) 의미
▶ 영화 정보 프로그램 예제
▷App.js
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Detail from "./routes/Detail";
import Home from "./routes/Home";
function App() {
return (
<Router>
<Switch>
<Route path="/movie/:id">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
export default App;
▷index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
※ 오류 발생 시 root.render(<App />); 로 변경.
▷Home.js ( in routes(folder) )
import {useEffect,useState} from "react";
import Movie from "../components/Movie";
function Home() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
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>
) : (
<div>
{movies.map((movie) => (
<Movie
key={movie.id}
id = {movie.id}
coverIMG={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
▷Detail.js ( in routes(folder) )
import { useEffect,useState } from "react";
import { useParams } from "react-router-dom";
import MovieDetail from "../components/MovieDetail";
function Detail() {
const [details, setDetails] = useState([]);
const { id } = useParams();
// App.js에 "/movie/:id"로 표기했기 때문에 {id}를 변수명으로 사용.
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
setDetails(json.data.details);
};
useEffect(() => {
getMovie();
}, []);
return(
<div>
{
details.map((movie) => (
<MovieDetail
key={movie.id}
coverIMG={movie.medium_cover_image}
year = {movie.year}
runtime = {movie.runtime}
title = {movie.title}
rating = {movie.rating}
genres = {movie.genres}
/>))}
</div>
);
}
※ return 내의 내용은 잘못된 예시. 추후 다른 포스트에서 수정 예정.
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
function Movie({ id, coverIMG, title, summary, genres }) {
return (
<div>
<img src={coverIMG} alt={title} />
<h2>
<Link to={`/movie/${id}`}>{title}</Link>
</h2>
<p>{summary}</p>
<ul>
<li>
{genres.map((g) => (
<li key={g}> {g}</li>
))}
</li>
</ul>
</div>
);
}
Movie.prototype = {
id: PropTypes.number.isRequired,
coverIMG: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default Movie;
import PropTypes from "prop-types";
function MovieDetail({ coverIMG, title, year, rating,runtime,genres }) {
return (
<div className = 'container'>
<img src={coverIMG} alt={title} />
<div className = "index">
<h2>{title}</h2>
<span>{year}|{runtime}</span>
<>{rating}</>
<ul>
{genres.map(ge =><li key = {ge}>{ge}</li>)}
</ul>
</div>
</div>
);
}
MovieDetail.prototype = {
id: PropTypes.number.isRequired,
coverIMG: PropTypes.string.isRequired,
year: PropTypes.number.isRequired,
runtime: PropTypes.number.isRequired,
rating: PropTypes.number.isRequired,
title:PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default MovieDetail;
※ return 내의 내용은 불완전한 예시. 추후 다른 포스트에서 수정 예정.
▷asyne-await
- then 대신 사용.
const 함수명 = asyne() => {
const json = await(
await fetch(~)).json();
then 후에 들어갔던 내용 ~
}; 형태. 한 번만 실행하기 위해 useEffect(() => { 함수명; }, []); 를 꼭 포함할 것.
▷ map이 이중으로 사용된 이유
: genres가 배열이기 때문.
※ 특정 고유값 없을 때 element가 고유한 값이라면 그 자체를 key로 둘 수 있다. 예제의 {g} 가 그 예.
▷import PropTypes from "prop-types"
- prop-types를 import해서 component 폴더에서 PropTypes를 지정해주기 위함
▷react router
- 터미널에 npm install react-router-dom 입력을 통해 설치.
- react router의 사용을 위해 App.js에 import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 입력.
Switch : router 탐색 후 URL(route)찾으면 컴포넌트 렌더링.
<Switch>
<Route path="/위치"><실행파일명 /></Route>
<Route path="/위치"><실행파일명 /></Route>
<Switch> 형식으로 표현.
이 때 위치 설명 없이 ' / ' 로 표기할 경우 홈 화면으로 간다는 뜻.
※ 링크는 (앞의 주소~ / 위치명 ) 형태로 되어있다.
- BrowserRouter : 위치명 구분을 / 로 하게 한다. HashRouter은 /#/로 구분.
- useParams : react router에서 제공하는 함수 url의 상세정보를 반환한다.
사용할 경우 해당 파일에 import {useParams} form "react-router-dom" 입력
▶깃허브 빌드
1. gh-pages 패키지 설치
2. 터미널에 npm i gh-pages 입력 (package.json으로 확인)
3. 호스팅할 깃허브 페이지 링크를 " 홈페이지 " : " 링크명 " 형식으로 맨 아래 작성
4. scripts () 에 deploy 속성 부여 : "deploy" : "gh-pages -d build" , "predeploy" : "npm run build"
5. 터미널에 npm run build 입력
'22 - 1학기 > 팀 스터디' 카테고리의 다른 글
| [웹 FE 스터디] 7 (0) | 2022.11.10 |
|---|---|
| [웹 FE 스터디] 리액트 토이프로젝트 | useState, useEffect, API, Router (0) | 2022.11.07 |
| [웹 FE 스터디] #7 PRACTICE MOVIE APP (0) | 2022.11.03 |
| [웹 FE 스터디] ReactJs ~6.4 (0) | 2022.11.03 |
| [웹 FE 스터디] 3.6~6.4 (0) | 2022.11.03 |