DevYoon
[Vue] TMDB API로 영화 관련 페이지를 만들어 보자 ⚒️ 본문
1️⃣ HOME : 영화 목록 조회 페이지
TMDB API에 popular로 요청 보내서 상위 40개의 영화를 보여준다.
<template>
<div class="home">
<div class='row'>
<movie-card v-for="movie in movies" :key="movie.id" :movie="movie"/>
</div>
</div>
</template>
<script>
import axios from 'axios'
import MovieCard from '../components/MovieCard.vue'
// @ is an alias to /src
const getWhat = 'popular'
const API_KEY = '#'
export default {
name: 'HomeView',
components: {
MovieCard
},
methods:{
getMovieData(){
axios.get(`https://api.themoviedb.org/3/movie/${getWhat}?api_key=${API_KEY}&language=ko-KR&page=2®ion=KR`)
.then(response => {
this.$store.state.movieList = response.data.results
})
}
},
mounted(){
this.getMovieData()
},
computed:{
movies(){
return this.$store.state.movieList
}
}
}
</script>
🎞️ Vue LifeCycle Hook mounted를 활용해 따로 어떤 버튼을 클릭하지 않아도 요청 후 영화 목록 데이터를 가져와 화면에 보여주었다.
🎞️ API에서 받아온 데이터 안에 id값이 있길래, for문의 key로 사용했다.
2️⃣ Random : 영화 추천 페이지
과제는 랜덤으로 영화를 하나 뽑아서 보여주는 거였는데, 좀 더 실존하는 서비스 느낌을 내보고 싶어서 문구를 추가해봤다.
오늘 뭐 보지? 버튼을 클릭하면 🔽 이렇게 변한다.
<template>
<div class='random'>
<h3>뭘 봐야 할지 모르겠다면, 저희가 대신 골라드릴게요!</h3>
<b-button variant="success" @click="randomMovie" class='pickButton'>오늘 뭐 보지?</b-button>
<div v-if="pick" class='pickText'>
<span>오늘은</span>
<span class='pickTitle'>{{pick.title}}</span>
<span>어때요?</span>
</div>
<div class='pickCard' v-if="pick">
<b-card
:title= "pick.title"
:img-src="Poster+pick.poster_path"
img-alt="Image"
img-top
tag="article"
style="max-width: 20rem;"
class="card">
<b-card-text class='card-text'>
{{pick.overview}}
</b-card-text>
</b-card>
</div>
</div>
</template>
<script>
import _ from 'lodash'
export default {
name:'randomView',
data(){
return{
pick:'',
Poster:"https://www.themoviedb.org/t/p/w600_and_h900_bestv2/"
}
},
methods:{
randomMovie(){
this.pick = _.sample(this.$store.state.movieList)
}
},
}
</script>
🎞️ 처음엔 Home에서처럼 요청을 보냈었는데, 생각해보니 store에 vuex-persistedstate를 사용했으니 굳이 API 요청을 보낼 필요가 없을 것 같아서 해당 부분 코드를 삭제했다. (local storage에 데이터가 남아 있으니)
🎞️ lodash의 sample로 영화 목록 중 하나를 랜덤으로 뽑았다.
🎞️ API 요청으로 받아온 데이터에는 poster_path 속성에 온전한 주소값이 들어가 있지 않다. (https://~~~.~~~ 이런 형식이 아님🥲) 그래서 data에 앞부분 주소를 추가했고, 화면에 보여줄 때는 둘을 합쳐주는 방식으로 작성했다.
3️⃣ WatchList : 영화 기록 페이지
💫 Form
<template>
<div class='WatchListForm'>
<h1><i>Movie Archive</i></h1>
<p>그동안 감상한 영화를 기록해보세요!</p>
<input type="text" v-model="watchedMovie" @keyup.enter="addWatched">
<b-button variant="success" @click="addWatched">add</b-button>
</div>
</template>
<script>
export default {
name:'WatchListForm',
data(){
return{
watchedMovie:'',
}
},
methods:{
addWatched(){
const watchedItem = {
title:this.watchedMovie,
date:new Date().getTime()
}
if(this.watchedMovie){
this.$store.dispatch('addWatched', watchedItem)
this.watchedMovie = ''
}
}
}
}
</script>
💫 Item
<template>
<div>
<div v-for="item in items" :key="item.date" class='item'>
<p>{{item.title}}</p>
</div>
</div>
</template>
<script>
export default {
name:'WatchListItem',
computed:{
items(){
return this.$store.state.userWatch
}
}
}
</script>
🎞️ Form에서 작성한 데이터를 store에 등록하고, Item에서는 store에 있는 데이터를 하나씩 보여준다.
🎞️ for문을 돌려줘야 하기 때문에 사용자가 입력한 값과 함께 작성 시간도 넣어 key로 사용했다.
4️⃣ Store
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex)
export default new Vuex.Store({
plugins:[createPersistedState()],
state: {
movieList:'',
userWatch:[]
},
getters: {
},
mutations: {
ADD_WATCHED(state, movieItem){
state.userWatch.push(movieItem)
}
},
actions: {
addWatched({commit}, movieItem){
commit('ADD_WATCHED', movieItem)
}
},
modules: {
}
})
🎞️ API에 요청해 받아 올 영화 목록과 사용자가 입력할 영화 목록을 분리해주었다.