2022/12/24 약 15:00~
시험이다 뭐다 해서 한동안 작업을 진행하지 못했다...
간만에 하는거라 좀 가물가물 하지만, 화이팅 해서 진행해보자
작업하던 폴더는 react-for-beginners
이번 챕터는 State, Effect, Props 연습할것
CreateReactApp을 사용하면 새로고침할 필요가 없다는듯. 아마 내장된 기능이 아닐까 싶음.
State는 직접적으로 수정할 수 없다.
배열에 내용물을 추가하고 싶어도, 직접 push 하는건 문제가 됨.
food = [1, 2, 3, 4];
[6, food] 로 배열을 합치면, [6, Array(4)]가 됨.
내용물을 합치고 싶으면,
[6 ...food] 로 만들면 [6, 1, 2, 3, 4] 가 된다.
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
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;
}
setToDo("");
setToDos((currentArray) => [toDo, ...currentArray]);
console.log(toDos);
};
return (
<form onSubmit={onSubmit}>
<div>
<h1>My To Dos ({toDos.length})</h1> {/* toDos 배열 내의 개수를 즉각적으로 최신화... */}
<input
onChange={onChange}
value={toDo}
type="text"
placeholder="Write your to do..."
/>
<button>Add To Do</button>
</div>
</form>
);
}
export default App;
============================================================
react.js는 함수의 첫번째 argument로 현재 State를 보낸다.
ex) setToDos((currentArray) => [toDo, ...currentArray]);
JSP의 <% %>처럼, js의 return 내에서는 {}(중괄호)를 써줘야 한다고 함.(뭐라 표현하는지는 모름)
ex) <h1>My To Dos ({toDos.length})</h1> {/* toDos 배열 내의 개수를 즉각적으로 최신화... */}
js에서의 map은 배열 안에 함수를 넣어준다?
map 내의 함수를 기존의 배열에 있는 내용물을 인자로 받아 반복문을 굴리는 것으로 보인다.
for each문과 유사하게 동작하며, state를 설정하던 함수처럼 첫번째 인자에서 기존의 값을 가져온다.
샘플코드
테스트
.map 함수가 원본 데이터에 변화를 주지는 않는다.
<ul>
{/*
key prop이 필요하다고 콘솔에서 오류 발생
{toDos.map((item) => <li>{item}</li>)}
*/}
{toDos.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
참고) .map 함수 문서
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
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;
}
setToDo("");
setToDos((currentArray) => [toDo, ...currentArray]);
};
console.log(toDos);
return (
<div>
<h1>My To Dos ({toDos.length})</h1> {/* toDos 배열 내의 개수를 즉각적으로 최신화... */}
<form onSubmit={onSubmit}>
<input
onChange={onChange}
value={toDo}
type="text"
placeholder="Write your to do..."
/>
<button>Add To Do</button>
</form>
<hr />
<ul>
{/*
key prop이 필요하다고 콘솔에서 오류 발생
{toDos.map((item) => <li>{item}</li>)}
*/}
{toDos.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default App;
============================================
useEffect?
수업코드
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
function App() {
// 2개의 state
// 1개는 로딩, 다른건 코인 리스트를 잠시 갖고 있기 위한 것
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
//component가 처음으로 render되었을때 함수 즉시 실행?
useEffect(() => {
//1. fetch로 데이터 연결
//2. 아마도 string으로 가져온 데이터를 json으로 변환하고, 변환된 json을 출력하는 코드인듯
// .then((response) => response.json())
// .then((json) => console.log(json));
//3. console.log으로 출력하던 데이터들을 state에 저장
//Loading... 글자가 금방 생겼다 사라지는 모습
.then((response) => response.json())
.then((json) => {
setCoins(json);
setLoading(false);
});
}, []); //마지막 인자가 아무것도 주시하지 않으면 단 한번만 실행되게 됨
return (
/*
<div>
<h1>The Coins!</h1>
{loading ? <strong>Loading...</strong> : null}
</div>
*/
//여기서부터 map함수를 활용해볼 것
<div>
<h1>The Coins! ({coins.length})</h1>
{loading ? <strong>Loading...</strong> : null}
<ul>
{coins.map((coin)=>(
<li key={coin.key}>
{coin.name} ({coin.symbol}): ${coin.quotes.USD.price}
</li>
))}
</ul>
</div>
);
}
export default App;
실습과제
//fetch, state, useEffect에 대한 이해도가 낮아서 다소 시간을 과하게 소요하여 해결함...
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
function App() {
const [loading, setLoading] = useState(true);
const [coins, setCoins] = useState([]);
const [money, setMoney] = useState(0);
const [coinSelect, setCoinSelect] = useState(0);
const onChange = (event) => {setMoney(event.target.value)};
const onSelectChange = (event) => {setCoinSelect(event.target.value)};
useEffect(() => {
method:'GET'
})
.then((response) => response.json())
.then((json) => {
setCoins(json);
setLoading(false);
console.log("실행중 : " + coins);
})
.then(console.log(coins)); //콘솔에 출력해보니 비어있는 배열이 출력됨. 위의 setCoins가 완료되기 전에 동작한다는 뜻...
// .then(setCoinSelect(coins[0].quotes.USD.price)); //이렇게 해도 터지는걸 보니, fetch는 동기방식으로 잘 굴러가고 있었다.
// 문제는 setCoins가 완료되기 전에 다음 코드가 실행되어버리는 것
}, []);
// 원인은 setState가 비동기방식으로 실행되는 것에 있는것으로 결론지었다.
// 그래서 useEffect를 하나 더 선언하여 해결하기로 하였다.
// 저번 수업 6.2에서 조건문으로 처음시작 자동동작 방지하고, 2번째인자(배열)안의 값이 변화할때만 동작하도록 한 적이 있었으니 충분히 가능할 것으로 보인다.
useEffect(() => {
if(coins == "" || coins == null) return;
setCoinSelect(coins[0].quotes.USD.price);
}, [coins]);
return (
// USD를 BTC로 돌려봐라.
// select?
<div>
<h1>The Coins! {loading ? "" : `(${coins.length})`}</h1>
내가 갖고있는 달러 입력 : <input
type="number"
value={money}
onChange={onChange}
/> <br />
{loading ? (
<strong>Loading...</strong>
) : (
<select onChange={onSelectChange}>
{coins.map((coin) => (
<option key={coin.key} value={coin.quotes.USD.price}>
{coin.name} ({coin.symbol}): ${coin.quotes.USD.price}
</option>
))}
</select>
)}
<br />
{
coins == "" || coins == null ? "" :
`구매할 수 있는 BTC : ${money / coinSelect}`
}
</div>
);
}
export default App;
============================================================
마지막 프로젝트를 진행할꺼라 함
영화를 보여주고, 영화에 대한 정보도 보여주고, 거기에 링크에 넣어 영화에 대한 정보를 더 많이 찾을 수 있게 연결해준다고 함
앱 안에서 페이지를 전환하는 방법?
쉬우니까 걱정하지 말래...
api를 통해 영화정보들을 가져오고, 조건을 걸어 맞는 애들만 가져오는 ~~~...
fetch에서 await를 활용하는 방법을 배움.
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
function App() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async() => {
//async-await 활용
/*// 사용 예 1)
const response = await fetch(
);
const json = await response.json();
*/
//사용 예 2)
const json = await (await fetch(
)).json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(() => {
/*
//then 활용
fetch(
)
.then((response) => response.json())
.then((json) => {
setMovies(json.data.movies);
setLoading(false);
});
*/
//async-await 활용(위에서 선언한 함수)
getMovies(); // 현재 console.log가 2번 찍히는데, 이는 set이 Movies에서 한번, Loading에서 한번 돌기 때문이다.
}, []);
console.log(movies);
return (
<div>
{loading ? (
<h1>Loading...</h1>
): (
<div>
{movies.map((movie) => (
<div key={movie.id}>
<h2>{movie.title}</h2>
<p>{movie.summary}</p>
<ul>
{movie.genres.map((g) => (
<li key={g}>{g}</li>
))}
</ul>
<img src={movie.medium_cover_image} />
</div>
))}
</div>
)}
</div>
);
}
export default App;
============================================================
React app에서 페이지를 전환하는 방법?
일단 컴포넌트로 코드 분할
npm install react-router-dom?
컴포넌트 단위로 찢어주었음.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
// <React.StrictMode>
<App />,
/* </React.StrictMode>, */
document.getElementById("root")
);
App.js
function App() {
return null;
}
export default App;
Home.js
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
import Movie from "./components/Movie";
function Home(){
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async() => {
const json = await (await fetch(
)).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} //내부랑 별개로 map이 돌고있으니 key
// {/*순서만 같으면 되고, 꼭 인자를 받는 애랑 이름이 같을 필요 없다 */}
// 라고 설명하신 줄 알았는데, api에서 받아온 값에 대한 얘기였던거 같다.
// 순서 바꿔도 잘 동작하는것을 보니, 이름이 같아야 하는것이 맞는듯.
// 이름 다를땐 동작 안하였음 ㅇㅇ
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
</div>
)}
</div>
);
}
export default Home;
Detail.js
function Detail(){
return <h1>Detail</h1>
}
export default Detail;
Movie.js
import PropTypes from "prop-types"
function Movie({coverImg, title, summary, genres }){
//모든 인자에 대한 정보들은 부모 컴포넌트로부터 받아온다.
return (
<div>
<img src={coverImg} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>
{genres.map((g) => (
<li key={g}>{g}</li>
))}
</ul>
</div>
);
}
// Typescript처럼 데이터형 확인해줄 수 있게 하는 그런것
Movie.propTypes = {
coverImg: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
}
export default Movie;
깃허브에 올려두고 싶긴 한데 할줄 모름 ㅋㅋ
현재까지 작업물이고, 내일 이어서 글 작성할 예정
============================================================
20221225 13:26~
리액트 라우터에 대해 알아볼 것
react router dom?
아래링크, 튜토리얼에서 복사한
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
코드 App.js 파일에 임포트... 가 아니라, 내 링크랑 강의 코드랑 좀 달라서 직접 타이핑함.
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
에서 링크(Link)는 나중에 한다고 지움
Tutorial v6.6.1
Tutorial Welcome to the tutorial! We'll be building a small, but feature-rich app that lets you keep track of your contacts. We expect it to take between 30-60m if you're following along. 👉 Every time you see this it means you need to do something in th
reactrouter.com
Router는 Hash 라우터와 Browser 라우터 2 종류가 있다고 함.
import { useState, useEffect } from "react"; // 한번에 여러개를 같은 곳에서 import 할 때 이렇게 할 수도 있나보다
import Movie from "../components/Movie";
위 Home.js에서 Movie 임포트하는 링크 잘못됨. ./가 아니라, ../로 상위폴더
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> {/* Switch는 Route를 찾음. Route는 링크 뒤에 붙는 URL을 의미함. Route를 찾으면 컴포넌트를 렌더링 */}
<Route path="/hello">
<h1>Hello</h1> {/**그냥 이렇게 작성해도 동작함 */}
</Route>
<Route path="/movie"> {/**놀랍게도 Route의 순서가 상관이 있음. 이걸 "/" 보다 아래에 놓으니 먼저 Switch에 걸려서 동작하고, 해당 라우터는 무시되었음 */}
<Detail />
</Route>
<Route path="/"> {/* 이 안에 컴포넌트를 적어줌. path와 URL을 비교해 컨트롤러 맵핑하는 느낌인듯 */}
<Home /> {/**컴포넌트 임포트하여 불러옴 */}
</Route>
</Switch>
</Router>
);
}
export default App;
Browser Router와 다른 라우터의 차이는 URL의 생김새에 있다.
Browser Router의 URL은 보통의 웹사이트처럼 우리가 흔히 상상하는 모양으로 생겼다.
HashRouter은 /#/~~~로 붙는다고 함. 그 외의 차이는 안알려줌. 예시로 보여준것에선 그냥 import만 다르게 해도 링크만 바뀌고 동작함
Switch는 라우트 하나만 동작(렌더링)하게 하기 위해서 사용하였음
리엑트에서 링크를 이동할 때, <a href="movie">를 넣어서 동작시켜도 돌아가지만, 페이지 전체를 재실행한다는 낭비가 있음.
이를 해결하기 위한 컴포넌트가 Link 이다. 브라우저 새로고침 없이 유저를 다른 페이지로 이동시켜주는 컴포넌트라고 한다.
Movie.js
import PropTypes from "prop-types";
import {Link } from "react-router-dom";
function Movie({coverImg, title, summary, genres }){
//모든 인자에 대한 정보들은 부모 컴포넌트로부터 받아온다.
return (
<div>
<img src={coverImg} alt={title} />
<h2>
<Link to="/movie">{title}</Link> {/** Link : 브라우저 새로고침 없이 유저를 다른 페이지로 이동시켜주는 컴포넌트 */}
</h2>
<p>{summary}</p>
<ul>
{genres.map((g) => (
<li key={g}>{g}</li>
))}
</ul>
</div>
);
}
// Typescript처럼 데이터형 확인해줄 수 있게 하는 그런것
Movie.propTypes = {
coverImg: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
}
export default Movie;
기존엔 App.js에서 모든 작업을 했었으나, 코드들을 분할했음.
App.js에서 코드들을 뽑아내고, 여기선 Router로 코드들을 가져오게 함
다음 강의는 동적 url에 대해 설명할거라고 함.
============================================================
아마 Spring, JSP에서 쓰던 링크에 데이터 붙이던것과 비슷할거같음.
url에 있는 변수값을 반환하는 함수
import { useParams } from "react-router-dom";
function Detail(){
const x = useParams();
console.log(x);
return <h1>Detail</h1>
}
export default Detail;
====>>>>
import { useParams } from "react-router-dom";
function Detail(){
const {id} = useParams(); // 이렇게 바꿔준것이 어떻게 동작에 변화를 준지는 모르겠으나,
console.log(id); // 오브젝트 안에 담겨있던 값을 빼내와서 동작함 ({id: '47400'} => 47400)
return <h1>Detail</h1>
}
export default Detail;
await는 async 함수 내부에 있지 않으면 사용할 수 없다.
import { getMouseEventOptions } from "@testing-library/user-event/dist/utils";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
function Detail(){
const {id} = useParams(); // 이렇게 바꿔준것이 어떻게 동작에 변화를 준지는 모르겠으나,
// console.log(id); // 오브젝트 안에 담겨있던 값을 빼내와서 동작함 ({id: '47400'} => 47400)
const getMovie = async() => {
const json = await (
).json();
console.log(json);
}
useEffect(() => {
getMovie();
}, []);
return <h1>Detail</h1>
}
export default Detail;
Movie.js 처럼 로딩?
Api에서 받아온 데이터 state에?
네비게이션바?
홈?
about 페이지?
해보는 코드챌린지 해보면 좋을것 같다~
============================================================
깃허브에 코드 올리고 어쩌고 하는 작은 팁 알려줄거라 하였음.
결과물을 github pages에 deploy 한다.
css는 카메라 없이 한다..?
css는 그냥 css고 알아서 해봐라...
터미널에서 npm i gh-pages 설치
- github pages
- 결과물을 github pages에 업로드 할 수 있게 해주는 패키지
- 깃헙에서 제공하는 무료 서비스
- html, css, js를 올리면 웹사이트로 만들어 세계에 뿌려줌.
package.json에는 build라는 script가 있음.
해당 스크립트를 실행하면 우리 웹사이트의 production ready code를 생성한다
production ready는 코드가 압축되고 모든게 최적화된다는 뜻
터미널에서
npm run build
입력하면 "build"라는 폴더가 생김. 이상한 js들로 가득함
생긴 build를 github 페이지에 push 해야함
3:15
push 전에, package.json 가서 ...
git 이 제대로 안되는듯? 환경설정부터...
https://herojoon-dev.tistory.com/75
React를 Github Page에 Publish(게시)하기
환경 Front : React Source Repository: Github Repository Web Hosting Service: Github Page 목표 Github Repository와 Github Page를 이용하여 React Source를 내 로컬환경이 아닌 웹상에 손쉽게 띄울 수 있습니다. 사전준비 크게
herojoon-dev.tistory.com
- https://herojoon-dev.tistory.com/10
컴퓨터에 깃이 안설치되어있던게 문제가 된듯?
깃 페이지 가서 깔고, vs code에 깃헙 추가하니까 해결됨
하고 나서 강의영상 따라감.
package.json 맨 아래에 스크립트 추가
},
}
scripts에 deploy랑 predeploy 추가
"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"
},
https://newchobo.github.io/react-for-beginners/
React App
newchobo.github.io
============================================================
결론?
뭐 수고했고 자랑스럽고 챌린지 꼭 해봐라
css할줄아니까 ~~~~ 그런 내용.
근데 css는 따로 공부를 하든 부트스트랩을 쓰든 해야될듯
Detail을 만들어보는건 너의 숙제다~
============================================================
<p>{summary.length > 235 ? `${summary.slice(0, 235)}...` : summary}</p>
summary가 너무 긴 애들 처리
============================================================
Breaking Change : 버전 업데이트로 코드 깨져서 코드 수정...
React.js는 breaking change가 없다...
구버전 코드에 대한 이해를 갖는것을 말하는듯..?
React는 하위버전을 호환한다는듯.
useState + useEffect 위주로 배웠으나, 구버전 방식은 기본은 같으나, 방식은 다를 수 있다.
이 아래의 강의들은 구버전 강의들이다... 라고 함.
구버전은 class를 많이 썻나봄.
당장은 안보고, 나중에 필요해지면 봐도 될듯?