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 함수를 실행하여 날씨와 미세먼지 데이터를 가져옵니다.
실제 완성된 화면은 아래와 같습니다.