엘리스 AI트랙 4기/프로젝트

Youtube search API 사용해서 검색 목록 이용하기

남쪽마을밤송이 2022. 5. 16. 13:53

 [01 Youtube API ] 

 (1) 사용하게 된 배경 

  • 프로젝트를 진행하며 메인 페이지에 게임 관련 유튜브 동영상을 보여주고 싶었다.
  • 따라서 "게임 리뷰"라는 키워드로 검색한 동영상 목록을 출력하려고 하는데, 당연히 블로그 검색부터 했다. 대부분의 블로그는 Channels, Playlists, Videos 메소드를 사용했는데 나는 검색 기능만 필요하기 때문에 Search 메소드를 사용할 것이다.
  • 그런데 Search 메소드 사용한 블로그대로 다 해봤는데도 안돼고 감이 안잡혀서... 공식 문서를 확인했다. 사실 그래도 모르겠어서 보류하고 뉴스 크롤링 기능부터 구현했는데 여기서 request 라이브러리를 사용한 걸로 감이 와서 해결할 수 있었다!

 (2) Youtube API 요청시 주의할 점 

  • Youtube API 요청은 무료로 사용할 수 있는 대신 하루에 요청할 수 있는 할당량이 정해져 있다. 일반적으로 일간 할당량은 10,000이다.
  • 따라서 지속적으로 유지할 서비스를 계획하는 개발자들은 이 할당량을 신중히 계산하고 할당량을 조금이라도 더 적게 소비하는 쿼리를 짜서 요청해야 할 것이다.
    • 나는 이번 프로젝트에 다양한 기능이 필요하지 않아서 고민해보지 않았지만 이와 관련한 블로그를 확인하니 여러 가지 메소드를 조합해서 필요한 정보 이상을 응답하지 않도록 하는 것 같았다.
  • 사용하는 메소드와 요청 형식에 따라 한 번 요청시 소비되는 cost가 달라지기 때문에, 여기를 확인하고 본인이 요청하는 쿼리의 cost 계산해봐야 한다.
    • 나는 단순하게 search 메소드를 사용하여 list를 응답받기 때문에 공식 문서에 의하면 100 cost를 사용한다. 그러면 하루에 100번의 요청밖에 하지 못한다는 것이므로 하루동안 100명이 새로고침 없이 한 번씩만 접속할 수 있다는 결론이 나온다... 할당량을 다 쓰면 유튜브에서 응답을 해주지 않으므로 당연히 페이지에는 동영상이 제대로 출력될 수 없을 것이다.
    • 이러한 문제를 해결하기 위해 나는 Redis 캐시 서버를 사용했고, 안전하게 Youtube API 응답은 86400초, 그러니까 하루동안 저장되도록 설정해두었다.

 [02 Youtube API 키 발급받기 ]

  • Youtube API를 사용하려면 먼저 구글에서 API Key를 발급받는 과정이 필요하다.

  • 검색창에 "YouTube Data API v3"를 검색하고 들어가서 사용 버튼을 누른다.

redis-cli # redis의 기본 포트인 6379로 접속
ping # pong으로 응답
  •  API 및 서비스 - 사용자 인증정보 탭에서  사용자 인증 정보 만들기 - API 키 를 클릭한다.
    • 나는 처음에 다른 블로그들을 보고 OAuth 클라이언트 ID도 필요한 줄 알고 만들었는데, 본인의 유튜브 채널에 접근하는 기능을 사용하는 것이 아니면 필요 없다.

  • 바로 API 키가 생성되는데 그 키를 복사해서 아래에서 사용하면 된다.

 [03 Youtube API 사용하기 ] 

 (1) params 설정하고 요청하기 

  • 나는 유튜브 관련 메소드를 두 개로 나눴다. 첫 번째는 요청을 수행하는 메소드, 두 번째는 응답받아 온 데이터를 내가 원하는대로 가공하는 메소드이다.
    • 하나로 합칠 수도 있을 것 같은데 나는 비동기 처리를 쉽게 하기 위해서 이렇게 나눴다.
  • 요청을 보낼 때는 공식 문서를 참고해서 요청 링크와 파라미터를 설정해줘야 한다. 나는 다음과 같이 설정했다.
    • axios 대신 저번 뉴스 크롤링할 때 사용했던 request를 써도 된다.
    • key에는 발급받은 Youtube API Key를 넣어준다.
    • q는 검색할 키워드를 넣고, 입력 받을거라면 변수로 설정도 가능하다.
    • part는 id 혹은 snippet을 설정할 수 있는데 대부분의 정보가 snippet에 있으므로 snippet으로 설정한다.
    • type은 채널이나 플레이리스트도 있는데 나는 비디오만 설정했다.
    • maxResults는 한 번에 받아올 응답 개수이다.
    • fields가 중요한데 API 요청시 소비하는 할당량을 조금이라도 줄이려면(할당량 최적화) 이 속성을 반드시 사용해야 한다. 나는 동영상 id와 title만 필요했기 때문에 다음과 같이 설정해줬다.
    • videoEmbeddable을 true로 하면 웹페이지로 퍼갈 수 있는 동영상만 검색한다는데 혹시 몰라 설정했다.
getYoutubeDatas: async () => {
    const params = {
      key: process.env.YOUTUBE_API_KEY,
      q: "게임 리뷰",
      part: "snippet",
      type: "video",
      maxResults: 8,
      fields: "items(id, snippet(title))",
      videoEmbeddable: true,
    };
    const youtubeDatas = axios.get(
      `https://www.googleapis.com/youtube/v3/search`,
      {
        params,
      }
    );
    return youtubeDatas;
  },
  • 여기까지 작성하고 youtubeDatas 응답값을 출력해보면 다음과 같다. 뭔가 200 OK로 받아온 것 같긴 한데 알아볼 수 없는 상태의 데이터이다.

 (2) 응답 데이터 가공하기 

  • 이제 뉴스 크롤링했을 때와 비슷하게 위의 데이터에서 내가 원하는 데이터만 추출하는 과정이 필요하다.
  • 위의 콘솔창을 맨 아래로 내려보면 다음과 같은 내용이 있을 것이다.
    • 나는 maxResults를 8개로 설정했는데 따라서  data.items 에 배열로 8개의 Object가 들어있음을 알 수 있다.

  • 이러한 구조를 기반으로 이제 코드를 짤 수 있다. 나는 각각의 Object에 forEach문을 사용해서 videoId와 title을 뽑아냈고 이걸 다시 객체 형태로 저장해서 반환했다.
    • 당연히 원하는 값이 잘 들어있는지  console.log( ) 로 확인하는 게 좋다.
getSearchedVideos: async (youtubeDatas) => {
    const searchedVideos = [];
    const videoLists = youtubeDatas.data.items;
    videoLists.forEach((element) => {
      const videoId = element.id.videoId;
      const title = element.snippet.title;
      searchedVideos.push({ videoId, title });
    });
    return searchedVideos;
  },
  • Postman으로 테스트하기 위해 Router 계층 코드까지 완성하고 테스트하면 내가 원한 형식대로 응답이 오는 것을 확인할 수 있다.

 (3) Front에서 적용한 모습 

  • Front분들이 바빠서 React에 youtube 동영상 플레이어 넣는 방법까지는 내가 찾아봤다.
  • 이것도 굳이 deprecated된 iframe 태그를 사용해야 하나 고민이 많았는데 깔끔하게 react-youtube를 사용하면 됐다. 무려 Youtube가 공식적으로 만들어 준 패키지!
    • 옵션은 원하는대로 설정하면 되고 이유는 알 수 없지만(아마도 string 타입에서 "를 그대로 쓰면 이후 값들이 전송되지 않으며 오류가 뜨기 때문일것으로 예상) 위에 Postman 응답값처럼  " 가  " 로 받아와져서 replace 함수와 정규표현식을 사용해서 해결했다.
import YouTube from "react-youtube";

...

const opts = {
    width: "250",
    height: "150",
    playerVars: {
      autoplay: 0,
    },
  };

...

<div className="popularChart youtube">
          <Row className="mb-3" style={{ marginTop: 40, marginLeft: 40 }}>
            <h3>유튜브 인기 동영상</h3>
          </Row>
          <div className="centeral">
            <div
              style={{
                margin: 20,
                display: "flex",
                flexDirection: "row",
                flexWrap: "wrap",
                width: "80%",
              }}
            >
              {video.map((item) => (
                <Col>
                  <Row
                    // className="justify-content-center"
                    style={{ padding: "10px 0" }}
                  >
                    <YouTube videoId={item.videoId} opts={opts} />
                    <div style={{ width: 280 }}>
                      {item.title.replace(/&QUOT;/gi, '"')}
                    </div>
                  </Row>
                </Col>
              ))}
            </div>
          </div>
          <div></div>
        </div>
  • 최종적으로 적용한 모습은 다음과 같다.

 [04 느낀점 ] 

  • 이걸 프로젝트 중반에 해서 벌써 3주는 지나가지고 기억을 더듬으면서 기록했다. 확실히 기록하면서 더 자세히 알게 된 것도 있고!
  • 수행착오를 겪은 과정을 많이 뺐는데 별별 방법을 다 따라해보다가 axios로 공식문서에 나온 경로로 요청해서 쉽게 해결하신 분이 있길래 이게 된다고? 하며 삽질한 코드 다 지우고 해봤더니 됐다... 역시 공식문서😂
  • 그리고 또 이 포스팅을 정리하면서 느낀점은 지금 우리 프로젝트 구조는 아래와 같은데 사실 front에서 바로 요청하면 axios도 한 번만 사용해서 속도가 훨씬 빠를 것 같다.
front에서 back으로 해당 api 요청 » back에서 youtube에 요청 » 응답값 가공해서 front로 전달 » front에서 출력
  • 나는 외부 API를 적용하는 역할이 BackEnd의 역할이라고 생각해서 내가 다 했는데, FrontEnd에서 처리해야 할 게 많고 그래서 구글링하다보면 FrontEnd에서 직접하는 분도 많은 것 같다. 혼란스럽네😳