vue 날씨 애플리케이션

개발공부·2022년 10월 17일
0

VUE 공부하기

목록 보기
1/5

css 코드는 강의 코드를 확인 권장

vue 첫 설치

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&deg;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&deg;C
          </p>
          <p>
            <img src="" alt="">
            25&deg;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&deg;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 영역에 있는 변수값에 접근할 수 있게 되었다는 것"

* openWeather API

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);

* env

▶ openWeather API에서 발급받은 key는 외부로 노출하면 안 됨
▶ .env 파일 생성
▶ 리액트와 다른 점 : VUEAPP + 해당 이름 붙일 것
(사용하는 건 process.env.VUE_
APP_해당이름)

[App.vue]

<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 }}&deg;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 }}&deg;C
                </p>
                <p>
                  <!-- <img src="./assets/up.svg" alt="" /> -->
                  {{ weather.highTemp }}&deg;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 }}&deg;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 입력 시)

profile
개발 블로그, 티스토리(https://ba-gotocode131.tistory.com/)로 갈아탐

0개의 댓글