티스토리 뷰
이번에는 기존에 Movie.js로 뭉쳐놓았던 것을 App.js과 Movie.js로 컴포넌트를 분할해서 정복해봅시다.
App.js
import { useEffect, useState } from 'react';
import Movie from './Movie';
function App() {
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.5&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;
- 중요한 것은 인자로 보내줄 속성값은 정확히 기입해야 하고 속성명의 경우는 내가 원하는 대로 수정해서 Movie컴포넌트의 매개변수로 보내줄 수 있다는 점이다.
- 보면 App이라는 상위 컴포에서는 인자로 넘겨줄 값은 정확하게 써줬고 그 속성에 해당하는 속성명은 coverImg 등으로 내가 원하는 식으로 Movie 컴포넌트(하위 컴포)에 넘겨주고 있다.
- map을 썼으니 당연히 key값을 줘야함
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>
);
}
Movie.propType = {
coverImg: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
genres: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default Movie;
- 전달받은 prop의 타입을 확인하기 위해서 propType을 임포트하고 검사하는 코드를 작성했다. 여기서 새롭게 등장한 것이 arrayOf(PropTypes.string)인데 array의 경우 여러가지 타입을 포함하는 경우와 한가지 특정 타입만 포함하는 경우에 나눠서 propType을 작성하는 법이 다르다. 해당 코드의 경우 string 이라는 데이터타입으로만 이루어져 있기 때문에 저렇게 작성한 것
- 매개변수로 전달해 줄 때에는 속성값을 쓰는 것이 아니라 속성명을 쓰는 것을 유의합니다.
- 또한, genres의 경우 객체를 매개변수로 전달받은 것이므로 Movie 내부에서 한번 더 map()함수를 통해 li를 구성하는 작업을 해야합니다. 여기에 물론 key 값을 또 작성해줘야겠죠. g라고 되어있는 변수는 각각의 장르를 나타내는 변수로 genres를 구성하는 하나의 데이터를 칭합니다. 이 값은 자체로 고유한 값(unique)을 나타내기 때문에 key의 값으로 변수 g를 줘도 괜찮습니다.
▼prop type 별 작성법▼
1) 여러 타입을 포함하는 객체 2) 특정 타입만 담고있는 배열
// An object that could be one of many types
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// An array of a certain type
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
😎React Router를 통해서 페이지 전환하기
파일구조▼
-> 여기서 Coin.js/ TodoList.js/Button.js관련 애들은 안 씁니다.
▶실행방법( 실행툴: VSCode)
terminal에 명령어: npm install react-router-dom 로 리액트 라우터 설치하기
- 주의) 최근에 router가 버전6으로 업그레이드 됐습니다. ->문제는 버전6가 버전5의 기존 방식과 호환이 되지 않게 만들어졌습니다. 제가 듣고 있는 강의에서는 버전5의 문법을 따르고 있기 때문에 npm install react-router-dom으로 설치된 최신버전(버전6)를 언인스톨하고 다시 버전5으로 재설치했습니다.
- package.json(파일)을 확인하면 내 react-router-dom의 버전을 알 수 있습니다.
npm uninstall react-router-dom // 내 리액트돔(최신ver) 삭제
npm i react-router-dom@5.3.0 // 5버전 설치 , 여기서 i 는 위의 install과 같습니다.
routes 라는 폴더를 따로 만들어서 그 안에 Home.js , Detail.js (메인 페이지, 상세 페이지 의미 내포) 파일을 만든다.
*routes(폴더명)를 screen으로 하든 pages 로 하든 상관없습니다.
Home.js에 기존에 App.js에 담긴 코드를 옮겨서 담아주고 대신에 App.js는 router를 render하는 것으로 코드를 바꾼다.
App.js에 기본적인 router 요소들을 복사해서 붙여줍니다. 아래 링크를 참고하면 오른쪽 페이지에 어떻게 해야하는 지 quick-start에 대한 설정법이 나와있습니다.
https://v5.reactrouter.com/web/guides/quick-start
react-router-dom에서 App.js에 import해야할 것들(초기세팅)▼
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
->여기서 Link는 삭제하도록 합니다. ( 나중에 배운다함 )
->리액트 라우터 돔에서 임포트를 함으로써 우리는 이미 만들어져있는 component(아래 primary compo 에 상세설명)를 사용할 수 있게 됩니다.
▶리액트 라우터의 Primary Components
-크게 3카테고리로 분류
- 1.라우터들(Routers) - Router의 종류는 Hash Router과 Browser Router 두가지가 있다.
- <BrowserRouter> and <HashRouter>
- 라우터 컴포넌트 형식이며 웹 프로젝트의 경우 위의 두가지 라우터를 제공, 가장 큰 차이점은 URL을 저장하는 방법과 웹서버와 통신하는 방법에서 나타난다.
- HashRouter는 주소에 해쉬(#)가 붙고 검색 엔진이 읽지 못한다. URL 해시를 서버에서 읽을 수 없기 때문에 Server Side Rendering으로 설정을 백업할 수 없다. 또한 해시 히스토리를 지원하지 않는다. 브라우저 또는 웹 서버에 제한이 없다. 서버 측 라우팅은 클라이언트 측 라우팅과 독립적이다. // 백엔드가 필요없는 작은 프로젝트에 용이, 배포 쉬움
- BrowserRouter, HTML5 히스토링 API를 사용하여 구성 요소를 렌더링한다. 즉, IE9이하 및 동시대 브라우저에서는 사용할 수 없다. 클라이언트 측 React 어플리케이션은 깨끗한 경로를 유지할 수 있지만 웹서버의 지원이 필요하다. //백엔드를 제공하는 대규모 프로젝트에 용이, 배포 어려움
- + 브라우저 라우터 부가설명: BrowserRouterURL를 브라우저의 현재 URL과 동기화한다. 이는 HTML-5 History API를 통해 수행되며 반면 HashRouter에는 URL의 해시 부분을 사용하여 동기화한다.
- 2. 라우트 매처스(route matchers) - Switch는 Route(URL을 의미함, 도메인 뒤에 달린 상세 웹페이지주소)를 찾는다 -> Route를 찾으면 컴포넌트를 렌더링한다.
- <Route> and <Switch>
- 3. 네비게이션(navigation) - navigation compo의 경우, "route changers(라우트 변경자)"라고 생각하기 쉬움
- <Link>, <NavLink>, and <Redirect>
- <Link> 컴포넌트는 브라우저의 새로고침 없이도 유저를 다른 페이지로 이동시켜주는 컴포넌트이다. (기존의 a href와 큰 차이점은 리로드가 필요없다는 점!), 어디서든 사용가능하다.
- 예시로 Movie컴포넌트 안에서 사용할 때는,
위와 같은 형식으로 임포트해주고 a href="" 대신 Link to="/movie" 를 사용하면 된다.import {Link} from 'react-router-dom'; function Movie({props}){ return //중략 <h2> <Link to="/movie">{title}<Link> <h2> //중략 }
App.js
-App.js에서 라우터를 렌더링하는 코드 작성
import { BrowserRouter as Router, Switch, Route } 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;
- 위에 설명한 것처럼 각각의 임포트한 요소를 컴포넌트처럼 사용하고 있다. Switch컴포넌트가 Route를 찾는다고 했으니 그 내부에 Route를 넣고 그리고 Route에는 path 속성의 값으로 "/"을 준다. ( /의 뜻이 home화면을 가리키기 때문, 유저가 /의 경로에 있으면 Home Route를 렌더링해줄 것) 이미 우리는 routes 폴더 내부에 Home.js와 Detail.js 컴포넌트를 만들어뒀기 때문에 Home을 import해주고 Route 태그 내부에 <Home /> 을 써준다. 마찬가지로 Detail.js을 import해주고 Route태그 내부에 <Detail/> 를 넣어준다.
react router란?
-라우터는 url을 보고있는 component이다. 사용자가 보고있는 URL에 따라서 Home을 보여주거나 Detail을 보여준다.
- Route를 만들면 path값에 해당하는 경로에 유저가 들어올 때에 내부 컴포넌트가 렌더링 된다.
e.g.) 아래는 라우트 안에 Detail 이라는 컴포넌트가 들어가 있고 Detail이 렌더링된다.
<Route path="/movie">
<Detail />
</Route>
*react-router-dom: web에서 사용
-또한, 리액트 라우터는 다이나믹(동적) URL을 지원해주기도 한다.
▶동적 URL 지원
※아래의 예시 코드들은 전체 코드를 포함하고 있지 않고 연관이 있는 부분만 기재했습니다.
App.js
<Route path="/movie/:id">
<Detail />
</Route>
기존 라우터 경로 뒤에 /:id 를 붙여주면 유저를 "/Movie/변수" 의 경로로 보낼 수 있다. 중요한건 그냥 id 가 아니라 :id이다! 변수에는 어떤 값이든 들어올 수 있다. -> 그렇다면 Movie 컴포넌트 측에서 id라는 prop을 받아야하는데
Movie.js
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>
);
}
보다시피 Movie의 props에는 id가 없다. -> 그 말은 Movie컴포넌트의 부모 요소를 살펴봐야 한다는 의미 -> Home 컴포넌트를 살펴보자 (Home컴포넌트가 Movie컴포넌트를 렌더링하고 있음)
- <img src={} alt={}/> // HTML <img>는 이미지 삽입 요소입니다. alt의 경우, 이미지의 "텍스트 설명"을 속성값으로 받는데 필수요소는 아니지만 스크린 리더가 alt의 값을 읽어 사용자에게 이미지를 설명하므로 접근성 차원에서 매우 유용하며 네이트워크 오류, 콘텐츠 차단, 죽은 링크 등 이미지를 표시할 수 없는 경우에도 이 속성의 값을 대신 보여줍니다. -> create-react-app에서는 시각장애인들을 위한 정보로 필수로 요구하고 있기 때문에 추가해주시는 것이 좋습니다.
Home.js
{movies.map((movie) => (
<Movie
key={movie.id}
id={movie.id} //추가해줌
coverImg={movie.medium_cover_image}
title={movie.title}
summary={movie.summary}
genres={movie.genres}
/>
))}
홈에서 props로 id를 넘겨주고 있지 않으니 Home.js에서 추가로 id를 속성으로 Movie.js에 전달해주고
Movie.js에서 받은 id를 title의 Link to 속성값으로 준다. (리로드 방지를 위해 react-router-dom에서 Link컴포넌트를 임포트해서 a href=""대신에 사용한 것)
Movie.js
import {Link} from 'react-router-dom';
function Movie({ id,coverImg, title, summary, genres }) {
return (
//중략
<h2>
<Link to=`movie/${id}`>{title}</Link>
</h2>
//중략
);
}
Movie.propType = {
id: PropTypes.number.isRequired,
//중략
}
받은 변수 id를 경로로 줘야하기 때문에 백틱을 사용해서 기존에 있던 코드와 합친다.
useParams()를 통해 변수값 알아내기
- useParamas 는 React Routor에서 제공하는 함수이다. url에 있는 값을 반환해준다. (특히 변경되는 값, url에 있는 변수값을 말한다. )
- useParams는 URL 파라미터의 키/값 쌍의 객체를 반환합니다. 이를 사용하여 현재 <Route>의 match.params에 액세스합니다.
▼사용방법
- React Router에 해당 url이 어떤 변수(:변수명)를 받을거라고 말해주기
- 해당 경로에 갔을 때 화면(컴포넌트)에 useParams() 사용하기
위의 코드를 실행하면 URL의 상세주소가 movie/40297 등으로 할당된 id값이 영화 링크마다 다른 것을 파악할 수 있다.
여기서 뒤에오는 id값이 무엇인지 알 수 있다면 매우 유용합니다. 해당 url에 요청을 보낼 수 있기 때문인데요(url은 id값을 요구합니다.)
맨 처음 App.js에 <Route path="movie/:id"> 를 써줬던 것을 기억해봅시다. 해당 코드는 React Router한테 여기 오는 id값이 뭔지 알고 싶다고 말하는 것입니다.
App.js
function App() {
return (
<Router>
<Switch>
<Route path="/movie/:id">
<Detail />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Router>
);
}
Detail.js
import { useParams } from 'react-router-dom';
function Detail() {
const x = useParams();
console.log(x);
return <h1>Detail</h1>;
}
export default Detail;
-> 해당 경로로 진입했을 때 보여지는 component인 Detail의 내부에 uesParams 임포트하고 x라는 변수에 값을 담고 그것을 콘솔에 표시하도록 했습니다.
->useParams() 함수를 사용하면 React Router은 :id에 해당하는 변수의 값을 넘겨줍니다.
function Detail() {
const { id } = useParams();
console.log(id);
return <h1>Detail</h1>;
}
반환값이 객체(key : value 한 쌍)이기 때문에 위와 같이 수정할 수 있겠죠. 이렇게되면 순전히 값에 대해서만 출력해서 볼 수 있습니다.
이제 url의 값(정확히는 id값)을 알아냈으니 API에 요청을 보내는 것을 할 수 있습니다.
useEffect(() => {
const json = (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
}, []);
- await은 async함수 내부에 있지 않으면 사용할 수 없다! async 함수에 담을 수 있게 따로 빼주자
import { useEffect } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { useParams } from 'react-router-dom';
function Detail() {
const { id } = useParams();
const getMovie = async () => {
const json = await (
await fetch(`https://yts.mx/api/v2/movie_details.json?movie_id=${id}`)
).json();
};
useEffect(() => {
getMovie();
}, []);
return <h1>Detail</h1>;
}
export default Detail;
주의▼
- 여기서 반환되는 객체는 "내가 App.js에서 Route에 써둔 변수명(:id)을 그대로 반영하고 있기 때문에" Detail 컴포넌트에 정의한 변수명과 헷갈리지 않도록 주의합니다. Route 에 써둔 변수명을 :gamza 라고 변경한다면 { gamza: '23413' } 이런식의 결과를 출력할 것입니다. -> 내부 컴포넌트의 id값을 받는 변수와 라우트에 명시한 변수명을 통일시켜주면 헷갈리지 않겠죠!
'Frontend > react.js' 카테고리의 다른 글
[React | node.js] Node.js프로젝트 세팅 (0) | 2022.05.18 |
---|---|
[React JS] 리액트 어플리케이션 배포하기 (with gh-pages) (0) | 2022.03.24 |
[React JS] 영화 어플리케이션 (0) | 2022.03.21 |
[React JS] Coin Tracker만들기 (0) | 2022.03.21 |
[React JS] To Do List 만들기 (0) | 2022.03.18 |
- Total
- Today
- Yesterday
- 타입스크립트 장점
- 원티드 FE 프리온보딩 챌린지
- 프리온보딩 프론트엔드 챌린지 3월
- 부트캠프항해
- 원티드 프리온보딩 FE 챌린지
- && 셸 명령어
- fs모듈 넥스트
- 형제 요소 선택자
- Prittier
- aspect-ratio
- grid flex
- 틸드와 캐럿
- getServerSideProps
- 원티드 3월 프론트엔드 챌린지
- nvm 설치순서
- tilde caret
- getStaticPaths
- text input pattern
- 항해99프론트
- ~ ^
- 타입스크립트 DT
- 항해99프론트후기
- 항해99추천비추천
- nvm경로 오류
- D 플래그
- 원티드 프리온보딩 프론트엔드 챌린지 3일차
- reactAPI
- is()
- float 레이아웃
- 프리렌더링확인법
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |