React YouTube Clone 정리

한대희·2023년 5월 17일
0
post-thumbnail

구현한 기능

  1. 유튜브 api 불러오기
  2. 검색한 keyword에 맞는 동영상 불러오기
  3. 동영상 정보 표시하기
  4. 해당 동영상과 관련된 동영상 표시하기

핵심 Skill

< 1. YouTube api 사용 >

  1. 구글에 youtube api 검색

  2. youtube api를 사용하려면 구글계정이 필요하고, api key가 필요하다.

  3. api key는 구글에 개발자 콘솔에 들어가서 발급 받을 수 있다.

  4. key를 발급 받기 전에 'API 및 서비스' 라는 탭에 들어 가서 Youtube Data api를 추가를 해주면 credential이 필요하다고 할텐데 그럼 해당 버튼을 눌러서 api key를 발급 받으면 된다.

  5. key를 발급받고, youtube api의 reference에 들어가 sidebar에서 원하는 api를 골라보자. 예를 들어 아래 사진에서 list(by keyword)라는 것은 search를 할 때 즉,키워드로 검색을 했을때 보여줄 list라는 의미다. 저기서 </>를 클릭하면 해당 api를 호출 할 수 있는 url이 나온다.

  1. </>를 클릭하여 아래와 같은 url을 얻으면 마지막에 발급 받은 api key를 입력 해주면 된다.
    https://youtube.googleapis.com/youtube/v3/search?part=snippet&maxResults=25&q=surfing&key=[YOUR_API_KEY]

  2. http인 위의 주소를 복사해서 마지막에 기존에 발급받았던 api key를 입력해주면 된다. q라고 되어 있는 곳에 입력된 값이 keyword다 위의 주소는 surfing을 검색하면 보여줄 list들을 보여줄 것이다.

  3. 결과를 확인 해보기 위해 postman을 통해 api호출이 어떤 데이터가 전달되는지 확인 해 보자

  4. postman에 가서 New Collection을 만들어 주고 여기서 발급 받은 api key를 key라는 이름의 변수로 추가를 해준다. 그리고 아래와 같이 변수로 입력한 key를 사용을 해주면 아래와 같이 데이터가 불러와 지는 것을 확인해 볼 수 있다.

< 2. 리액트 라우터 >

1. createBrowserRouter, RouterProvider

react router를 이용하여 CSR(Client Side Routing)을 구현 하였다.

CSR이란 라우팅이 될 때 페이지가 새로고침 되면서 전체 페이지가 로딩 되는 것이 아니라 필요한 부분만 부분적으로 업데이트 되는 것을 말한다.

예를 들어 보면 어떤 버튼을 클릭시 상단의 Nav Bar는 그대로 있고 그 아래에 해당 버튼에 해당하는 페이지가 렌더링이 되는 이러한 방식을 의미한다.

리액트 라우터에는 Outlet이라는 것이 있는데 Router경로 설정에 children에 입력한 경로들은 Outlet에서 렌더링이 된다.

//😃 index.js에서 createBrowserRouter를 통해 router경로 설정을 해준다.
const router = createBrowserRouter([
  {
// 최상위 경로에서는 App 컴포넌트를 렌더링 하고
    path: '/',
    element: <App />,
    errorElement: <NotFound />,
//😃  Outlet에서는 아래의 경로에 해당 하는 컴포넌트가 렌더링이 된다.
//😃 index: true는 최상위 경로 즉 '/'를 의미한다.
    children: [
      {index:true, element: <Videos />},
      {path: 'videos', element: <Videos />},
      {path: 'videos/:keyword', element: <Videos />},
      {path: 'videos/watch/:videoId', element: <VideoDetail />},
    ]
  }
])

//😃 위에서 설정한 router를 RouterProvider에 전달하면 된다.
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

그 다음 아래의 App.js에거 Outlet을 설정해 주면 Outlet이 아닌 SearchHeader컴포넌트는 그대로 있고 Outlet에서 childeren에 들어 있는 모든 컴포넌트 들이 라우팅이 될 것이다.

function App() {
  return (
    <>
      <SearchHeader />
      <YouTubeApiProvider>
        <QueryClientProvider client={queryClient}>
          <Outlet />
        </QueryClientProvider>
      </YouTubeApiProvider>
    </>
  );
}

2. useNavigate

useNaigate를 사용하면 지정된 경로로 이동할 수 있다.
현재 SearchHeader컴포넌트에서 키워드를 검색하면 해당 키워드에 관한 videos를 불러와서 렌더링 하는 검색 기능이 구현되어 있다.
input창에 키워드를 검색하여 submit하면 해당 키워드가 붙은 경로의 페이지가 라우팅이 되도록 하였다.


import React, {useEffect, useState} from 'react'
import {BsYoutube, BsSearch} from 'react-icons/bs'
import { Link, useNavigate, useParams } from 'react-router-dom'


export default function SearchHeader() {
  const navigate = useNavigate()
  const [text, setText] = useState('')
  const {keyword} = useParams()

  useEffect(() => (
    setText(keyword || '')
  ), [keyword])
  
  const handleChange = (e) => {
    setText(e.target.value)
  }
  //😃 키워드를 검색하는 input이 submit이 되면 아래와 같이 navigate이 지정된 경로로 이동이 되고 현재 input창에 입력된 값을 
  	   param으로 전달 하고 있다.
  const handleSubmit = (e) => {
    e.preventDefault()
    setText('')
    navigate(`/videos/${text}`)
  }
  return (
    <header className='w-full flex p-4 text-2xl border-b border-zinc-600 mb-4'>
      <Link to='/' className='flex items-center'>
        <BsYoutube className='text-4xl text-logo'/>
        <h1 className='font-bold ml-2 text-3xl'>YouTube</h1>
      </Link>
      <form className='w-full flex justify-center' onSubmit={handleSubmit}>
        <input className='w-7/12 p-2 outline-none bg-black text-gray-50' type="text" placeholder='Search...' value={text} onChange={handleChange} />
        <button className='bg-zinc-600 px-4'><BsSearch /></button>
      </form>
    </header>
  )
}

3. useParam

useParam을 사용하면 현재 url에 전달된 param을 가져 올 수 있다.

< 3. 리액트 쿼리 >

리액트 쿼리는 네트워크에서 가져온 데이터의 상태를 관리할 수 있게 도와주는 라이브러리이다.

데이터를 불러오는 로직을 커스텀 훅으로 만들어도 될 텐데 리액트 쿼리라는 라이브러리를 사용하는 이유는 리액트 훅, 즉 함수는 값의 재사용이 아니라 로직의 재사용이기 때문이다.

이게 무슨 말이냐면 데이터를 불러오는 커스텀 훅을 만들면 각각의 컴포넌트에서 해당 훅을 사용을 할 것이고, 그러면 각각의 컴포넌트 마다 다른 값이 컴포넌트의 훅 안의 state에 할당이 될 것이다.

즉 데이터를 불러오는 로직만 재사용할 뿐 값은 해당 훅을 사용하는 컴포넌트 마다 state값이 다 다르게 되는 것이다.

리액트 쿼리를 사용하면 이 문제를 해결할 수 있고, 더 나아가 만약 페이지가 다시 렌더링 되어 리액트 쿼리가 다시 호출이 되어도 이미 불러온 데이터, 즉 캐시된 데이터가 있다면 데이터를 더 이상 불러오지 않는다.

//😀 queryClient라는 인스턴스를 만들어 준다.
const queryClient = new QueryClient()
function App() {
  return (
    <>
      <SearchHeader />
      <YouTubeApiProvider>
//😀 불러온 데이터 즉, 영상들은 Navbar아래의 Outlet에서만 필요하기 때문에 Outlet을 Provider로 감싸 주고 위에서 만들어둔 
	 인스턴스를 전달한다.
        <QueryClientProvider client={queryClient}>
          <Outlet />
        </QueryClientProvider>
      </YouTubeApiProvider>
    </>
  );
}

useQuery를 사용하면 불러온 데이터를 캐시 메모리에 저장을 해준다. 따라서 useQuery를 사용할 때 캐시 메모리에 저장될 key이름과 데이터를 불러오는 함수를 전달해야 한다.

key이름은 배열로 전달 할 수 있는데 배열로 여러개의 key를 전달하면 같은 데이터라도 각각의 key마다 다른 캐시를 사용할 수 있다.

아래의 코드는 우리가 검색창에 입력한 keyword별로 다른 네트워크 요청을 하므로 아래와 같이 배열에 두개의 key를 넣어 Keyword별로 다른 캐시를 사용할 수 있게 해 준 것이다.

그리고 아래의 코드에 나와 있는 staleTime은 useQuery로 가져온 데이터가 얼마의 시간이 지나야 오래된 데이터로 간주되어 데이터를 다시 불러 올 것인지를 지정해 주는 것이다. 만약 시간을 지정해 주는 이유는 useQuery로 가져온 데이터는 데이터를 불러오는 즉시 오래된 데이터로 간주 되어 계속 해서 데이터를 불러 오게 되어 성능상에 문제가 될 수 있기 때문이다.

const { data: videos } = useQuery(['videos',keyword], () => youtube.search(keyword), {staleTime: 1000 * 60 })

< 4. axios >

먼저 데이터를 불러올 때 fetch를 사용할 수 있는데 fetch는 브라우저에서 제공하는 유용한 api이다. 하지만 fetch의 몇가지 문제점이 있다.

먼저 브라우저에서 제공하는 fetch api를 사용을 했을 때 몇가지 문제점이 있다.

첫번째로는 데이터를 받아 올 때마다 json()를 통해 데이터를 자바스크립트 객체로 변환을 해 줘야 한다.

두번째로는 fetch를 사용할 때 에러가 발생하면 catch를 통해 잡을 수 있지만, catch에 잡히지 않는 에러들이 있는데 이 에러들은 then으로 들어와서 then안에서 thorw new Error를 통해 다시 한번 에러 처리를 해줘야 하는 번거로움이 있다.

이것을 해결해 줄 수 있는 것이 axios 라이브러리이다.

profile
개발 블로그

0개의 댓글