원형 카드 배너 만들기 - API 요청 후 브라우저에 동적으로 렌더링 하기

ChoiYongHyeun·2024년 1월 20일
0

망가뜨린 장난감들

목록 보기
10/19
post-thumbnail

토이프로젝트 기간 24.01.17 ~ 24.01.20
완성한 페이지
전체 코드


전일에 이렇게 요청을 보내와서 사용 할 수 있게 파싱해왔으니

이제 파싱해온 데이터를 가지고 동적으로 컴포넌트들을 구성하자

WeatherRender 생성자 만들기

class WeatherRender extends LocateFetch {
  constructor(api) {
    super(api);
    this.setParams();
    this.init();
    this.cards = document.querySelectorAll('.card');
    this.currentDate = new Date();
    this.weatherData = undefined;
    this.monthMap = {
      0: 'january',
      1: 'february',
      2: 'march',
      3: 'april',
      4: 'may',
      5: 'june',
      6: 'july',
      7: 'august',
      8: 'september',
      9: 'october',
      10: 'november',
      11: 'december',
    };
    this.dayMap = {
      0: 'Monday',
      1: 'Tuesday',
      2: 'Wednesday',
      3: 'Thursday',
      4: 'Friday',
      5: 'Saturday',
      6: 'Sunday',
    };
    this.PTYmap = {
      0: ['sunny', 'images/sunny.png'], // 맑음
      1: ['rainy', 'images/rainy.png'], // 비
      2: ['snow', 'images/snow.png'], // 비/눈
      3: ['snow', 'images/snow.png'], // 눈
      4: ['shower', 'images/rain-thunder.png'], // 소나기
      5: ['windy', 'images/windy.png'], // 바람 많음
    };
    this.SKYmap = {
      0: ['sunny', '/images/sunny.png'],
      3: ['cloudy', '/images/cloudy.png'],
      4: ['windy', '/images/windy.png'],
    };
    this.hours = Array.from({ length: 25 }, (_, i) => {
      const paddedHour = `0${i}`.slice(-2);
      return `${paddedHour}00`; // [0100 , 0200 ... 2400]
    });
  }

API 를 활용해 자료를 가져온 후 렌더링 해야 하기에

이전에 정보를 가져오는 WeatherFetch 를 상속받은 WeatherRender 생성자를 만들어주었다.

부모 생성자인 WeatherRender 의 파라미터를 setParamse() , init() 메소드를 실행시켜주고

파싱해온 자료들을 렌더링 하기 위해 객체들을 준비해주었다.

API 요청하고 적절한 이미지 배너 준비하기

...
  async getWeathers() {
    // GET 요청을 보내지 않았을 때는 GET 요청을 받아오고
    // 받아온적 있을 경우엔 이미 받아온 것을 return
    if (!this.weatherData) {
      this.weatherData = await this.parseResponse();
      return this.weatherData;
    }
    return this.weatherData;
  }
...

getWeathers 메소드는 parseResponse 이 한 번도 실행되지 않았다면 파싱해온 후 프로퍼티로 저장 후 반환하며

한 번이라도 실행되어 저장되어 있다면, 저장된 파싱 정보들을 반환하도록 하였다.

나름 싱글톤 패턴을 흘깃 하고 보긴했어서 비슷한 원리로 따라해봤다.
맞는지는 모르겠다. 지피티한테 이 악물고 "그래 나도 싱글톤 아닌건 알아, 그래도 비슷한 원리지 ?!" 이러고 물어봐도 계속 부정한다.

  choiceSrc(ptyCode, skyCode) {
    if (!ptyCode) {
      return this.SKYMAP[skyCode];
    }
    return this.PTYmap[ptyCode];
  }

파싱해온 정보들 중 PTY , SKY 라는 값들이 존재한다.

해당 값들은 PTY : 비가 온다면 어떤 상태, SKY : 하늘 상태 를 의미한다.

  this.PTYmap = {
      0: ['sunny', 'images/sunny.png'], // 맑음
      1: ['rainy', 'images/rainy.png'], // 비
      2: ['snow', 'images/snow.png'], // 비/눈
      3: ['snow', 'images/snow.png'], // 눈
      4: ['shower', 'images/rain-thunder.png'], // 소나기
      5: ['windy', 'images/windy.png'], // 바람 많음
    };
    this.SKYmap = {
      0: ['sunny', '/images/sunny.png'],
      3: ['cloudy', '/images/cloudy.png'],
      4: ['windy', '/images/windy.png'],
    };

그렇기에 비가 오지 않는다면 SKYmap 에서 제목과 이미지 주소를 가져오고

비가 온다면 (혹은 눈) PTYmap 에서 제목과 이미지 주소를 가져오도록 하였다.

...
  getDate() {
    const { monthMap, dayMap } = this;

    let hour = this.currentDate.getHours();
    const index = `0${hour}`.slice(-2) + '00';

    const meridie = hour >= 12 ? 'PM' : 'AM';
    hour = hour >= 12 ? hour - 12 : hour;
    const day = dayMap[this.currentDate.getDay()];
    const date = this.currentDate.getDate();
    const month = monthMap[this.currentDate.getMonth()];

    return [hour, index, meridie, day, date, month];
  }
...

카드배너에서 날짜와 시간등을 가져오기 위한 메소드를 만들어주었다.

여기서 index 에 해당하는 것은 파싱해온 데이터의 생김새가

다음처럼 생겼기 때문에 시간을 시간00 형태로 변경해주었다.

파싱해온 정보를 이용해 동적으로 렌더링 하기

...
async renderMain(address) {
    const [hour, index, meridie, day, date, month] = this.getDate();

    const targetWeather = await this.getWeathers();
    const { POP, PTY, REH, SKY, TMP, WSD } = targetWeather[address][index];

    const [weatherState, src] = this.choiceSrc(PTY, SKY);

    return `<div class="weather-wrapper">
    <div class="main-weather">
      <div class="header-weather">🌍${address}</div>
      <div class="body-weather">
        <img class="weather-img" src=${src} />
        <div class="weather-text">
          <div class="temperature-text">${TMP}℃</div>
          <div class="state-text">${weatherState}</div>
          <div class="date-text">
            <span class="hour">${hour}</span>
            <span span="meridie">${meridie}</span>,${day},${date}, ${month}
          </div>
        </div>
        <hr class = 'cross-line' / >
        <div class="weather-sub">
          <div class="sub-wrapper">
            <div class="weather-icon">💨</div>
            <div class="datail-number">${WSD} km/h</div>
            <div class="datail-text">wind</div>
          </div>
          <div class="sub-wrapper">
            <div class="weather-icon">💧</div>
            <div class="datail-number">${REH}%</div>
            <div class="datail-text">Humidity</div>
          </div>
          <div class="sub-wrapper">
            <div class="weather-icon">☂️</div>
            <div class="datail-number">${POP}%</div>
            <div class="datail-text">chance of <br />rain</div>
          </div>
        </div>
      </div>
      <div class="tail-weather"></div>
    </div>`;
  }
...

이전에 디자인 해놨던 컴포넌트를 반환값으로 가지고 온후

메인 페이지에 들어갈 파라미터들을 넣어주었다.

async renderMain(address) {
    const [hour, index, meridie, day, date, month] = this.getDate();

    const targetWeather = await this.getWeathers();
    const { POP, PTY, REH, SKY, TMP, WSD } = targetWeather[address][index];
  ...

renderMain 함수는 지역 별 address 를 인수로 받아

해당 address 의 현재 시간에 따른 정보를 가져올 것이기 때문에 address 로 지역을 특정하고 index 로 시간을 특정하였다.

이후 반환되는 값들은 이전에 컴포넌트화해놨던 태그들을 담아주었다.

...
  async renderAll() {
    const { locateArr } = this;
    const weatherData = await this.getWeathers();

    locateArr.forEach((locate, index) => {
      const { address } = locate;

      const card = this.cards[index];
      this.renderMain(address).then((res) => {
        card.innerHTML = res;
      });
    });
  }
}
...

이후 renderAll 에서는 locateArr 에 들어있는 지역을 순서대로 하여 메인 페이지를 렌더링 해주었다.

정보를 제공하는 홈페이지의 서버 환경에 따라 응답 받아오는 시간이 5000ms 까지 느려져 렌더링 되는데 5000ms 가 걸리기도 한다.

이를 해결하기 위해서는 서버 단에서 미리 정보들을 받아온 후 해당 서버에서

빠르게 가져오면 될 것 같다.

express 를 이용해서 서버를 파놓을까 하다가 깃허브 페이지를 만들기 위해서

그냥 그 부분은 포기하기로 했다.

저번에 했던 toDoList 를 서버를 만들어 깃허브에 올려놨드니
깃허브 페이지에서는 서버를 개인이 작동 시킬 수 없어 하루종일 만든게 실행이 안되더라

토이프로젝트 이후 있을 프로젝트들에서는 백엔드 단과 함꼐 할 것이니 그 부분은 기대가 된다 룰루루


회고

쌈뽕해보이는 고화질 사진을 배경으로 넣어주고 완성했다.

밑 서브 영역도 만들려면 만들 수 있었지만 디자인 하면서 만드는 방법은 이미 체득했기 때문에

이제는 다른 공부들 마저 하고 다음 스텝으로 넘어가야 겠다 생각했다. 그렇게 해야 사람들과 하는 프로젝트를 할 수 있을 것 같기 때문이다.

그래도 자주 이렇게 바닐라 자바스크립트로 토이프로젝트들은 계속 해야겠다.

하면서 배우는게 많았다. 이번에는 Promise.all 에 대한 개념을 한 번 더 확립 할 수 있었다.

이번에 하면서 조금 아쉬웠던 점

조금 아쉬웠던 점은 반응형 웹스럽게 단위들도 바꿔볼 걸 하는 후회가 든다.

단위를 모두 px 단위로 하다보니 내 모니터에서는 잘 보이는 것들이

화면 크기를 축소하거나 확대하면 부자연스럽다.

다음번엔 단위를 염두해서 잘 만들어봐야겠다.

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글