css 코드는 강의 코드를 확인 권장
npm install -g @vue/cli
vue create . //혹은 폴더명
첫 템플릿 (App.vue)
<template>
<div class="container my-5">
<h1 class="title text-center">Weather in</h1>
<form class="search-location">
<input type="text"
class="form-control text-muted form-rounded p-4 shadow-sm"
placeholder="What City?"
autocomplete="off">
</form>
<div class="card rounded my-3 shadow-lg back-card overflow-hidden">
<!-- Top of card starts here -->
<div class="card-top text-center" style="margin-bottom: 15rem">
<div class="city-name my-3">
<p>Abuja</p> //변수 넣을 자리
<span>...</span>
<p class="">NG</p> //변수 넣을 자리
</div>
</div>
<!-- Top of card ends here -->
<!-- card middle-body card-body used cos I want to just update the inner-->
<div class="card-body">
<div class="card-mid">
<div class="row">
<div class="col-12 text-center temp">
<span>20°C</span> //변수 넣을 자리
<p class="my-4">Clouds everyWhere</p>
</div>
<div class="row">
<div class="col d-flex justify-content-between px-5 mx-5">
<p>
<img src="" alt="">
18°C
</p>
<p>
<img src="" alt="">
25°C
</p>
</div>
</div>
</div>
</div>
<!-- card middle ends here -->
<div class="card-bottom px-5 py-4 row">
<div class="col text-center">
<p>20°C</p> //변수 넣을 자리
<span>fells like</span>
</div>
<div class="col text-center">
<p>55%</p> //변수 넣을 자리
<span>humidity</span>
</div>
</div>
<!-- card bottom ends here -->
</div>
</div>
</div>
</template>
<script>
export default {
name: "App"
}
</script>
<style scoped>
@import "./assets/custom.css";
</style>
참고 : https://hj-tilblog.tistory.com/m/87
참고 : https://v3.ko.vuejs.org/guide/conditional.html#v-if-%E1%84%83%E1%85%A2-v-show
참고 : https://v3.ko.vuejs.org/guide/events.html#%E1%84%87%E1%85%A9%E1%86%A8%E1%84%92%E1%85%A1%E1%86%B8-%E1%84%8B%E1%85%B5%E1%84%87%E1%85%A6%E1%86%AB%E1%84%90%E1%85%B3-%E1%84%92%E1%85%A2%E1%86%AB%E1%84%83%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5
참고 : https://sas-study.tistory.com/m/293
* 데이터 입력 시 {{}} 사용
v-bind: 해당 선언변수를 value 값으로 연결
v-on : 폼 입력 바인딩 방식 중 하나로 input 태그나textarea태그 등 사용, 양방향 데이터 바인딩으로 내용이 작성하면 해당 내용이 선언한 데이터 값에 바로 적용
v-model : dom 이벤트가 트리거될 때 js를 실행할 수 있는 이벤트 리스너
v-if : 조건에 따라 블록을 렌더링할 때 사용
▶ v-on과 v-model 차이점 : v-model은 영문 이외의 사용 시 양방향 바인딩이 한 박자 늦게 반응해 마지막 글자가 떨어져 나가는 현상 발생, 이럴 경우 v-on 사용 권장
▶ v-on:submit.prevent : 제출 이벤트가 페이지를 다시 로드되지 않게 함(검색 창에 국가 지역 입력 시(ex seoul) 그 값은 사라지게 함)
▶ this 키워드 사용 : this를 사용하지 않고 try 이후의 값들 작성 시 각각의 값들을 제대로 찾지 못함
(자바스크립트에서 this 사용 : 객체의 인스턴스를 가져올 때 사용. this를 내부에서 obj 변수처럼 활용)
Vue 인스턴스를 생성할 때 파라미터로 전달되는 익명객체는 일정한 가공을 거쳐서 인스턴스화되고 "this키워드를 통해서 직접적으로 data 영역에 있는 변수값에 접근할 수 있게 되었다는 것"
https://openweathermap.org/
▶ 로그인 후 사용 가능
▶ 로그인 후 상단에 API 누르면 자신의 API 발급되는데 이메일 인증 후 정상적으로 작동함
▶ 예시의 url : https://openweathermap.org/current
https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={api key}
▶ 데이터 확인 시 도시별 온도가 절대온도로 나옴
▶ 한국에서 주로 사용하는 섭씨로 사용하려면 -273.15 붙일 것
const response = fetch(baseURL);
const data = await response.json();
console.log(data);
▶ openWeather API에서 발급받은 key는 외부로 노출하면 안 됨
▶ .env 파일 생성
▶ 리액트와 다른 점 : VUEAPP + 해당 이름 붙일 것
(사용하는 건 process.env.VUE_APP_해당이름)
<template>
<div id="main" :class="isDay ? 'day' : 'night'">
<div class="container my-5" style="max-width: 400px; min-width: 360px">
<h1 class="title text-center">Weather in</h1>
<form class="search-location" v-on:submit.prevent="getWeather">
<input
type="text"
class="form-control text-muted form-rounded p-4 shadow-sm"
placeholder="What City?"
v-model="citySearch"
autocomplete="off"
/>
</form>
<p class="text-center my-3" v-if="cityFound">No city found</p>
<div
class="card rounded my-3 shadow-lg back-card overflow-hidden"
v-if="visible"
>
<!-- weather animation container -->
<div>
<div icon="sunny" v-if="clearSky" data-label="Sunny">
<span class="sun"></span>
</div>
<div icon="snowy" v-if="snowy" data-label="Chilly">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div icon="stormy" v-if="stormy" data-label="Soggy">
<span class="cloud"></span>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div icon="cloudy" v-if="cloudy" data-label="Perfect">
<span class="cloud"></span>
<span class="cloud"></span>
</div>
<div icon="nightmoon" v-if="clearNight" data-label="Cool!">
<span class="moon"></span>
<span class="meteor"></span>
</div>
</div>
<!-- Top of card starts here -->
<div class="card-top text-center" style="margin-bottom: 15rem">
<div class="city-name my-3">
<p>{{ weather.cityName }}</p>
<span>...</span>
<p class="">{{ weather.country }}</p>
</div>
</div>
<!-- top of card ends here -->
<!--card middle body, card body used cos I want to just update the innerHTML -->
<div class="card-body">
<!-- card middle starts here -->
<div class="card-mid">
<div class="row">
<div class="col-12 text-center temp">
<span>{{ weather.temperature }}°C</span>
<p class="my-4">{{ weather.description }}</p>
</div>
</div>
<div class="row">
<div class="col d-flex justify-content-between px-5 mx-5">
<p>
<!-- <img src="./assets/down.svg" alt="" /> -->
{{ weather.lowTemp }}°C
</p>
<p>
<!-- <img src="./assets/up.svg" alt="" /> -->
{{ weather.highTemp }}°C
</p>
</div>
</div>
</div>
<!-- card middle ends here -->
<!-- card bottom starts here -->
<div class="card-bottom px-5 py-4 row">
<div class="col text-center">
<p>{{ weather.feelsLike }}°C</p>
<span>Feels like</span>
</div>
<div class="col text-center">
<p>{{ weather.humidity }}%</p>
<span>humidity</span>
</div>
</div>
<!-- card bottom ends here -->
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
cityFound: false,
visible: false,
stormy: false,
cloudy: false,
clearSky: false,
clearNight: false,
snowy: false,
isDay: true,
citySearch: "",
weather: {
cityName: "Gwarinpa",
country: "NG",
temperature: 12,
description: "Clouds everywhere",
lowTemp: "19",
highTemp: "30",
feelsLike: "20",
humidity: "55",
},
};
},
methods: {
getWeather: async function() {
console.log(this.citySearch)
const key = process.env.VUE_APP_KEY
const baseURL = `https://api.openweathermap.org/data/2.5/weather?q=${this.citySearch}&appid=${key}`
//fetch weather
try {
const response = await fetch(baseURL);
const data = await response.json();
console.log(data);
this.citySearch = "";
this.weather.cityName = data.name;
this.weather.country = data.sys.country;
this.weather.temperature = Math.round(data.main.temp -273.15);
this.weather.description = data.weather[0].description;
this.weather.lowTemp = Math.round(data.main.temp_min-273.15);
this.weather.highTemp = Math.round(data.main.temp_max-273.15);
this.weather.feelsLike = Math.round(data.main.feels_like-273.15);
this.weather.humidity = Math.round(data.main.humidity);
const timeOfDay = data.weather[0].icon;
///check for time of day
if (timeOfDay.includes("n")) {
this.isDay = false;
} else {
this.isDay = true;
}
const mainWeather = data.weather[0].main;
//check weather animations
if (mainWeather.includes("Clouds")) {
this.stormy = false;
this.cloudy = true;
this.clearSky = false;
this.clearNight = false;
this.snowy = false;
}
if (mainWeather.includes("Clouds")) {
this.stormy = false;
this.cloudy = true;
this.clearSky = false;
this.clearNight = false;
this.snowy = false;
}
if (
mainWeather.includes("Thunderstorm") ||
mainWeather.includes("Rain")
) {
this.stormy = true;
this.cloudy = false;
this.clearSky = false;
this.clearNight = false;
this.snowy = false;
}
if (mainWeather.includes("Clear") && this.isDay) {
this.stormy = false;
this.cloudy = false;
this.clearSky = true;
this.clearNight = false;
this.snowy = false;
}
if (mainWeather.includes("Clouds") && !this.isDay) {
this.stormy = false;
this.cloudy = false;
this.clearSky = false;
this.clearNight = true;
this.snowy = false;
}
if (mainWeather.includes("Snow")) {
this.stormy = false;
this.cloudy = false;
this.clearSky = false;
this.clearNight = false;
this.snowy = true;
}
this.visible = true;
this.cityFound = false;
} catch (error) {
console.log(error);
this.cityFound = true;
this.visible = false;
}
},
},
};
</script>
<style scoped>
@import "./assets/animation.css";
@import "./assets/custom.css";
</style>
결과 (seoul 입력 시)