업데이트:

카테고리:

/

태그: , ,

API 문서

axios 요청

axios 설치 : npm install axios --save

axios.get() 을 이용하여 해당 홈페이지에 요청한다.

src/api/axios.js 를 생성하여 해당 파일에 기본 axios 요청을 설정한다.

import axios from 'axios';

// 기본이 되는 설정을 세팅한다.
const instance = axios.create({
  baseURL: "https://api/themoviedb.org/3",
  params: {
    api_key: "bb3738ca53fd0213d09143e2353c91ce",
    language: "ko-KR"
  },  
});

export default instance;
// src/api/requests.js 
// 요청 url들을 담아둔다.
const requests = {
  fetchNowPlaying: "movie/now_playing",
  fetchNetflixOriginals: "/discover/tv?with_networks=213",
  fetchTrending: "/trending/all/week",
  fetchTopRated: "/movie/top_rated",
  fetchActionMovies: "/discover/movie?with_genres=28",
  fetchComedyMovies: "/discover/movie?with_genres=35",
  fetchHorrorMovies: "/discover/movie?with_genres=27",
  fetchRomanceMovies: "/discover/movie?with_genres=10749",
  fetchDocumentaries: "/discover/movie?with_genres=99",
};

export default requests;

스크롤시 NavBar 색깔 변경

nav 스타일에 해당 코드 입력

transition-timing-function: ease-in;
transition: all 0.5s;

[transition](https://developer.mozilla.org/ko/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions) 은 애니메이션, 속성에 변화가 생길때 일정 기간에 걸쳐 보여주는 함수이다.

// src/components/Nav.js

// 스크롤을 감지하여 어느정도 밑으로 내려가면 검은색 배경화면이 나오도록 설정
useEffect(() => {
    // 스크롤이 있을때 해당 함수 실행
    window.addEventListener("scroll", () => {
      if(window.scrollY > 50) {
        setShow(true);
      } else {
        setShow(false);
      }
    })

    return () => {
      // 스크롤이 없으면 이벤트 삭제
      window.removeEventListener("scroll", () => {})
    }
  });

이미지 배너 생성

랜덤한 영화를 가져오기 위해 Math.floor(Math.random() * 10) 을 사용

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

  const fetchData = async () => {

    // 현재 상영중인 영화정보 요청 (20개의 데이터가 들어있다.)
    const request = await axios.get(requests.fetchNowPlaying);
    
    // movie 데이터 안에서 랜덤으로 돌린다
    const movieId = request.data.results[Math.floor(Math.random() * request.data.results.length)].id;

    // 특정영화의 데이터 가져오기
		// {response.data = movieDetail} 에 할당한다.
    const {data: movieDetail} = await axios.get(`movie/${movieId}`, {
      params: {append_to_response: "videos"}
    })

    setMovie(movieDetail)
  };

글자 생략하기

const truncate = (str, n) => {
  return str?.length > n ? str.substr(0, n-1) + '...' : str;
}

글자가 존재하고 해당 글자가 n 개를 넘어간다면 n번째 이후는 …으로 대체, 아니면 그대로 출력

@media 란?

미디어 유형에 따라 만족할 경우에만 css블럭을 적용함

  • all : 모든 유형에 적합
  • print : 인쇄 결과물이나 미리보기 화면에서 표시중인 문서
  • screen : 화면이 대상
  • speech : 음성 합성장치 대상

Styled Component

Js 안에서 Css 코드를 처리하는 라이브러리

npm install --save styled-components 를 이용하여 설치

return (
      <Container>
        <HomeContainer>
          clicked
        </HomeContainer>
      </Container>
    )

// 함수 바깥에 해당 형식으로 작성
// 어떤 태그인지 어떤 스타일을 먹일지 변수로 저장후 return에서 컴포넌트 처럼 사용한다.
const Container = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  width: 100%;
  height: 100vh;
`

const HomeContainer = styled.div`
  width: 100%;
  height: 100%
`

React Router Dom

웹앱에서 동적 라우팅 구현가능, 컴포넌트 기반에 라우팅을 용이하게 한다.

SPA 를 구성하기 위해서 하나의 페이지만 처음에 로딩하고 필요한 컴포넌트를 라우팅으로 변화시킨다.

설치: npm install react-router-dom --save

초기설정 : src폴더의 index.js파일에서 BrowserRouter를 구성하고 루트 구성을 안에 넣어준다.

URL과 UI를 동기화 시켜준다.

라우터 설정
Routes : 앱에서 생성될 모든 개별 경로에 대한 상위 역할, 모든 컴포넌트 중 첫번째 컴포넌트가 반환
Route : 단일 경로를 만드는데 사용

  • path : 컴포넌트에 URL를 지정, \ 로 구성된 컴포넌트는 처음 로드될 때 먼저 랜더링 된다.
  • element : 해당 경로에 맞는 컴포넌트를 지칭
    Link : Router에서 지정한 경로를 이동할 수 있다.

중첩 라우팅

// 보여줄 레이아웃 구성
const Layout = () => {
  return (
    <div>
      <Nav/>
      <Outlet/>
      <Footer/>
    </div>
  )
}
// 중첩 라우팅 구성
function App() {
  return (
    <div className="App">
      <Routes>
        <Route path="/" element={<Layout/>}>
            <Route index element={<MainPage/>}/>
            <Route path=":movidId" element={<DetailPage/>}/>
            <Route path="search" element={<SearchPage/>}/>
        </Route>
      </Routes>
    </div>
  );
}

코드 내에서 어떤 URL 에 따라 어떤 컴포넌트를 로드 할것 인지 작성

코드를 어지럽히지 않고 깔끔하게 작성이 가능하다.

useRouteshook 을 이용해서 작성이 가능하다.

Outlet

자식 경로 요소를 랜더링할려면 부모 경로에서 <Outlet> 을 사용해야된다. 하위 경로가 랜더링 될때 중첩된 UI 표시가능

함수

  • useNavigate : 경로를 바꿔준다. 해당 경로로 이동
  • useParams : path를 읽어올 수 있다. 동적으로 데이터를 가져오기가 가능
  • useLocation : 현재 위치를 반환, 위치가 변경될때 마다 side effect를 수행할때 유용
    1. hash: “”
    2. key: “1afzzwsd”
    3. pathname: “/search”
    4. search: “?q=%EC%95%BC%ED%98%B8”
    5. state: null

    ⇒ 해당 형식으로 path와 쿼리… 다양한 정보를 반환해준다.

검색 페이지 구현

  1. 검색 바 생성

     const navigate = useNavigate();
        
     const handleChange = (e) => {
         setSearchValue(e.target.value);
         navigate(`/search?q=${e.target.value}`)
       };
    

    ⇒ 영화 검색 단어를 작성할 때마다 페이지를 이동하도록 한다

  2. 검색어 데이터 가져오기

     const useQuery = () => {
         return new URLSearchParams(useLocation().search);
       }
        
       let query = useQuery();
       // q에 적혀있는 내용을 가져오게된다.
       const searchTerm = query.get("q")
        
       useEffect(()=> {
         if (searchTerm) {
           fetchSearchMovie(searchTerm);
         }
       // 해당 데이터가 변할때마다 useEffect 데이터를 실행
       }, [searchTerm]);
        
       const fetchSearchMovie = async (searchTerm) => {
         try {
           const request = await axios.get(`/search/multi?include_adult=false&query=${searchTerm}`)
           console.log(request);
           setSearchResults(request.data.results);
         } catch (error) {
           console.error(error)
         }
       }
    

    ⇒ 쿼리에 있는 데이터를 가져와 변경될 때 마다 새로운 데이터를 가져온다.

  3. UI 호출

     const renderSearchResults = () => {
         return searchResults.length > 0 ? (
           // 검색 결과가 존재할 때
           <section className='search-container'>
             {searchResults.map((movie) => {
               if (movie.backdrop_path !== null && movie.media_type !== "person") {
                 const movieImageUrl = "https://image.tmdb.org/t/p/w500" + movie.backdrop_path
                 return (
                   <div className='movie'>
                     <div className='movie__column-poster'>
                       <img src={movieImageUrl} 
                       alt="movie" 
                       className='movie__poster'/>
                     </div>
                   </div>
                 )
               }
             })}
        
           </section>
           // 존재하지 않을 때
         ) : <section className='no-results'>
           <div className='no-results__text'>
             <p>
               찾고자 하는 검색어 "{searchTerm}"에 맞는 영화가 없습니다.
             </p>
           </div>
         </section>
       };
        
       return renderSearchResults();
    

    ⇒ render할 부분을 따로 함수로 빼두어서 조건에 따라 랜더링 파트를 조절할 수 있다.

Debounce

검색에 입력할 때 결과가 나타날 때까지 지연이 있다. debounce function에 의해 제어하고 있기 때문

UI 코드가 모든 검색어를 처리할 필요가 없어 성능이 향상될 수 있음

react에서 hook 제작

useFucntion 형식으로 파일을 만든다.

import { useState, useEffect } from "react";

export const useDebounce = (value, delay) => {
  const [debounceValue, setDebounceValue] = useState(value);

  useEffect(() => {
		// 
    const handler = setTimeout(() => {
      setDebounceValue(value)
    }, delay);

    // dalay전에 데이터가 들어온다면
    // handler의 딜레이를 초기화 시켜준다.
    return () => {
      clearTimeout(handler);
    }
    // 값이 바뀌거나 딜레이가 바뀐다면
  }, [value, delay]);

  return debounceValue;
}

영화 상세페이지 구현

  1. 클릭시 상세페이지로 이동
<div 
  className='movie__column-poster'
  onClick={() => navigate(`/${movie.id}`)}>
  1. 상세페이지에서 데이터 가져오기
// 영화 id가 변경될때마다 아래 함수를 실행
useEffect(() => {
    async function fetchData() {
      const request = await axios.get(`/movie/${movieId}`);
      console.log(request.data)
      setMovie(request.data);
    }
    
    fetchData();
  }, [movieId]);

모달창 외부 클릭 시 모달창 닫기

useRef : 특정 DOM 을 선택하는 리액트 훅

  • 클래스 컴포넌트 ⇒ React.createRef
  • 함수형 컴포넌트 ⇒ useRef

특정 돔을 잡고싶을때 ref={ref} 을 넣어주면 해당 Dom을 잡을 수 있다.

이것을 이용해서 모달창 외부면 모달 창이 닫히도록 설계!

import React, { useEffect } from 'react'

const useOnClickOutSide = (ref, handler) => {

  useEffect(() => {
    const listener = (event) => {
      console.log(ref.current)
      // 내부를 클릭하고 있으면 아무 반응 없음
      if (!ref.current || ref.current.contains(event.target)) {
        return ;
      }
      // 외부를 클릭하면 닫힘
      handler(); //setModalOpen(false);
    };

    document.addEventListener("mousedown", listener);
    document.addEventListener("touchstart", listener);

    // 언마운트 되었을 때 함수를 제거함
    return () => {
      document.removeEventListener("mousedown", listener);
      document.removeEventListener("touchstart", listener);
    };
  }, []);
}

export default useOnClickOutSide;

Swiper

공식문서를 참고해서 Swiper를 구현

Swiper하고싶은 큰틀을 Swiper 로 감싸고 각 리스트 원소를 SwiperList 로 감싼다. Swiper