티스토리 뷰
이 포스팅은 https://nomadcoders.co/react-for-beginners 강의를 보며 작성하였습니다.(무료!)
이번 section에서는 지금까지 배웠던 State, Effect, props를 연습하면서 지금 까지 배운 것들을 연습하고 적용해 볼 것이다.
먼저 To Do List를 만들어보자!
git : https://github.com/leesulgi66/React_for_Beginners/commit/f7ce28c1e54a926bb370047a601c5a8e15144137
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;
}
setToDo("");
setToDos((currentArray=>[toDo, ...currentArray]));
};
console.log(toDos);
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, index)=><li key={index}>{item}</li>)}
</ul>
</div>)
}
export default App;
state값은 toDo와 toDos 두 가지이며 toDo의 값이 작성되면 기존의 toDos에 toDo값이 더해진 새로운 배열을 얻는 형태로 완성되었다. 이러한 배열을. map() 함수를 이용하여 분리해 주고 리스트를 만들었다. map함수가 조금 낯설었다. java에서의 map은 key-value값의 조합인데 JS에서는 오히려 for each구문과 비슷한 것 같았다. 또한 map함수는 인자를 세 가지 가질 수 있고 순서대로 value, index, array의 값을 받을 수 있었다.
React에서 {} 중괄호 안에는 JS코드를 작성할 수 있는데, 그렇다면 방금 위의 문장들을 for문을 이용해서도 작성할 수 있을까?라는 생각을 가지게 되었다. 왜냐하면 .map()이라는 함수가 나에게는 낯설었기 때문에 쉽고 친숙한 for문을 이용하는 게 낫지 않을까?라는 생각을 하게 되었다.
결론 적으로 말하면 할 수 없다. 그 이유는 return문 안에서는 JSX를 반환해 주어야 한다. 단순히 배열을 순회하는 것으로는 작성할 수가 없다. 이 것은 React의 Virtual Dom의 작동박식과도 연관이 있는데, Virtual DOM은 이전 상태와 새로운 상태의 차이를 최소화하여 변경 사항만을 업데이트하는 방식으로 동작한다. 이를 위해 React는 각 요소에 고유한 key 값을 할당하여 변경되지 않은 요소는 이전의 상태를 그대로 유지하면서, 변경된 요소만 한다. 하지만 for문에서 반복되는 생성 요소들은 key값을 가지지 않게 되고 for문을 사용하여 배열을 처리할 경우, Virtual DOM에서 요소를 업데이트할 때 올바르게 처리되지 않을 수 있다. 따라서 각 요소의 key값과 새로운 배열을 반환하는 map() 함수가 React의 동작과 더 잘 맞고 for문을 사용하고 싶다면 return문 밖에서 따로 함수화 시켜 작동하는 함수를 불러오는 것으로 가능하다.(여기서도 key값을 부여하는 것을 권장하고 있다.)
이번에는 Coin Tracker를 만들어 보자.
import { useState, useEffect } 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 ? <string>Loading...</string> :
<select>
{coins.map((coin) => <option key={coin.id}>{coin.name} ({coin.symbol}): ${coin.quotes.USD.price} USD</option>)}
</select>}
</div>);
}
export default App;
git : https://github.com/leesulgi66/React_for_Beginners/commit/14c09598cc1b0d97ed1a03c4032a55c39843bddf
똑같이 여기서도 state와 effect를 사용하여 복습해 보았다. 여기선 처음으로 api를 요청하는 방법이 나왔는데 어떠한 형식으로 불러내는지 잘 몰라서 추가로 검색을 해보았다.
fetch()는 JavaScript내장 객체로 API를 호출하는 역할을 한다.
fetch(url, options)
.then((response) => console.log("response:", response))
.catch((error) => console.log("error:", error));
첫 번째 인자에는 URl, 두 번째 인자에는 option을 받고 옵션에는 method, headers, body등을 설정해 줄 수 있다. API요청이 성공했을 때에는 response를 받고, 실패했을 경우 error로 예외처리가 가능하다. default 값이 GET요청이기 때문에 GET요청을 할 때는 option인자 값이 필요 없다. POST, PUT, DELETE 요청 시에는 option의 값을 넣어주어야 한다.
이러한 fetch()의 데이터는 바로 사용할 수 없는데, fetch를 사용할 땐 두 단계를 거쳐야 한다. 먼저 올바른 url로 요청을 보내야 하고, 바로 뒤에 오는 응답에 대해 json()을 해줘야 하는 것이다. json()는 response 스트림을 가져와 스트림이 완료될 때까지 읽는다. 그리고 다 읽은 body의 텍스트를 Promise 형태로 반환한다.
fetch("https://api.coinpaprika.com/v1/tickers") // uri의 요청
.then(response => response.json()) // reponse를 json형식으로 파싱
.then((json) => {
setCoins(json); // json 데이터를 Coins에 저장(배열형태)
});
마지막으로 영화를 소개해 주는 Movie App을 만들어 보자.
import { useState, useEffect } from "react";
function App() {
const [loading, setLoading] = useState(true);
const [movies, setMovies] = useState([]);
const getMovies = async() => {
const response = await fetch(`https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year`);
const json = await response.json();
setMovies(json.data.movies);
setLoading(false);
}
useEffect(()=>{
getMovies();
}, []);
return (
<div>
{loading ? <h1>Loading...</h1> :
<div>{movies.map(movie =>
<div kye={movie.id}>
<img src={movie.medium_cover_image} />
<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;
영화 API를 이용해 간단히 영화의 이미자와 설명을 보여주는 app을 만들었다. 이 프로젝트도 똑같이 fetch()를 사용했지만, .then구문을 사용하지 않고 따로 함수로 만들어서 async-await 구문을 이용하여 만들었다. 이렇게 보는 것이 코드가 더 깔끔해 보이긴 했다. 그리고 기존에는 App.js에 모든 코드를 썼지만, 새로운 폴더를 만들고 기존에 사용한 뷰를 옮겨 담아 각각의 컴포넌트화 시켰다. 캡슐화를 하는 과정과 약간 비슷한 느낌이었다.
이제 Router라는 것을 사용해 볼까 한다. 터미널에 라우터 설치를 하고
npm i react-router-dom@5.3.0
App.js에 라우터를 임폴트 해주었다.
import {
BrowserRouter as Router,
Switch,
Route,
}from "react-router-dom";
function App() {
return
}
export default App;
Router와 Switch를 사용했는데 Switch는 Route를 찾아주는 것으로 Route는 URL을 의미한다. 그리고 Route를 찾으면 컴포넌트를 랜더링 한다.
※ 현재 영상은 라우터 5버전으로 사용하고 있으며 6버전에는 변경점이 있다!!
react-router-dom 5버전 -> 버전6 바뀐 부분
1. Switch컴포넌트가 Routes컴포넌트로 대체되었습니다.
Switch -> Routes
2. Route컴포넌트 사이에 자식 컴포넌트를 넣지 않고, element prop에 자식 컴포넌트를 할당하도록 바뀌었습니다.
Route path="/" element={< Home / >}
react-router-dom 6버전 문서
https://reactrouter.com/docs/en/v6/getting-started/overview
기본 적인 형태는(5.3ver)이렇게 될 것이다.
import {
BrowserRouter as Router,
Switch,
Route,
}from "react-router-dom";
function App() {
return <Router>
<Switch>
<Route>
</Route>
</Switch>
</Router>;
}
export default App;
<Route> 안에 Componet를 써줄 것이다.
function App() {
return <Router>
<Switch>
<Route path="/movie">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>;
}
위와 같은 방식으로 Route 안에 uri를 입력해 주고 거기에 맞는 component를 보여주는 것이 가능하다. 이제 주소값을 이용해 각기 다른 컴포넌트를 보여주는 것이 가능해졌다. 추가로 동적인 uri를 만들어 이제 원하는 각각의 페이지의 detail을 보여줄 수 있도록 설정을 해보자.
- /movie뒤에 :id 값을 입력해 준다. /movie/id가 되지 않도록 주의하자. (고정 주소값으로 입력됨)
- 변경된 주소를 사용 할 Movie.js에서 Link를 import한 후 Link to={}로 선언
- Movie.js는 Home.js에서 props를 전달 받고 있기에 Home.js에서 id값을 넘겨준다.
- Detail.js 에서는 useParams()를 사용해 유동적인 id값을 받아올 수 있다.
이제 title을 클릭하면 우리가 원하는 해당주소(movie/id)로 이동할 수 있다.
uri에 유동적인 id를 넣을 수 있게 됨으로써 동적인 주소를 할당할 수 있다. api의 detail 페이지도 요청이 가능하다.
api : https://yts.mx/api/v2/movie_details.json?movie_id=63306
id= 다음으로 오는 숫자에 id값을 넣게 되면 서로 다른 detail 페이지에 접근이 가능하다. 이제 detail 페이지도 입맛에 맞게 원하는 대로 만들어 보자.
npm i gh-pages
터미널에 npm i gh-pages를 입력해 gh-pages툴을 설치한다. 이는 결과물을 github pages에 업로드할 수 있게 해주는 패키지이다. github pages는 무료 서비스이고 html, css, jabascript를 업로드하면 웹사이트로 만들어서 전 세계에 무료로 배포해 준다.
가장 먼저 해야할 것은 package.json에 있는 script를 확인하는 것이다. 여기에는 build라는 script가 있다. 이 script를 실행하면 웹사이트의 production ready code(코드 압축 및 최적화)를 생성한다. 터미널에 다음을 입력해 준다.
npm run build
build가 끝나면 build폴더가 생성된다. 이제 github pages에 push해 주어야 한다. 그전에 package.json으로 가서 마지막줄에 다음 사항을 입력해 준다.
"homepage" : "https://leesulgi66.github.io/React_for_Beginners"
입력 전에 항상 앞의 문단 뒤에 ,를 입력해서 연결해 주는 것도 잊지 말자.
주소의 구성은 "homepage" : "https://{깃허브아이디}.github.io/{리포지토리 주소} 형식이다.
다음은 scripts에 script를 추가해 줄것이다.
"deploy": "gh-pages -d build",
"predeploy": "npm run build"
predeploy는 deploy가 실행되기 전에 실행되는 script이다. predeploy가 먼저 실행이 돼서 빌드를 시켜주고 빌드가 끝나면 gh-pages가 build폴더를 위에 적은 주소로 업로드해 줄 것이다. 이제 터미널에 deploy를 실행시켜 보자.
npm run deploy
이제 package.json에서 homepage에 적었던 url로 가보면 내가 만든 react앱으로 접속할 수 있다.
하지만 detail page로 이동하는 주소의 연결이 잘 되지 않았다. 클릭을 하면 페이지 전환이 아니라 /move/{number}의 주소값만 추가되고 이동이 되지 않았다. 검색을 해보다 결국 react-router-dom의 버전을 5에서 6으로 올리고 추가 오류수정을 위해 react-scripts 삭제 후 재 설치를 하였다. 이 과정에서 시간을 좀 많이 잡아먹은 것 같다. 또한 switch의 코드를 수정하고 App.js의 코드를 6버전에 맞게 수정했다.
//react-router-dom 재설치
npm install react-router-dom
//react-scripts 삭제
npm uninstall react-scripts
//react-scripts 재설치
npm install react-scripts
App.js코드 수정
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./routes/Home";
import Detail from "./routes/Detail";
function App() {
return <Router>
<Routes>
<Route path={`${process.env.PUBLIC_URL}/movie/:id`} element={<Detail />} />
<Route path={`${process.env.PUBLIC_URL}/`} element={<Home />} />
</Routes>
</Router>;
}
export default App;
주소로 이동하면 잘 동작하는걸 볼 수 있다.
https://leesulgi66.github.io/React_for_Beginners/https://leesulgi66.github.io/React_for_Beginners/
드디어 내가 만든 앱이 첫 생명을 얻었다.
'개발일지 > React' 카테고리의 다른 글
ReactJS 로 영화 웹 서비스 만들기 #8 Old Codes (3) | 2024.08.28 |
---|---|
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #7 Practice Movie App Styles (0) | 2024.08.21 |
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #6 Effects (0) | 2024.08.15 |
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #5 Create React App (0) | 2024.08.13 |
노마드 코더 - ReactJS 로 영화 웹 서비스 만들기 #4 Props (0) | 2024.08.09 |