[React & NodeJS]영화웹페이지#5

jeje·2021년 7월 9일
0

영화웹페이지

목록 보기
5/5

날짜 : 21.07.09
참고 강의

1. Favorite 페이지

1) NavBar 구축

[src] - [NavBar]를 구축한다.

  • [App.js] 수정(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;
  • [NavBar.js] 코드
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;
  • [RightMenu.js] 코드
/* 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);
  • [Navbar.css] 코드
@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>
	...
	...
    )
}

2. 포스터 미리보기

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>
    )
}
  • 결과화면

    영화제목 위에 커서를 가져가면 포스터를 볼 수 있다.

3. Remove 버튼 생성

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('삭제에 실패하였습니다')
                }
            })
    }
}

Full Code

Walang Github

Walang Notion

profile
나의 색으로 가득하게

0개의 댓글