Fake Momentum 만들기(2)

Doum Kim·2020년 7월 5일
0

Javascript

목록 보기
20/23

Todo-list 기능 추가

todo.js

const makeTodoList = () => {
  const todoListForm = document.querySelector('.todo-list-form');
  const inputTodoItem = todoListForm.querySelector('.input-todo-item');
  const TodoList = document.querySelector('.todo-list');

  const TODOS_LS = 'todos';
  let TODOS = [];

  const saveTodos = () => {
    localStorage.setItem(TODOS_LS, JSON.stringify(TODOS));
  };

  const deleteTodoItem = (e) => {
    const ItemWillBeDeleted = e.target.parentElement;
    TodoList.removeChild(ItemWillBeDeleted);
    const filteredTodos = TODOS.filter((todo) => {
      return todo.id !== parseInt(ItemWillBeDeleted.id);
    });
    TODOS = filteredTodos;
    saveTodos();
  };

  const pushTodoItem = (todo) => {
    const toDoItem = {
      todo: todo,
      id: TODOS.length + 1,
    };
    TODOS.push(toDoItem);
    saveTodos();
  };

  const addDeleteFeature = (button) => {
    button.addEventListener('click', deleteTodoItem);
  };

  const makeTodoItem = (text) => {
    const itemTemplate = document.querySelector('.todo-item-template');
    const clone = document.importNode(itemTemplate.content, true);
    const TodoListItem = clone.querySelector('li.todo-item');
    const spanItemText = clone.querySelector('span');
    const deleteItemButton = clone.querySelector('button');
    TodoListItem.id = TODOS.length + 1;
    spanItemText.textContent = text;
    deleteItemButton.textContent = '🗑';
    addDeleteFeature(deleteItemButton);
    TodoList.appendChild(clone);
    pushTodoItem(text);
  };

  const loadTodos = () => {
    const loadedTodos = localStorage.getItem(TODOS_LS);
    if (loadedTodos) {
      const parsedTodos = JSON.parse(loadedTodos);
      parsedTodos.forEach((todoItem) => {
        makeTodoItem(todoItem.todo);
      });
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const currentTodo = inputTodoItem.value;
    makeTodoItem(currentTodo);
    inputTodoItem.value = '';
  };

  const init = () => {
    loadTodos();
    todoListForm.addEventListener('submit', handleSubmit);
  };

  init();
};

export default makeTodoList;

이번에는 todo item 등록, 삭제 그리고 로컬 스토리지에 저장하는 기능을 구현했다.

기능을 크게 나눠보면 template 태그를 이용해 element를 생성 및 추가해주는 기능과 추가할 때 생성해 준 각각 아이템의 id를 이용해 todo item을 삭제해준다. TODOS라는 배열에 push를 해주고 로컬 스토리지에 저장을 해준다. 또한 기존에 로컬스토리지에 데이터가 있을 경우에는 load 단계에서 받아온 데이터로 element 생성 함수를 각각 호출시켜준다.

input에 할 일을 추가해주면 로컬 스토리지에 저장 및 화면에 출력을 해준다.

배경 이미지 랜덤으로 불러오기

background.js

const changeBackground = () => {
  const backgroundElement = document.getElementById('background');

  const IMG_AMOUNT = 4;

  const paintImage = (imageNumber) => {
    backgroundElement.style.backgroundImage = `url(./img/bg${imageNumber}.jpg)`;
  };

  const getRandomNumber = (amount) => {
    const randomNumber = Math.floor(Math.random() * amount + 1);
    return randomNumber;
  };

  const backgroundFadeIn = () => {
    setTimeout(() => backgroundElement.classList.add('fade-in'), 300);
  };

  const init = () => {
    paintImage(getRandomNumber(IMG_AMOUNT));
    backgroundFadeIn();
  };

  init();
};

export default changeBackground;

배경 이미지를 랜덤으로 불러오기 위해서는 일단 랜덤한 숫자가 필요했다.
그리고 그 랜덤 숫자를 이용해서 백그라운드 이미지에 url을 랜덤하게 불러올 수 있었다.
그리고 고화질의 배경 사진을 불러오다보니 다소 버퍼링이 걸리는 증상이 있어 backgroundFadeIn이라는 함수로 fadeIn 효과를 주었다. (아직도 조금 수정해야하는 부분....)

날씨 정보 제공 구현

weather.js

const makeWeatherInfo = () => {
  const weatherElement = document.querySelector('.weather-info');

  const COORDS = 'coords';
  const API_KEY = '키';

  const getWeatherInfo = (lat, lon) => {
    fetch(
      `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`,
    )
      .then((res) => res.json())
      .then((data) => {
      const temperature = data.main.temp - 273.15;
      const countryName = data.sys.country;
      const locationName = data.name;
      weatherElement.textContent = `${temperature.toFixed(
        2,
      )}°C ${locationName} ${countryName}`;
    });
  };

  const saveCoords = (coords) => {
    localStorage.setItem(COORDS, JSON.stringify(coords));
  };

  const getGeoSuccess = (position) => {
    const { latitude, longitude } = position.coords;
    const coordsInfo = {
      latitude,
      longitude,
    };
    saveCoords(coordsInfo);
    getWeatherInfo(latitude, longitude);
  };

  const getGeoFail = () => {
    console.log('Can not access geo info.');
  };

  const askForCoords = () => {
    navigator.geolocation.getCurrentPosition(getGeoSuccess, getGeoFail);
  };

  const loadCoords = () => {
    const loadedCoords = localStorage.getItem(COORDS);
    if (!loadedCoords) {
      askForCoords();
    } else {
      const parseCoords = JSON.parse(loadedCoords);
      getWeatherInfo(parseCoords.latitude, parseCoords.longitude);
    }
  };

  const init = () => {
    loadCoords();
  };
  init();
};

export default makeWeatherInfo;

날씨 정보 제공 API는 https://api.openweathermap.org 이 곳을 이용했다.
간단하게 설명하자면 navigator객체에 gelocation 프로퍼티의 getCurrentPosition이라는 메소드를 사용해서 현재 위치 정보를 얻었다. 그렇게 얻은 위치 정보를 이용하여 API에 요청을 보내면 날씨에 대한 정보를 얻을 수 있었다.

여기서 API 요청은 fetch를 사용했으며 json() 이라는 메소드를 처음 사용해봤다.

Body mixin의 json() 매서드는 Response 스트림을 가져와 스트림이 완료될때까지 읽는다. 이 메서드는 body 텍스트를 JSON으로 바꾸는 결과로 해결되는 promise를 반환한다.
MDN 링크

완성 후기

일단 바닐라 자바스크립트 하나 하나 내가 만들어간다는게 재밌으면서도 손이 많이 가는 작업이란걸 깨달았다. 내가 좀 더 리액트 공부를 열심히 해야겠다는 이유를 알았다... 고생하기 싫으니깐..

로컬 스토리지르 활용해서 데이터 저장을 하는 방법도 손에 익었고 어떻게 하면 기능별로 함수를 작게 쪼개서 관리하면 좋을지 훈련도 됐고 이렇게 기능별로 작게 쪼개서 사용하면 어떤 이점이 있는지도 알게 되었다.

그리고 fetch로 비동기 요청을 했을 경우에 응답을 어떤식으로 처리해줘야 하는지도 새롭게 배운거 같아서 좋다. 항상 axios만 써보다가 fetch를 써보니 또 새로운걸 알아가는 재미가 있었다.

또 navigator에 위치정보를 알 수 있는 메소드가 있다는건 알았지만 직접 써보니 역시 나는 아직 갈 길이 멀었구나 더 열심히 해야겠다라는 동기부여를 얻었다.

다음에는 또 어떤걸 만들어보지 고민을 하며 포스팅을 마친다.

0개의 댓글