날짜 : 21.07.09
참고 강의
1) NavBar 구축
[src] - [NavBar]를 구축한다.
import React from 'react';
import {
BrowserRouter as Router,
Switch,
Route,
} from "react-router-dom";
// pages for product
import LandingPage from './components/LandingPage/LandingPage';
import LoginPage from './components/LoginPage/LoginPage';
import RegisterPage from './components/RegisterPage/RegisterPage';
import Auth from './hoc/auth';
import MovieDetail from './components/MovieDetail/MovieDetail';
import NavBar from './components/NavBar/NavBar';
import Footer from './components/Footer/Footer';
function App() {
return (
<Router>
<NavBar />
<div>
<Switch>
<Route exact path="/" component={Auth(LandingPage, null)} />
<Route exact path="/login" component={Auth(LoginPage, false)} />
<Route exact path="/register" component={Auth(RegisterPage, false)} />
<Route exact path="/movie/:movieId" component={Auth(MovieDetail, null)} />
</Switch>
</div>
<Footer />
</Router>
);
}
export default App;
import React, { useState } from 'react';
import LeftMenu from './Sections/LeftMenu';
import RightMenu from './Sections/RightMenu';
import { Drawer, Button } from 'antd';
import Icon from '@ant-design/icons';
import './Sections/Navbar.css';
function NavBar() {
const [visible, setVisible] = useState(false)
const showDrawer = () => {
setVisible(true)
};
const onClose = () => {
setVisible(false)
};
return (
<nav className="menu" style={{ position: 'fixed', zIndex: 5, width: '100%' }}>
<div className="menu__logo">
<a href="/">Logo</a>
</div>
<div className="menu__container">
<div className="menu_left">
<LeftMenu mode="horizontal" />
</div>
<div className="menu_rigth">
<RightMenu mode="horizontal" />
</div>
<Button
className="menu__mobile-button"
type="primary"
onClick={showDrawer}
>
<Icon type="align-right" />
</Button>
<Drawer
title="Basic Drawer"
placement="right"
className="menu_drawer"
closable={false}
onClose={onClose}
visible={visible}
>
<LeftMenu mode="inline" />
<RightMenu mode="inline" />
</Drawer>
</div>
</nav>
)
}
export default NavBar;
/* eslint-disable jsx-a11y/anchor-is-valid */
import React from 'react';
import { Menu } from 'antd';
import axios from 'axios';
import { USER_SERVER } from '../../../Config';
import { withRouter } from 'react-router-dom';
import { useSelector } from "react-redux";
function RightMenu(props) {
const user = useSelector(state => state.user)
const logoutHandler = () => {
axios.get(`${USER_SERVER}/logout`).then(response => {
if (response.status === 200) {
props.history.push("/login");
} else {
alert('Log Out Failed')
}
});
};
if (user.userData && !user.userData.isAuth) {
return (
<Menu mode={props.mode}>
<Menu.Item key="mail">
<a href="/login">Signin</a>
</Menu.Item>
<Menu.Item key="app">
<a href="/register">Signup</a>
</Menu.Item>
</Menu>
)
} else {
return (
<Menu mode={props.mode}>
<Menu.Item key="logout">
<a onClick={logoutHandler}>Logout</a>
</Menu.Item>
</Menu>
)
}
}
export default withRouter(RightMenu);
@import '~antd/dist/antd.css';
.menu {
padding: 0 20px;
border-bottom: solid 1px #e8e8e8;
overflow: auto;
box-shadow: 0 0 30px #f3f1f1;
background-color: white;
}
.menu__logo {
width: 150px;
float: left;
}
.menu__logo a {
display: inline-block;
font-size: 20px;
padding: 19px 20px;
}
.menu__container .ant-menu-item {
padding: 0px 5px;
}
.menu__container .ant-menu-submenu-title {
padding: 10px 20px;
}
.menu__container .ant-menu-item a,
.menu__container .ant-menu-submenu-title a {
padding: 10px 15px;
}
.menu__container .ant-menu-horizontal {
border-bottom: none;
}
.menu__container .menu_left {
float: left;
}
.menu__container .menu_rigth {
float: right;
}
.menu__mobile-button {
float: right;
height: 32px;
padding: 6px;
margin-top: 8px;
display: none !important; /* use of important to overwrite ant-btn */
background: #3e91f7;
}
.menu_drawer .ant-drawer-body {
padding: 0 !important;
}
/* align header of Drawer with header of page */
.menu_drawer .ant-drawer-header {
padding: 14px 24px !important;
}
@media (max-width: 767px) {
.menu__mobile-button {
display: inline-block !important;
}
.menu_left,
.menu_rigth {
display: none;
}
.menu__logo a {
margin-left: -20px;
}
.menu__container .ant-menu-item,
.menu__container .ant-menu-submenu-title {
padding: 1px 20px;
}
.menu__logo a {
padding: 10px 20px;
}
}
2) [NavBar] - [Sections] - [LeftMenu.js] 파일 생성
import React from 'react';
import { Menu } from 'antd';
const SubMenu = Menu.SubMenu;
const MenuItemGroup = Menu.MenuItemGroup;
function LeftMenu(props) {
return (
<Menu mode={props.mode}>
<Menu.Item key="mail">
<a href="/">HOME</a>
</Menu.Item>
<Menu.Item key="favorite">
<a href="/favorite">Favorite</a>
</Menu.Item>
</Menu>
)
}
export default LeftMenu;
3) [src] - [components] - [FavoritePage] - [FavoritePage.js] 파일 생성
import React from 'react';
import './favorite.css';
function FavoritePage() {
return (
<div style={{ width: '85%', margin: '3rem auto' }}>
<h2>Favorite Movies</h2>
<hr />
<table>
<thead>
<tr>
<th>Movie Title</th>
<th>Movie RunTime</th>
<td>Remove From Favorite</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
)
}
export default FavoritePage;
4) [FavoritePage] - [favorite.css] 파일 생성
table {
font-family: Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
}
td,
th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}

5) [FavoritePage] - [FavoritePage.js] useEffect 생성
function FavoritePage() {
useEffect(() => {
axios.post('/api/favorite/getFavoredMovie', { userFrom: localStorage.getItem('userId') })
.then(response => {
if (response.data.success) {
} else {
alert('영화 정보를 가져오는데에 실패하였습니다')
}
})
})
}
6) [server] - [routes] - [favorite.js] 에 route 추가
router.post('/getFavoredMovie', (req, res) => {
Favorite.find({ 'userFrom': req.body.userFrom })
.exec((err, favorites) => {
if (err) return res.status(400).send(err)
return res.status(200).json({ success: true, favorites })
})
})
7) [FavoritePage] - [FavoritePage.js]에 useState 생성, return 수정
function FavoritePage() {
const [Favorites, setFavorites] = useState([])
...
...
return(
...
<tbody>
{Favorites.map((favorite, index)=>(
<tr key={index}>
<td>{favorite.movieTitle}</td>
<td>{favorite.movieRunTime} mins</td>
<td><button>Remove</button></td>
</tr>
))}
</tbody>
...
...
)
}
1) [FavoritePage] - [FavoritePage.js]에 포스터 미리보기 코드 작성
import { Popover } from 'antd';
import { IMAGE_BASE_URL } from '../../Config';
function FavoritePage() {
const renderCards = Favorites.map((favorite, index) => {
const content = (
<div>
{favorite.moviePost ?
<img src={`${IMAGE_BASE_URL}w500${favorite.moviePost}`} /> : "No Image"}
}
</div>
)
return (<tr key={index}>
<Popover content={content} title={`${favorite.movieTitle}`}>
<td>{favorite.movieTitle}</td>
</Popover>
<td>{favorite.movieRunTime} mins</td>
<td><button>Remove</button></td>
</tr>
)
})
...
...
return (
<div style={{ width: '85%', margin: '3rem auto' }}>
<h2>Favorite Movies</h2>
<hr />
<table>
<thead>
<tr>
<th>Movie Title</th>
<th>Movie RunTime</th>
<td>Remove From Favorite</td>
</tr>
</thead>
<tbody>
{renderCards}
</tbody>
</table>
</div>
)
}
영화제목 위에 커서를 가져가면 포스터를 볼 수 있다.

1) 버튼에 onClick 이벤트 생성
movieId와 userFrom에 해당하는 favorite을 삭제해야기 때문에 onClick에서 movieId와 userFrom 정보를 가져와야한다
function FavoritePage() {
const onClickDelete = (movieId, userFrom)=>{
const variables = {
movieId,
userFrom
}
axios.post('/api/favorite/removeFromFavorite',variables)
.then(response=>{
if(response.data.success){
}else{
alert('삭제에 실패하였습니다')
}
})
}
const renderCards = Favorites.map((favorite, index) => {
const content = (
<div>
{favorite.moviePost ?
<img src={`${IMAGE_BASE_URL}w500${favorite.moviePost}`} /> : "No Image"}
}
</div>
)
return (<tr key={index}>
<Popover content={content} title={`${favorite.movieTitle}`}>
<td>{favorite.movieTitle}</td>
</Popover>
<td>{favorite.movieRunTime} mins</td>
<td><button onClick={()=>onClickDelete(favorite.movieId, favorite.userFrom)}>Remove</button></td>
</tr>
)
})
}
2) [routes] - [favorite.js]에 route 생성
router.post('/removeFromFavorite', (req, res) => {
Favorite.findOneAndDelete({ movieId: req.body.movieId, userFrom: req.body.userFrom })
.exec((err, result) => {
if (err) return res.status(400).send(err)
return res.status(200).json({ success: true })
})
})
3) [FavoritePage] - [FavoritePage.js]에서 remove 수행
axios를 매번 refresh하여 새로운 변화값 나타내는 방법으로 수행
function FavoritePage() {
const fetchFavoredMovie = () => {
axios.post('/api/favorite/getFavoredMovie', { userFrom: localStorage.getItem('userId') })
.then(response => {
if (response.data.success) {
setFavorites(response.data.favorites)
} else {
alert('영화 정보를 가져오는데에 실패하였습니다')
}
})
}
useEffect(() => {
fetchFavoredMovie()
}, [])
const onClickDelete = (movieId, userFrom) => {
const variables = {
movieId,
userFrom
}
axios.post('/api/favorite/removeFromFavorite', variables)
.then(response => {
if (response.data.success) {
fetchFavoredMovie()
} else {
alert('삭제에 실패하였습니다')
}
})
}
}