영화 정보가 담긴 api를 활용해 간단한 영화 소개 사이트를 만들고 있다.
어제 fetch 후 then을 쓰는 이유에 대해 나름 찾아봤는데 요즘은 then보다는 async-await을 많이 사용한다고 한다. 자바스크립트 튜토리얼에 이와 관련해 자세히 설명돼있다.
내가 바로 이해하기엔 앞서 건너뛴 내용들이 많아서 이 문서의 상위 문서를 먼저 공부하고 뒷 내용을 이해하면 좋을 것 같다.
이게 then을 사용해서 api를 호출하는 코드
useEffect(() => {
fetch(`https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year`)
.then((response) => response.json())
.then((json) => {
setMoives(json.data.movies);
setLoading(false);
});
}, []);
이건 async-await을 사용해서 api를 호출하는 코드
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();
}, [])
아직도 구체적인 동작 원리는 모르겠다. 이 부분은 실습 끝나고 다음주부터 이론 공부하며 채워나가자.
위에서 받아온 api를 출력하기 위해 별도의 Movie
컴포넌트를 만들었다.
<div>
<img src={coverImage} alt={title} />
<h2>{title}</h2>
<p>{summary}</p>
<ul>{genres === null ? "" : genres.map((g) => <li key={g}>{g}</li>)}</ul>
</div>
App컴포넌트에서 movie에 대한 정보를 prop로 받아와야 정상적으로 실행한다. 이 때 오류를 줄이기 위해서 propTypes를 지정했다.
Movie.propTypes = {
coverImage: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string),
};
네트워크 계층에서 사용하는 router가 맞다. velog url에서 url의 규칙을 간단히 생각해보면 메인 url은 https://velog.io/이다. 그런데 지금 글을 쓰고 있는 내 하위 페이지는 https://velog.io/write?id=422f7f1e-0cf5-44c1-816c-50de776b5660 라는 복잡한 주소를 갖고 있다.
기본으로 설정된 https://velog.io을 제외하고 보면 "/", "/~~" 이런 주소로 설정된 것을 알 수 있다.
정리하면 유저의 현재 url에 따라서 보여지는 화면을 달리 하고 싶을 때 라우터를 사용한다.
movie 앱에서 영화제목을 클릭하면 세부 페이지로 이동시키고 싶은데, 그때 라우터를 사용해야 한다고 한다.
$ npm i react-router-dom
작업하는 경로에서 명령어를 입력하면 리액트 라우터가 설치된다. src폴더에 routes
와 components
폴더를 만들면 준비가 대충 끝난다.
여태까지 App.js에 직접 파일을 작성하는 식으로 프로젝트를 진행했는데, 이제 routes폴더 안의 js파일들이 각 화면을 담당하는 일종의 페이지 역할을 하게 된다.
App.js는 routes폴더를 render한다. App.js파일은 return null;
하고 비워두면 됨.
각각의 컴포넌트 조각들은 components폴더에 넣고 그 컴포넌트를 이용해 만들어진 페이지를 routes폴더에 넣으면 된다.
내가 작업한 프로젝트는 이런 경로로 구성함
기본적으로 Home.js에 있는 페이지가 보여지고, 나중에 url이 /~~~ 등으로 추가되면 Detail.js 페이지를 보여줄 거다.
이제 그런식으로 작업해야한다.
import {
BrowserRouter as Router,
Switch,
Route
} from "react-router-dom";
App.js
에 react-router-dom
으로부터 라우터, 스위치, 라우트를 import한다.
function App() {
return (
<Router>
<Switch>
<Route path="/movie">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
Route 안에 내가 보여주고자 하는 페이지 컴포넌트를 넣는다. path는 홈디렉토리는 "/"
, 그 외에는 내가 지정해준다.
단, 내가 사용한 react router는 5.3.0버전인데 현재는 업데이트로 인해 Switch가 사라지고 몇가지 변경 사항이 있다고 한다.
🙄 변경사항
1. Switch컴포넌트가 Routes컴포넌트로 대체되었습니다. Switch -> Routes
2. Route컴포넌트 사이에 자식 컴포넌트를 넣지 않고, element prop에 자식 컴포넌트를 할당하도록 바뀌었습니다.Route path="/" element={< Home / >}
위에서 import하지 않은게 하나 있는데 Link
다. (다른 컴포넌트에 넣어야 함) 이런식으로 각 페이지마다 url을 지정해주고, a태그를 통해 이동하게 되면 기능은 정상적으로 작동하지만 리렌더링이 발생한다.
이 문제를 해결하기 위해 Link태그가 존재한다.
Movie컴포넌트에서
import { Link } from "react-router-dom";
<h2><Link to="/movie">{title}</Link></h2>
타이틀 부분만 다음과 같이 수정했다. 리액트 라우터로 "/movie" url을 Detail 컴포넌트로 설정했기 때문에 h2태그를 클릭하면 Detail 페이지로 넘어간다.
이런식으로 Link로 이동해버리면 prop를 못 받아오는데 다행히 url에도 변수를 넣을 수 있다고 한다. (dynamic url)
리액트 내부에서 깃 페이지에 배포할 수 있다고 한다.
package.json
의 가장 하단에서 (블럭 내부 아님)
"homepage": "https://ppparkta/github.io/cinema"
를 붙여넣고 디버그 블럭 안에
"deploy": "gh-pages -d build",
"predeploy": "npm run build"
를 붙여넣어준다. 이렇게 명령어를 만들면 deploy를 실행할 때 predeploy에서 먼저 파일을 빌드시킨 뒤에 가장 하단에 적어준 homepage주소로 업로드 하겠다는 의미이다.
작업 끝났으면 터미널에
$ npm run deploy
입력해주면 내가 입력한 주소로 업로드된다. 참고로 주소는 https://깃허브아이디.github.io/저장소명
으로 설정해야 한다.
오늘 리액트 기초 강의도 마무리됐지만 저장소 관리를 잘못해서 배포를 하진 못했다. 새로운 저장소를 만들어야 pages에 업로드할 수 있을 것 같다.
내일 할 것