개발/Youtube크롤링(crawling)

[VUE.js] 유튜브 크롤링

황성안 2021. 8. 17. 00:49
728x90
  1. 사용자 요청
  2. 비어있는 HTML 페이지 보내기
  3. HTML에 담긴 JS코드를 다운
  4. 다운로드되면 실행
  5. JS코드가 비워저있는 곳을 HTML에 담는다.

SPA 이슈들

JS 코드가 많을수록 사용자 대기(처음)시간이 길어진다.

왜써?

SPA는 멋지고, 프레임워크를 쓰면 개발을 편하게 할수있기때문입니다.

모듈화

JS파일이 100개면 다운로드 요청이 100개가된다.. 오래걸린다..

Webpack이 해주는 것들

  1. 번들링
  2. ES6 문법을 ES5로 변환(w/Babel)
  3. 사용하지 않는 코드 제거 (tree-shaking)(중요)
    • JS 용량이 줄어들어 사용자는 더 빨리 화면을 볼수있다.
  4. 공백 제거, 코드 축약 등 결과물 크기 쿠기 축소

Vue -cli 개발순서

  1. 화면(UI)을 계발한다. > prototyping tool? kakao oven, adobe xd, figma
  2. 화면에 맞는 컴포넌트 구조를 설계한다.
  3. 공식 스타일 가이드를 따르며 구현한다.

Youtube Clone

vue 파일 만들어주기

vue create 0511

axios 설치

npm install axios

서버 켜기

npm run serve

필요 없는거 지우기

App.vue 안의 헬로 관련 지우고, component 에서 hello 지우기

화면 설계

component(4개)

1. 검색창

  1. SearchBar.vue
    • 유튜브 API 를 가져올 것이다.
      • AIzaSyAso2hTzkzplrQFH3CzG4rb95NlhLBsCsY

2. 비디오 보여주는 곳

  1. VideoList.vue
    1. VideoListItem.vue

3. 영상 재생하는 곳

  1. VideoListItemDetail.vue

component 안에 파일만들기

현재 component 안에 4개의 파일이있다.

App.vue의

첫번째 자식 Searchbar.vue,

두번째 자식 VideoList.vue,

​ - VideoList.vue 의 첫번째 자식 VideoListItem.vue

세번째 자식 VideoListItemDetail.vue

Keyword 입력을하면 app.vue 에서 받고 API와 연동하여 props 를 통해 videoList로 보내준다.

VideoList에서 props를 통해 VideoListItem.vue 로 이동

VideoListItem.vue 를 클릭했을때 실제 video 보여주는데 신호를 emit으로

전달해준다. videoList > app.vue

app.vue 에서 props 로 VideoListItemDetail.vue로 출력

UI 만들기

공식문서의 스타일을 지키도록 노력

https://kr.vuejs.org/v2/style-guide/

App.vue

서치바, 비디오리스트 임포트해주기

서치바, 비디오리스트 컴포넌트해주고

서치바, 비디오리스트 div 안 추가

data 받아야하니까 스크립트에 추가

배열받아와야하니 [], onKeywordEnter(videoList)에서 받아오는거

<template>
  <div id="app">
    <!-- 자식한테 받을거 -->
    <SearchBar
      @on-keyword-enter="onKeywordEnter"
    />
    <VideoList 
    :videoList="videoList"
    />
  </div>
</template>

<script>
import SearchBar from '@/components/SearchBar'
import VideoList from '@/components/VideoList'


export default {
  name: 'App',
  components: {
    SearchBar,
    VideoList,
  },
  data() {
    return {
      videoList: [],
    }
  },
  methods: {
    onKeywordEnter(videoList) {
      //console.log('부모에서찍음', videoList)
      this.videoList = videoList
      console.log(videoList)
    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

SearchBar.vue

axios 불러올떄

github.com/axios/axios/ 로 이동하여 확인해보자.

google에 Youtube API 라고 처보자. (https://developers.google.com/youtube/v3/docs?hl=ko)

우리가 검색할수있는 search api 로이동 (https://developers.google.com/youtube/v3/docs/search?hl=ko)

list 로 이동하면 HTTP 요청란이있다. (https://developers.google.com/youtube/v3/docs/search/list?hl=ko)

"GET https://www.googleapis.com/youtube/v3/search"

우리는 part(snippet), q(검색어), type(video) 를보면된다.

queryString => "?part=snippet&" 이런식으로 사용하면된다.

axious.get(url[,config])

thencatch 대신 어씽크 사용

<template>
  <div>
      <input type="text" @keyup.enter="onKeywordEnter">
  </div>
</template>

<script>
// axios 패키지에서 axios 를 꺼낸다.
// 사용안하면 에러뜹니다.
import axios from 'axios'


const YOUTUBE_API_KEY = 'AIzaSyAso2hTzkzplrQFH3CzG4rb95NlhLBsCsY'
const YOUTUBE_API_URL = 'https://www.googleapis.com/youtube/v3/search'


export default {
    name: 'SearchBar',
    methods:{
        async onKeywordEnter(event) {
            const keyword = event.target.value
            const config = {
                params: {
                    part: 'snippet',
                    type: 'video',
                    q: keyword,
                    key: YOUTUBE_API_KEY,
                },
            }
            //console.log(keyword)

            //axios.get(YOUTUBE_API_URL, config)

            const response = await axios.get(YOUTUBE_API_URL, config)
            //console.log(response)
            const videoList = response.data.items
            // 부모한테 올리기 케밥케이스 비디오리스트를 app.vue methods 에넘긴다.
            this.$emit('on-keyword-enter', videoList)
        },
    },
}
</script>

<style>

</style>

VideoList.vue

props:에서 넘어오는건 Array 배열이다.

props에서 데이터 받아왔으니까 그냥 div 로 올려주자

<template>
  <div v-if="videoList.length">
      <VideoListItem
        v-for="video in videoList"
        :video="video"
        :key="video.id.videoId"
      />
  </div>
</template>

<script>
import VideoListItem from '@/components/VideoListItem'


export default {
    name: 'VideoList',
    components:{
        VideoListItem,
    },
    props: {
        videoList: Array,
    },
}
</script>

<style>

</style>

VideoListItem.vue

VideoList에 props 해주자.

여기서 썸네일과 타이틀만 뽑아보자!

<template>
  <div>
      <img :src="thumbUrl" alt="">
      <h3>{{ videoTitle | unescape }}</h3>
      <p>{{ videoDesc }}</p>

  </div>
</template>

<script>
import _ from 'lodash'

export default {
    nanme: 'VideoListItem',
    props: {
        video: Object,
    },
    computed: {
        videoTitle() {
            return this.video.snippet.title
        },
        videoDesc() {
            return this.video.snippet.description
        },
        thumbUrl() {
            return this.video.snippet.thumbnails.medium.url
        },
    },
    filters: {
        unescape(rawText) {
            return _.unescape(rawText)
        }
    },
}
</script>

<style>

</style>

entity 해결방법

npm i lodash
//unescape 를 추가시켜서 ' 표시해주기
 <h3>{{ videoTitle | unescape }}</h3>
//임포트 시켜줄거
import _ from 'lodash'
run 

etag : 특정 지문같은 것 id 대신 etag 를 써도된다.

snippet : 우리가 필요한 정보들이있음 title , descpition, thumbnail

video. snippet.title

html 의 ' ("&#39 ;") 이녀석이

클릭했을때 비디오로가기

VideoListItem.vue

div, style 에 추가

hober > 손가락으로 표시해주는 걸로 변경

background 바꿔주기

onSelectVideo 를 하면 videolist > app > detail 로 가게끔 하기

onSelectVideo는 props 된다. videoList 에가서 받아주자.

<template>
  <div @click="onSelectVideo" class="video-list-item" >
      <img :src="thumbUrl" alt="">
      <h3>{{ videoTitle | unescape }}</h3>
      <p>{{ videoDesc }}</p>

  </div>
</template>

<script>
import _ from 'lodash'

export default {
    nanme: 'VideoListItem',
    props: {
        video: Object,
    },
    computed: {
        videoTitle() {
            return this.video.snippet.title
        },
        videoDesc() {
            return this.video.snippet.description
        },
        thumbUrl() {
            return this.video.snippet.thumbnails.medium.url
        },
    },
    filters: {
        unescape(rawText) {
            return _.unescape(rawText)
        }
    },
}
</script>

<style>
.video-list-item:hover {
    cursor: pointer;
    background-color: #eee;
}
</style>

videoList.vue

리스트 아이템에서 받아오게하기

methods props 만들어주기 app 으로보낸다.

<template>
  <div v-if="videoList.length">
      <VideoListItem
        v-for="video in videoList"
        :video="video"
        :key="video.id.videoId"
        @on-select-video="onSelectVideo"
      />
  </div>
</template>

<script>
import VideoListItem from '@/components/VideoListItem'


export default {
    name: 'VideoList',
    components:{
        VideoListItem,
    },
    props: {
        videoList: Array,
    },
    methods: {
        onSelectVideo(video) {
            this.$emit('on-select-video', video)
        },
    },
}
</script>

<style>

</style>

app.vue

여기선 onSelectVideo를 올려받은다음 detail로 내려보내줘야합니다

methods 로 onSelectVideo 만들어주겠습니다.

<template>
  <div id="app">
    <!-- 자식한테 받을거 -->
    <SearchBar
      @on-keyword-enter="onKeywordEnter"
    />
    <VideoList 
    :videoList="videoList"
    @on-select-video="onSelectVideo"
    />
  </div>
</template>

<script>
import SearchBar from '@/components/SearchBar'
import VideoList from '@/components/VideoList'


export default {
  name: 'App',
  components: {
    SearchBar,
    VideoList,
  },
  data() {
    return {
      videoList: [],
      video: {},
    }
  },
  methods: {
    onKeywordEnter(videoList) {
      //console.log('부모에서찍음', videoList)
      this.videoList = videoList
      console.log(videoList)
    },
    onSelectVideo(video) {
      this.video = video

    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

VideoListItemDetail.vue

app의 video를 받아와야합니당

또 app 에서 디테일 추가해주세요 import, components, div 에서 보여주기

<template>
  <div id="app">
    <!-- 자식한테 받을거 -->
    <SearchBar
      @on-keyword-enter="onKeywordEnter"
    />
    <VideoListItemDetail
      :video="video"
    />
    <VideoList 
    :videoList="videoList"
    @on-select-video="onSelectVideo"
    />
  </div>
</template>

<script>
import SearchBar from '@/components/SearchBar'
import VideoList from '@/components/VideoList'
import VideoListItemDetail from '@/components/VideoListItemDetail'


export default {
  name: 'App',
  components: {
    SearchBar,
    VideoList,
    VideoListItemDetail,
  },
  data() {
    return {
      videoList: [],
      video: {},
    }
  },
  methods: {
    onKeywordEnter(videoList) {
      //console.log('부모에서찍음', videoList)
      this.videoList = videoList
      console.log(videoList)
    },
    onSelectVideo(video) {
      this.video = video

    },
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

 

당신이 화면을 보여주고싶을떄

구글에 youtube iframe api 검색

https://developers.google.com/youtube/iframe_api_reference?hl=ko

https://developers.google.com/youtube/player_parameters?hl=ko

URL 끝부분에 플레이어 매개변수를 직접 추가하면 됩니다.

<iframe id="ytplayer" type="text/html" width="640" height="360"
src="https://www.youtube.com/embed/M7lc1UVf-VE?autoplay=1&origin=http://example.com"
frameborder="0"></iframe>

src에 바인딩해주자

detail

https://www.youtube.com/embed/ 이주소를 computed 로 뺴준다.

<div v-if="video">

를 사용하려면 무조건 null 로만해야한다 {} 사용시 안뜸

비디오 비율은 직접 줘서 맞출수있다.

<iframe 여기다 크기 넣기:src="videoUrl" frameborder="0"></iframe>
<template>
  <div v-if="video">
      <iframe :src="videoUrl" frameborder="0"></iframe>
  </div>
</template>

<script>
export default {
    name: 'VideoListItemDetail',
    props: {
        video: Object,
    },
    computed: {
        videoUrl() {
            const videoId = this.video.id.videoId
            return `https://www.youtube.com/embed/${videoId}`
        },
    },
}
</script>

<style>

</style>

디자인하기

조회한 영상보기

width:

margin: 0 auto; 가운데 정렬

app.vue

<template>
  <div id="app">
    <!-- 자식한테 받을거 -->
    <header>
    <SearchBar
      @on-keyword-enter="onKeywordEnter"
    />
    </header>
    <section>
      <VideoListItemDetail
        :video="video"
      />
      <VideoList 
        :videoList="videoList"
        @on-select-video="onSelectVideo"
      />
    </section>

  </div>
</template>

<script>
import SearchBar from '@/components/SearchBar'
import VideoList from '@/components/VideoList'
import VideoListItemDetail from '@/components/VideoListItemDetail'


export default {
  name: 'App',
  components: {
    SearchBar,
    VideoList,
    VideoListItemDetail,
  },
  data() {
    return {
      videoList: [],
      video: {},
    }
  },
  methods: {
    onKeywordEnter(videoList) {
      //console.log('부모에서찍음', videoList)
      this.videoList = videoList
      console.log(videoList)
    },
    onSelectVideo(video) {
      this.video = video

    },
  },
}
</script>

<style>
section, header {
  width: 80%;
  margin: 0 auto;
  padding: 1rem 0;
}
section {
  display: flex;
}
</style>

서치바

div에 class 스로 search-bar 주기

.search-bar > input

서치바안에있는 인풋테그 크기키워주기

양옆으로 꽉차게 width: 100%

<template>  <div class="search-bar">      <input type="text" @keyup.enter="onKeywordEnter">  </div></template><script>// axios 패키지에서 axios 를 꺼낸다.// 사용안하면 에러뜹니다.import axios from 'axios'const YOUTUBE_API_KEY = 'AIzaSyAso2hTzkzplrQFH3CzG4rb95NlhLBsCsY'const YOUTUBE_API_URL = 'https://www.googleapis.com/youtube/v3/search'export default {    name: 'SearchBar',    methods:{        async onKeywordEnter(event) {            const keyword = event.target.value            const config = {                params: {                    part: 'snippet',                    type: 'video',                    q: keyword,                    key: YOUTUBE_API_KEY,                },            }            //console.log(keyword)            //axios.get(YOUTUBE_API_URL, config)            const response = await axios.get(YOUTUBE_API_URL, config)            //console.log(response)            const videoList = response.data.items            // 부모한테 올리기 케밥케이스 비디오리스트를 app.vue methods 에넘긴다.            this.$emit('on-keyword-enter', videoList)        },    },}</script><style>.search-bar > input {    width: 100%;    padding: 0.5rem;    font-size: 2rem;}</style>

 

비디오 디테일

div를 div로 감싸서 컨테이늘 줘보자

<template>  <div class="video-detail" v-if="video">      <div class="video-container">        <iframe :src="videoUrl" frameborder="0"></iframe>      </div>  </div></template><script>export default {    name: 'VideoListItemDetail',    props: {        video: Object,    },    computed: {        videoUrl() {            const videoId = this.video.id.videoId            return `https://www.youtube.com/embed/${videoId}`        },    },}</script><style>.video-detail {    width: 70%;    padding: 1rem;}.video-container {    position: relative;    padding-top: 56.25%}.video-container > iframe {    position: absolute;    top: 0;    left: 0;    width: 100%;    height: 100%;}</style>

API 키 숨기기

구글에 vue environment variables 를 검색

https://cli.vuejs.org/guide/mode-and-env.html

에서 Environment Variables 를 참고하면된다.

.env                # loaded in all cases.env.local          # loaded in all cases, ignored by git.env.[mode]         # only loaded in specified mode.env.[mode].local   # only loaded in specified mode, ignored by git

mode 는 개발환경인지 배포 환경인지 확인하고 우리는 local 로 사용한다

그리고 그 안에다가 이런식으로 설정

FOO=barVUE_APP_NOT_SECRET_CODE=some_value

주의 : 중요한 비밀스러운 app은 넣지마세용 실제로 숨길려면 백엔드에서 숨겨야합니다.

.env.local 의 파일을 만들어줍니다. 거기에 아래 코드를 입력해줍니다.

VUE_APP_NOT_SECRET_CODE=AIzaSyAso2hTzkzplrQFH3CzG4rb95NlhLBsCsY

그리고 다시 서치바로이동하여

const YOUTUBE_API_KEY = process.env.VUE_APP_YOUTUBE_API_KEY

배포하기

netlify 검색

https://www.netlify.com/

배포할때에는 폴더를 따로뺴야합니다.

node_modules 만 뺴고 밖으로 폴더를 하나만들어서 빼냅니다.

github 로그인해주세요

git new 새로 레파지토리만들고

git initgit remote add origin https://github.com/hsa8275/youtube-clone.gitgit add .git commit -m "dasd"git push

git 올려주고

https://app.netlify.com/teams/hsa8275/overview

에서 New file 로해주고 git 과 연동시켜줍니다.

show advanced 클릭

New variable 클릭해주고

key 에는 VUE_APP_YOUTUBE_API_KEYvalue에는 (내 key)AIzaSyAso2hTzkzplrQFH3CzG4rb95NlhLBsCsY

수정사항이 발생하면

app.vue 에서 status를 입력하면 자동으로 업로드시키는 녀석이 감지한다add . git commit -m "update 0.8275"

그리고 만약 뭔가 더 하고싶다면 node_module을 깔아야한다.

npm install

@@@@도메인 숨기자

사실상 프론트엔드에서 숨기는건 불가

아래를따라해보자

https://console.cloud.google.com/home/dashboard?project=pjt07-309511&hl=ko

사용자 인증정보 > 연필모양 > 애플리케이션 제한사항 > HTTP 리퍼러 선택 후 웹사이트 제한사항에

https://elegant-cray-27596f.netlify.app/ 넣고 저장

728x90