유데미, 스나이퍼팩토리,10주완성 프로젝트 캠프, 프론트엔드(리액트/react) - 리액트 라우터

YoungWook·2023년 6월 27일
0
post-thumbnail

☁️ 라우터(Router) 란 ?

👉 정의

  • 사용자가 요청한 URL에 따라 해당 URL에 맞는 페이지를 보여주는 것이라고 생각할 수 있다.
  • 리액트에서는 라우팅 관련 라이브러리가 많이 있는데, 이중 가장 많이 쓰이는 리액트 라우터(React Router)를 사용해보려 한다.

🔌 리액트 라우터(React Router)

  • 사용자가 입력한 주소를 감지하는 역할을 하며, 여러 환경에서 동작할 수 있도록 여러 종유의 라우터 컴포넌트를 제공한다.
  • 가장 많이 사용하는 라우터 컴포넌트는 BrowserRouter와 HashRouter이다.

🔍 종류

  1. BrowserRouter : HTML5를 지원하는 브라우저의 주소를 감지 한다.
  2. HashRouter 해시 주소(https://duddnr787.tistory.com)를 감지 한다.

✏️  설치

  • npm
npm install react-router-dom
  • yarn
yarn add react-router-dom

📃 활용

  1. BrowserRouter 태그로 컴포넌트 사용하기

    • BrowserRouter를 사용 할 것이기 때문에, BrowserRouter 태그로 컴포넌트를 감싸주자.
    • Header는 모든 URL에 공통 적용할 Component로 최상단에 위치 할 예정이다.
  2. Routes 태그, Route 태그 컴포넌트 사용하기

    • Routes 태그 컴포넌트는 여러 Route를 감싸서 그 중 규칙이 일치하는 라우트 단 하나만을 렌더링 시켜주는 역할을 한다.
    • Route 태그는 path속성에 경로, element속성에는 컴포넌트를 넣어 준다.
  3. Link 태그 컴포넌트 사용하기

    • 웹 페이지에서는 원래 링크를 보여줄 때 a태그를 사용한다. 하지만 a태그는 클릭시 페이지를 새로 불러오기 때문에 사용하지 않는다.
      ( a 태그 사용 ❌ )
    • Link 컴포넌트를 사용하는데, 생김새는 a태그를 사용하지만, History API를 통해 브라우저 주소의 경로만 바꾸는 기능이 내장되어 있다.
      (새로고침이 되지 않는다 !)
       문법 : <Link to="경로">링크명</Link>
       import { Link } from 'react-router-dom';

⛏️ 실습

React 영화 웹 페이지 만들어보기.

✏️ 요구사항

  1. React router 사용하기.
  2. 오픈 API 사용해서 데이터 가져오기.

👾 코드

App.js

  • 먼저 BrowserRouter 태그로 전체를 감싸주며, 페이지 이동이 필요한 페이지들은 Routes로 감싸주었다.
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Home from 'pages/Home';
import Detail from 'pages/Detail';
import Navigation from 'components/Navigation';
import Footer from 'components/Footer';
import Search from 'pages/Search';


function App() {
  return (
    <BrowserRouter>
      <Navigation />
      <Routes>
        <Route path='/' element={<Home />} />
        <Route path='/movie/:id' element={<Detail />} />
        <Route path='/search/:searchMovie' element={<Search />} />
      </Routes>
      <Footer />
    </BrowserRouter>
  );
}

export default App;

📃 Home 페이지

  • Movie 컴포넌트가 들어있다.
import Movies from 'components/Movies';
import React from 'react';

const Home = () => {
  return (
    <div>
      <Movies />
    </div>
  );
};

export default Home;

// Movie.js

import React, { useEffect, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import styles from 'styles/Movie.module.css';

const Movies = () => {
  const [movies, setMovies] = useState([]);

  const navigate = useNavigate();

  const getMovie = () => {
    fetch(
      'https://yts.mx/api/v2/list_movies.json?minimum_rating=8.8&sort_by=year'
    )
      .then((response) => response.json())
      .then((json) => {
        const moviesData = json.data.movies;
        const slicedMovies = moviesData.slice(0, 8); 
        setMovies(slicedMovies);
        console.log(json.data);
      });
  };

  useEffect(() => {
    getMovie();
  }, [])

  const movieDetailHandle = (id) => {
    navigate(`/movie/${id}`);
  }
  return (
    <div>
      <div className={styles.movieWrap}>
        {movies.map(movie => {
          return (
            <div key={movie.id} className={styles.movieImg} style={{ backgroundImage: "url(" + `${movie.medium_cover_image}` + ")" }} onClick={() => movieDetailHandle(movie.id)}>
              <div className={styles.overlay}>
                <div className={styles.head}>
                  <p></p>
                  <p>{movie.rating} / 10</p>
                </div>
                <div className={styles.genre}>
                  <p>{movie.genres}</p>
                </div>
                <button className={styles.btn}>View Detail</button>
              </div>
            </div>
          )
        })}
      </div>
    </div>
  );
};

export default Movies;

📃 Detail 페이지

  • 파라미터로 넘겨준 id 를 useParams() 를 이용해 가져온 후 MovieDetail 컴포넌트로 props로 넘겨준다.
import MovieDetail from 'components/MovieDetail';
import React from 'react';
import { useParams } from 'react-router-dom';
import styles from 'styles/MovieDetail.module.css';

const Detail = () => {
  const { id } = useParams();

  return (
    <div className={styles.box}>
      <MovieDetail id={id} />
    </div>
  );
};

export default Detail;

//MovieDetail.js

import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import styles from 'styles/MovieDetail.module.css';

const MovieDetail = ({ id }) => {
  const [movieDetail, setMovieDetail] = useState({});

  const getMovieDetail = () => {
    fetch(
      `https://yts.mx/api/v2/movie_details.json?movie_id=${id}`
    )
      .then((response) => response.json())
      .then((json) => {
        console.log(json.data)
        setMovieDetail(json.data.movie);
      });
  };

  useEffect(() => {
    getMovieDetail();
  }, [])

  return (
    <div className={styles.wrap}>
      <div className={styles.movieDetailWrap}>
        <img src={`${movieDetail.medium_cover_image}`} alt="디테일 이미지" className={styles.img} />
        <div className={styles.infoWrap}>
          <div className={styles.title}>{movieDetail.title}</div>
          <div className={styles.year}>{movieDetail.year}</div>
          <div className={styles.genres}>{movieDetail.genres}</div>
          <span className={styles.rating}>★ {movieDetail.rating} / 10</span>
          <span className={styles.download}>⬇ {movieDetail.download_count}</span>
          <button className={styles.btn}>⬇ Download</button>
        </div>
        <div className={styles.info}><h3>줄거리</h3>{movieDetail.description_full}</div>
      </div>
    </div>
  );
};

export default MovieDetail;

📃 검색 페이지

  • 검색창 내용을 파라미터로 넘겨주어 useParams() 를 이용해 값을 받아왔다.
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import styles from 'styles/Search.module.css'
import stylesMovie from 'styles/Movie.module.css';

const Search = () => {
  const { searchMovie } = useParams();
  const [searchResults, setSearchResults] = useState([]);
  const navigate = useNavigate();

  const handleSearch = async () => {
    try {
      const response = await fetch(
        `https://yts.mx/api/v2/list_movies.json?query_term=${searchMovie}`
      );
      const data = await response.json();
      console.log(data.data);
      setSearchResults(data.data.movies);
      console.log(searchResults);
    } catch (error) {
      console.log('Error:', error);
    }
  };

  const movieDetailHandle = (id) => {
    navigate(`/movie/${id}`);
  }

  useEffect(() => {
    handleSearch();
  }, [])

  return (
    <div className={styles.searchWrap} >
      {searchResults.length !== 0 ? (
        <div>
          <h2><strong>'{searchMovie}' </strong>의 검색 결과는 최대 <strong> {searchResults.length} </strong> 건 입니다.</h2>
          <div className={stylesMovie.movieWrap} style={{justifyContent: 'center'}}>
            {searchResults.map(movie => {
              return (
                <div key={movie.id} className={stylesMovie.movieImg} style={{ backgroundImage: "url(" + `${movie.medium_cover_image}` + ")" , marginRight:'20px'}} onClick={() => movieDetailHandle(movie.id)}>
                  <div className={stylesMovie.overlay}>
                    <div className={stylesMovie.head}>
                      <p></p>
                      <p>{movie.rating} / 10</p>
                    </div>
                    <div className={stylesMovie.genre}>
                      <p>{movie.genres}</p>
                    </div>
                    <button className={stylesMovie.btn}>View Detail</button>
                  </div>
                </div>
              )
            })}
          </div>
        </div>
      ) : (
        <div>
          <h1>검색 결과가 없습니다 ㅠ..ㅠ</h1>
        </div>
      )}
    </div>
  );
};

export default Search;

🔑 결과

업로드중..

📌 후기

일단 먼저 useState()로 상태 관리 하는 법과 useEffect()를 이용해 특정 작업을 수행하는 법에 대해 많이 익숙해진 것 같다. 또한 오픈 API를 이용해 데이터가 어떤 식으로 요청 응답이 되는지 알 수 있었다.
부족한점은 아직 화면 레이아웃 잡는 법과 스타일링이 부족한 것 같다.


본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.
profile
영차 조와쒀

0개의 댓글