Vue + Nuxt 날씨 서비스 만들기 – 4.View 페이지 세팅
페이지의 이동을 담당하는 Router.
데이터를 저장해 놓고 사용하는 Store.
모두 세팅을 마쳤습니다.
이제 View 페이지에 데이터를 가져오겠습니다.
pages/index.vue
<template>
<div id="main" class="main">
<section class="box">
<h2 class="title">
Today’s Weather
</h2>
<p class="desc">
날씨 서비스 토이프로젝트입니다.<br>
해당 지역을 선택하여 날씨 정보를 확인할 수 있습니다.
</p>
<ul class="skills">
<li>Prototype : Figma</li>
<li>FrontEnd : VueJS</li>
<li>ETC : Vuex, NuxtJS</li>
<li>CSS : SCSS</li>
<li>API : OpenWeather</li>
</ul>
<TheLocations />
</section>
</div>
</template>
<script>
// import { mapMutations } from 'vuex'
import TheLocations from "../components/TheLocations";
export default {
components: {
TheLocations
}
}
</script>
index 페이지에는 프로젝트를 소개하는 내용이 있고,
전체적인 Locations 컴포넌트에서 진행합니다.
components/TheLocations.vue
<template>
<div class="locations">
<button type="button" class="btn-location"
:class="{ active : this.$route.params.id == 'MyLocation' }"
@click="goToLocation('MyLocation')"
>
내 위치
</button>
<button type="button" class="btn-location"
v-for="item in locations"
:class="{ active : local == item.nameeng }"
@click="goToLocation(item.nameeng)"
>
{{ item.namekor }}
</button>
</div>
</template>
<script>
export default {
props : {
local : String
},
computed : {
locations() {
return this.$store.state.locations;
}
},
methods : {
goToLocation(local) {
this.$router.push('/location/' + local);
}
}
}
</script>
Store에 등록되어 있는 지역 리스트를 뿌려주는 컴포넌트입니다.
지역명을 클릭하면 라우터가 변경되고, 해당되는 곳의 날씨를 가져옵니다.
pages/location/_id.vue
<template>
<div id="sub" class="sub" v-if="currentLocation && weatherData && air.data">
<section class="box">
<article class="current">
<div class="row">
<div class="col">
<div class="current__temp">
{{ parseInt(weatherData.current.temp - 273.15) }}°
</div>
<div class="current__main">
{{ weatherData.current.weather[0].main }}
</div>
</div>
<div class="col">
<div class="current__icon">
<img :src="require(`~/assets/images/${weatherData.current.weather[0].icon}@2x.png`)">
</div>
</div>
</div>
</article>
<article class="info">
<h2 class="info__name">
Howdy! {{ currentLocation.nameeng }}
</h2>
<div class="info__location">
<TheLocations v-bind:local="currentLocation.nameeng"></TheLocations>
</div>
</article>
</section>
<section class="box">
<h2 class="title">
Today
<small>
{{ translateTimeStamp(weatherData.current.dt, 'date') }}
</small>
</h2>
<ul class="list">
<li v-for="n in 4">
<div class="list__icon">
<img :src="require(`~/assets/images/${weatherData.hourly[n - 1].weather[0].icon}@2x.png`)">
</div>
<div class="list__title">
{{ parseInt(weatherData.hourly[n * 3].temp - 273.15) }}°
</div>
<div class="list__subtitle">
{{ translateTimeStamp(weatherData.hourly[n * 3].dt) }}
</div>
</li>
</ul>
<article class="etc">
<div class="col">
<strong>
{{ weatherData.current.humidity }}%
</strong>
Humidity<br>
(습도)
</div>
<div class="col">
<strong>
{{ parseInt(weatherData.current.feels_like - 273.15) }}°
</strong>
Feels Like<br>
(체감 온도)
</div>
<div class="col">
<strong>
{{ weatherData.current.wind_speed }}
</strong>
Wind<br>
(풍속)
</div>
</article>
</section>
<section class="box">
<h2 class="title">
Air Polutions
</h2>
<ul class="list list--align">
<li>
<div class="list__air">
미세먼지
</div>
<div class="list__title" v-bind:style="{ color : translateFineDust(air.data.list[0].components.pm10).color }">
{{ translateFineDust(air.data.list[0].components.pm10).text }}
</div>
<div class="list__subtitle">
{{ air.data.list[0].components.pm10 }} ㎍/㎥
</div>
</li>
<li>
<div class="list__air">
초미세먼지
</div>
<div class="list__title" v-bind:style="{ color : translateUltraFineDust(air.data.list[0].components.pm2_5).color }">
{{ translateUltraFineDust(air.data.list[0].components.pm2_5).text }}
</div>
<div class="list__subtitle">
{{ air.data.list[0].components.pm2_5 }} ㎍/㎥
</div>
</li>
</ul>
</section>
<section class="box">
<h2 class="title">
8-day Forecast
</h2>
<ul class="item">
<li v-for="item in weatherData.daily">
<span class="item__date">
{{ translateTimeStamp(item.dt, 'date') }}
</span>
<span class="item__box">
<span>
<img :src="require(`~/assets/images/${item.weather[0].icon}@2x.png`)">
</span>
<span>
오전 <strong>{{ parseInt(item.temp.morn - 273.15) }}°</strong> /
오후 <strong>{{ parseInt(item.temp.eve - 273.15) }}°</strong>
</span>
</span>
</li>
</ul>
</section>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
export default {
data() {
return {
weatherData : '',
air : {
data : '',
pm10 : '',
pm2_5 : ''
}
}
},
computed : {
...mapGetters({
currentLocation : 'getCurrentLocation'
})
},
methods : {
...mapActions({
getWeather: 'fetchWeatherData'
})
},
head() {
return {
title: 'Today’s Weather - ' + this.$route.params.id,
meta: [
{
hid: 'description',
name: 'description',
content: 'Today’s Weather - ' + this.$route.params.id,
}
]
}
},
beforeMount() {
if (this.$route.params.id == 'MyLocation') {
this.getLocation();
} else {
let current = this.$store.state.locations.find(n => {
return n.nameeng == this.$route.params.id
});
this.$store.commit('setCurrentLocation', current)
}
},
mounted() {
this.$nextTick(function () {
this.$store.dispatch('fetchWeatherData', {
lat : this.currentLocation.lat,
lon : this.currentLocation.lon
}).then(response => {
this.weatherData = response
}, error => {
console.log(error);
})
this.$store.dispatch('fetchAirData', {
lat : this.currentLocation.lat,
lon : this.currentLocation.lon
}).then(response => {
this.air.data = response;
}, error => {
console.log(error);
})
})
},
}
</script>
실제 날씨 정보를 뿌려주는 컴포넌트입니다.
html 부분은 데이터를 가져와 출력시켜주는 곳입니다.
소스를 나눠서 살펴보겠습니다.
data() {
return {
weatherData : '',
air : {
data : '',
pm10 : '',
pm2_5 : ''
}
}
},
computed : {
...mapGetters({
currentLocation : 'getCurrentLocation'
})
},
사용할 데이터들을 선언해 줍니다.
날씨 데이터와 미세먼지 데이터를 사용합니다.
computed로 선언 된 currentLocation는 현재 지역을 나타냅니다.
beforeMount() {
if (this.$route.params.id == 'MyLocation') {
this.getLocation();
} else {
let current = this.$store.state.locations.find(n => {
return n.nameeng == this.$route.params.id
});
this.$store.commit('setCurrentLocation', current)
}
},
mounted() {
this.$nextTick(function () {
this.$store.dispatch('fetchWeatherData', {
lat : this.currentLocation.lat,
lon : this.currentLocation.lon
}).then(response => {
this.weatherData = response
}, error => {
console.log(error);
})
this.$store.dispatch('fetchAirData', {
lat : this.currentLocation.lat,
lon : this.currentLocation.lon
}).then(response => {
this.air.data = response;
}, error => {
console.log(error);
})
})
},
내 위치 기준 날씨를 가져올 경우 getLocation 함수를 실행하여 위치를 가져옵니다.
mount가 되었을 때 fetchWeatherData, fetchAirData 함수를 실행하여 날씨와 미세먼지 데이터를 가져옵니다.
실제 완성된 화면은 아래와 같습니다.