일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- web
- AWS
- 알고리즘
- django widget
- Algorithm
- 파이썬 알고리즘
- DRF
- PYTHON
- js
- API
- 장고
- 백준
- Baekjoon
- 알고리즘 연습
- CSS
- react
- es6
- form
- java
- javascript
- 알고리즘 문제
- c++
- 알고리즘 풀이
- Git
- HTML
- MAC
- django rest framework
- Django
- 파이썬
- django ORM
- Today
- Total
수학과의 좌충우돌 프로그래밍
[React] react의 네비게이션, react-router-dom 본문
react-router-dom 설치
react 의react-router-dom
에 대해서 알아보도록 하겠습니다.
react-router-dom
은 네이게이션을 만들어주는 패키지로서 별도의 설치가 필요합니다.
다음과 같이 npm을 설치를 진행합시다.
npm install react-router-dom
기본적인 설명은 저번 시간까지 진행하였던 영화목록 프로젝트에 이어서 진행을 하도록 하겠습니다.
https://ssungkang.tistory.com/entry/React-Axios-%EC%99%80-CSS
react-router-dom
을 적용하기 위해 기존의 코드를 약간 수정하고 넘어가겠습니다.
우선 디렉토리 구조를 변경해주었습니다.
src 안에 components 와 routes 를 만들고 routes 안에는 두 개의 route를 만들어주었습니다.
routes는 url마다 대응되는 페이지 개념이고 components 는 route 각 페이지에서 사용할 components 를 넣도록 하겠습니다.
src
├── App.js
├── Fruit.js
├── components
│ └── Movie.js
├── index.js
└── routes
├── About.js
└── Home.js
현재 App.js
는 영화 리스트 api와 통신하여 해당 데이터를 movie component로 만들어주고 있습니다.
import React from 'react';
import axios from 'axios';
import Movie from './components/Movie';
class App extends React.Component {
state = {
movies: []
};
getMovies = async () => {
const {
data: {
data: { movies }
}
} = await axios.get("https://yts.mx/api/v2/list_movies.json?sort_by=rating");
this.setState({ movies });
}
componentDidMount() {
this.getMovies();
}
render() {
const { movies } = this.state;
return (
<section className="container">
<div className="movies">
{
movies.map(movie => {
return <Movie
key={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
/>
})
}
</div>
</section>
)
}
}
export default App;
이를 복사해서 movies.js
로 옮기고 내용을 약간 수정합시다.
디렉토리 경로가 바뀌었으므로 import 경로를 바꾸고 class name 과 export name을 바꿔주어야 합니다.
import React from 'react';
import axios from 'axios';
import Movie from '../components/Movie';
class Home extends React.Component {
state = {
movies: []
};
getMovies = async () => {
const {
data: {
data: { movies }
}
} = await axios.get("https://yts.mx/api/v2/list_movies.json?sort_by=rating");
this.setState({ movies });
}
componentDidMount() {
this.getMovies();
}
render() {
const { movies } = this.state;
return (
<section className="container">
<div className="movies">
{
movies.map(movie => {
return <Movie
id={movie.id}
year={movie.year}
title={movie.title}
summary={movie.summary}
poster={movie.medium_cover_image}
/>
})
}
</div>
</section>
)
}
}
export default Home;
Router와 Route
이제 App.js
에 router를 추가해보도록 하겠습니다.
Route
는 url을 기준으로 어떤 component를 불러올지 결정하게 됩니다.
또한 Route
들은 Router
로 항상 묶여있어야 하는데 여기서는 HashRouter
를 사용하였지만 다른 Router
를 사용해도 무방합니다.
// App.js
import React from "react";
import { HashRouter, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
const App = () => {
return (
<HashRouter>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</HashRouter>
);
}
export default App;
about 페이지, /about
으로 가게 되면 Home
, About
두 개의 component가 둘 다 render 된 것을 확인할 수 있습니다.
왜냐하면 Route
는 해당 경로에 해당하는 모든 component를 다 불러오게 됩니다.
이를 해결하기 위해서는 다음과 같이 exact 를 설정해줍니다.
이럴 경우 해당 path로 접근했을 때만 해당 component를 불러오고 뒤에 추가적인 url에 대해서는 불러오지않습니다.
import React from "react";
import { HashRouter, BrowserRouter, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
const App = () => {
return (
<HashRouter>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</HashRouter>
);
}
export default App;
네비게이션, Link
이제 각 페이지로 이동할 수 있는 네비게이션을 만들어줍시다.
네비게이션은 어느 페이지에서나 보여야 하므로 다음과 같이 component로 추가해주었습니다.
// App.js
import React from "react";
import { HashRouter, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Navigation from ".components/Navigation";
const App = () => {
return (
<HashRouter>
<Navigation />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</HashRouter>
);
}
export default App;
아직 Navigation
component는 만들지 않았으니 만들어주겠습니다.
react가 아닌 그냥 html a 태그를 사용해도 페이지를 바꿀 수 있습니다.
// components/Navigation.js
import React from "react";
const Navigation = () => {
return <div>
<a href="/">Home</a>
<a href="/about">About</a>
</div>
}
export default Navigation;
하지만 이 경우 네비게이션의 해당 링크를 클릭하면 해당 링크로 이동하면서 새로고침이 되고 맙니다.
이는 react와 같은 SPA에서는 원하는 방법이 아닙니다.
따라서 react-router-dom
의 Link
를 사용해보도록 하죠.
이 경우 새로고침, redirect 없이 바로 페이지를 띄우게 됩니다.
// components/Navigation.js
import React from "react";
import { Link } from "react-router-dom";
const Navigation = () => {
return <div>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</div>
}
export default Navigation;
그리고 Link
는 항상 Router
안에서 존재해야 합니다.
현재는 Navigation.js
가 HashRouter
안에 있으므로 문제 없이 동작하는 걸 확인할 수 있습니다.
Route Props
이번에는 각 영화 데이터를 클릭할 시 상세 페이지로 이동하도록 하겠습니다.
Movie.js
에 Link
를 사용하여 값을 넘겨줍니다.
to
인자에 object로서 경로는 pathname을 통해서 값은 state를 통해서 넘겨줄 수 있습니다.
// components/Movie.js
import React from "react";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
const Movie = ({ id, year, title, summary, poster }) => {
return (
<Link to={{
pathname: `/movie/${id}`,
state: {
year:year,
title:title,
summary:summary,
poster:poster
}
}}>
<div className="movie">
<img src={poster} alt={title} title={title} />
<div className="movie__data">
<h3 className="moivie__title">{title}</h3>
<h5 className="moivie__year">{year}</h5>
<p className="moivie__summary">{summary}</p>
</div>
</div >
</Link>
);
}
Movie.propTypes = {
year: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
summary: PropTypes.string.isRequired,
poster: PropTypes.string.isRequired
};
export default Movie;
다음으로는 App.js
에 movie/:id
로 가는 route를 추가해줍시다.
// App.js
import React from "react";
import { HashRouter, Route } from "react-router-dom";
import Home from "./routes/Home";
import About from "./routes/About";
import Detail from "./routes/Detail";
import Navigation from "./components/Navigation";
const App = () => {
return (
<HashRouter>
<Navigation />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/movie/:id" component={Detail} />
</HashRouter>
);
}
export default App;
이제 Detail.js
를 다음과 같이 만들어줍시다.
Router
에서 각 Route
에 기본적인 props를 넘겨줍니다.
console.log 를 통해서 어떤 props들이 넘어가는지 확인해봅시다.
import React from "react";
const Detail = (props) => {
console.log(props);
return <span>hello</span>;
}
export default Detail;
console에 찍힌 결과입니다.
우선 props.location.state
에 to
인자를 통해서 넘긴 값이 담긴 것을 확인할 수 있습니다.
그 외에도 history, location, match, staticContext 등 다양한 값들이 보입니다.
이들에 대해서는 별도의 포스팅에서 좀 더 자세히 다뤄보도록 하겠습니다.
{history: {…}, location: {…}, match: {…}, staticContext: undefined}
history: {length: 20, action: "PUSH", location: {…}, createHref: ƒ, push: ƒ, …}
location:
pathname: "/movie-detail"
state:
year: 2013
title: "Doctor Who The Day of the Doctor"
summary: "In 2013, something terrible is awakening in London's National Gallery; in 1562, a murderous plot is afoot in Elizabethan England; and somewhere in space an ancient battle reaches its devastating conclusion."
poster: "https://yts.mx/assets/images/movies/doctor_who_the_day_of_the_doctor_2013/medium-cover.jpg"
__proto__: Object
search: ""
hash: ""
__proto__: Object
match: {path: "/movie-detail", url: "/movie-detail", isExact: true, params: {…}}
staticContext: undefined
__proto__: Object
Route Props 문제점
그런데 문제점이 생깁니다.
Home
에서 movie를 클릭해서 detail 페이지로 이동할 시에는 데이터를 담아서 가지만 해당 url로 직접 접근할 경우에는 props.location.state
의 값이 undefined
로 표시됩니다.
따라서 이럴 경우에는 따로 처리를 해줘야하는데 Home
으로 이동시켜보도록 하겠습니다.
react-route-dom
의 history
를 이용하면 원하는 url로 이동할 수 있습니다.
// routes/Detail.js
import React, { useEffect } from "react";
const Detail = (props) => {
useEffect(()=>{
if (props.location.state === undefined){
props.history.push("/");
}
})
return <span>{props.location.state.title}</span>;
}
export default Detail;
다음 문제점은 새로고침 할 때 일어납니다.
새로고침할 시에 state들이 유지가 안되기 때문에 props.location.state
값이 없어 에러가 발생합니다.
useEffect
에서 home으로 redirect 시키기 전에 먼저 render
가 일어나 화면에 값을 뿌려주기 때문이죠.
다음과 같이 분기처리함으로서 해결할 수 있습니다.
// routes/Detail.js
import React, { useEffect } from "react";
const Detail = (props) => {
useEffect(()=>{
if (props.location.state === undefined){
props.history.push("/");
}
})
if (props.location.state){
return <span>{props.location.state.title}</span>;
} else {
return null;
}
}
export default Detail;
'웹프로그래밍 > React' 카테고리의 다른 글
[react] react-create-app의 Proxy (0) | 2020.02.27 |
---|---|
[React] axios 의 withCredentials (0) | 2020.02.17 |
[React] Axios 와 CSS (0) | 2020.01.27 |
[React] Component Life Cycle method (0) | 2020.01.26 |
[React] Class Component 와 State (1) | 2020.01.26 |