노마드코더 바닐라JS로 크롬앱 만들기 3

딩쓰·2022년 12월 3일
post-thumbnail

노마드코더 바닐라JS로 크롬앱 만들기를 수강하면서 내용을 정리한 글입니다.

#5.0 ~ #5.3

5. CLOCK

Intervals

서로 다른 기능에 대해 파일을 분리하려고 css와 js폴더로 구분함. 따라서 html의 파일에서 greeting.js와 clock.js를 import 경로를 바꾸자.

//index.html
 <script src ="js/greetings.js"> </script>
 <script src ="js/clock.js"> </script>

html에 h2로 시계가 들어갈 자리를 만들고, JS를 이용해서 해당 값을 바꿈.

  • setInterval(): 특정 시간마다 함수를 실행하고 싶을 때 사용.
    • 첫번째 인자:실행하고자 하는 function
    • 두번째 인자:함수의 호출되는 간격(ms단위, 5000ms = 5초)
<h2 id="clock">00:00</h2>
const clock = document.querySelector("h2#clock")

function sayHello() {
  console.log("hello"); 
}

setInterval(sayHello, 5000);
//5초마다 sayHello를 실행.
  • setTimeout():간격을 두고 함수를 딱 한번만 실행할 때 사용함.
    • 첫번째 인자: 실행하고자 하는 function
    • 두번째 인자: 지연시킬 시간(ms)
setTimeout(sayHello, 5000);

Timeouts and Dates

자바스크립트의 Date 객체(참고)를 이용하면 날짜와 시간 정보를 가져올 수 있음.

  • new Date() : 현재 날짜,시간,분,초에 대한 정보 (const date = new Date();)
  • date.getDate() : 일
  • date.getDay(): 요일
  • date.getFullYear(): 연도
  • date.getHours(): 시
  • date.getMinutes() : 분
  • date.getSeconds(): 초

시계를 만들기 위해 setInterval을 이용하여 getClock 함수를 1초에 한번씩 실행시킨다. 매 초마다 우리가 하고 있는건 새로운 Date object를 만들고 있음.

setInterval을 사용하면 맨 처음에도 간격을 두고 함수가 실행되기 때문에, 바로 함수를 1번 실행시키기 위해 웹사이트가 load되자마자 getClock()을 실행함.

const clock = document.querySelector("h2#clock")

function getClock() {
  const date = new Date();
  clock.innerText =`${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; 
}

getClock(); 
setInterval(getClock, 1000);

✅ 결과화면

PadStart

위의 화면에서는 15:55:1 이런식으로 시간에 따라서, 시계의 숫자가 한자리이다. 그래서 이 숫자를 앞에 0을 채워서 두자리로 표시하자. String()을 이용해 Date object에서 얻은 값(숫자)를 문자열로 바꾼다.
=> padStart()가 문자열에만 쓸 수 있어서 String()을 이용해 문자열로 변환시킴.

pad.Start()padEnd()의 사용예시는 다음과 같다.

  • string().padStart(목표 문자열 길이, 채울 문자): 목표 문자열 길이보다 짧은 문자열의 앞부분을 특정 문자로 채운다.
  • string().padEnd(목표 문자열 길이, 채울 문자): 목표 문자열 길이보다 짧은 문자열의 뒷부분을 특정 문자로 채운다.
function getClock() {
  const date = new Date();
  const hours = String(date.getHours()).padStart(2,"0");
  const minutes = String(date.getMinutes()).padStart(2,"0");
  const seconds = String(date.getSeconds()).padStart(2,"0");

  clock.textContent =`${hours} : ${minutes} : ${seconds}`; 
}

✅ 결과화면

++ 댓글을 보니 위의 내용들을 한번에 해결하는 메서드도 있다!

function getClock() {
clock.innerText = new Date().toLocaleTimeString();
}

getClock(); 
setInterval(getClock, 1000);

💡 .toLocaleTimeString()을 이용한 결과 화면

#6.0~ #6.2

6. QUOTES AND BACKGROUND

Quotes

이제 화면에 명언을 랜덤으로 띄어보자. quotes.js파일을 만들고 html에 import 한다.
quote : 명언으로된 문자열과 author : 작가이름의 문자열로 구성된 객체(10개)들이 들어있는 배열 quotes를 이용하자.

const quotes = [

{
quote: 'I never dreamed about success, I worked for it',
author: 'Estee Lauder'
},
{
quote: 'Do not try to be original, just try to be good.',
author: 'Paul Rand'
},
//... 
  
];  
//index.html에 만들기
<div id="quote">
  <span></span>
  <span></span>
 </div>

0부터 배열의 길이(10)사이의 숫자를 이용해서 배열의 아이템들을 가져온다. 인덱스 숫자 0~9까지 나오는 함수를 만들기 위해 Math 모듈(JS에 내장됨)을 사용함. Math.floor(Math.random() * 10)

  • Math.random() : 0부터 1사이의 랜덤한 숫자를 제공함 ex)0.35016598

    • 10을 곱하면 0에서 10사이의 숫자를 얻을 수 있음.
  • 소수점 부분을 없애기 위해 사용할 수 있는 세 가지 함수

    • Math.round() : 소수점을 반올림해서 반환함. ex) 1.5 => 2
    • Math.ceil() : 숫자를 올림. 숫자를 천장(ceil)까지 높여줌. ex) 1.01 => 2
    • Math.floor() : 숫자를 내림. 숫자를 마루(floor)까지 내려줌 ex) 1.99 =>1

    배열의 길이에 따라 랜덤 숫자의 범위를 바꿔주기 위해 quotes.length로 배열 길이를 얻는다.

const quote = document.querySelector("#quote span:first-child")
const author = document.querySelector("#quote span:last-child")

const todayQuote = (quotes[Math.floor(Math.random() * quotes.length)]);

quote.innerText = todayQuote.quote;
author.innerText = todayQuote.author;

✅ 결과화면

Background

배경 이미지를 랜덤으로 보여줄거임. background.js 파일을 만들고 html에 import 한다. 그리고 사용할 이미지들(3장, 파일명 : 0.jpeg,1.jpeg,2.jpeg)을 img폴더에 넣는다.

랜덤한 숫자를 얻어서 이미지를 고르고 body에 추가하는 방식이라 Quotes랑 거의 비슷함.
다만 html요소를 dom으로 가져오지 않고 js에서 이미지태그를 만들어서 추가하는 방식이라는 점이 조금 다름.

document.createElement(태그)로 html요소를 만들고 src를 설정할 수 있다. 그리고 document.body.appendChild(요소)로 body에 추가해준다.

appendChild 대신 prepend를 사용해도 된다. appendChild는 맨 뒤에, prepend는 맨 앞에 요소를 추가한다.

const images = ["0.jpeg","1.jpeg","2.jpeg"];

const chosenImage = images[Math.floor(Math.random() * images.length)];

const bgImage = document.createElement("img");
bgImage.src = `img/${chosenImage}`;
document.body.appendChild(bgImage);
  • 결과 화면

#7 TO DO LIST ~ #7.0~ #7.8

7. TO DO LIST

Setup

todo.js파일을 만들고 index.html에 import 한다. 사용자가 todo를 나열하는 목록이 필요하므로 form과 ul을 추가함.

<form id="todo-form">
  <input type="text" placeholder="Write a To Do and Press Enter" required />
</form>
<ul id="todo-list"></ul>

form에서 엔터를 누르면 inputvalue를 새로운 변수에 복사하고 input을 비운다.

const toDoForm = document.getElementById("todo-form");
const toDoInput = toDoForm.querySelector("input")
//const toDoInput = document.querySelector(#todo-form input)
const toDoList = document.getElementById("todo-list");

fuction paintToDo(newTode){
  console.log("i will paint", newTodo);
}  

function handleToDoSubmit(event) {
  event.preventDefault();  
  const newTodo= toDoInput.value;
  toDoInput.value="";
  paintToDo(newTodo);
}

toDoForm.addEventListener("submit", handleToDoSubmit);

Adding ToDos

이제 사용자가 input창에 입력하면 입력값으로 Todolist를 만드는 것을 해볼거임. toDo를 그리는 paintToDo함수를 만든다. <li>(list item)를 만들어서 <ul>(list)에 추가한다. lispan 요소를document.createElement()로 만들고 li.appendChild(span)으로 spanli에 추가한다.

function paintToDo (newTodo) {
  const li = document.createElement("li");
  const span = document.createElement("span");
  li.appendChild(span);
  span.innerText = newTodo;
  toDoList.appendChild(li);
}

function handleToDoSubmit(event) {
  //생략
  paintToDo(newTodo);
}

✔️ 결과 화면

Deleting To Dos

todo 목록을 삭제하기 위해 각 todo 항목에 ❌ 버튼을 추가한다. 그리고 버튼에 클릭 이벤트 리스너 함수를 추가한다. 버튼을 눌렀을 때 어떤 항목의 버튼을 눌렀는지 알기 위해 deleteToDo함수에서 event 객체를 매개변수로 받는다. event.target.parentElement로 버튼의 parent(li)를 확인할 수 있음. event.target인 button의 parent를 삭제하면 해당 항목이 삭제됨.

  • console.log(event)해서 객체에서 target을 찾을 수 있음.

  • console.dir(event.target)해서 target인 button의 부모요소를 찾음

  • event 객체

    • target 객체: 클릭된 HTML 요소. 모든 HTML 요소에는 하나 이상의 프로퍼티 존재
      • parentElement 요소: 클릭된 요소의 부모
function deleteToDo(event){
  const li = event.target.parentElement;
  li.remove();
}

function paintToDo (newTodo) {
  const li = document.createElement("li");
  const span = document.createElement("span");
  span.innerText = newTodo;

  const button = document.createElement("button");
  button.innerText = "❌";
  button.addEventListener("click", deleteToDo);

  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

Saving To Dos

브라우저에 todo 목록을 저장하기 위해 로컬 스토리지를 사용한다. 변수 toDos에 배열을 만들어 놓고, handleToDoSubmit 함수에서 todo 항목을 배열에 추가한다.

toDos 배열의 내용을 로컬 스토리지에 넣는 saveToDos 함수를 만든다. 하지만 로컬스토리지에는 문자열만 저장할 수 있다. 따라서 JS의 객체나 배열 등을 문자열로 바꿔주는 JSON.stringify()를 사용한다.

  • JSON.stringify(): JS코드(객체,배열 등)를 문자열로 변환한다.

const toDos = [];

function saveToDos() {
  localStorage.setItem("todos", JSON.stringify(toDos));  
}

function handleToDoSubmit(event) {
  event.preventDefault();  
  const newTodo= toDoInput.value;
  toDoInput.value="";
  toDos.push(newTodo);
  paintToDo(newTodo);
  saveToDos();
}

✅ 결과 화면

Loading To Dos

이제 로컬 스토리지에 저장되어 있는 todos 항목들을 화면에 보여줄거임.

  • JSON.parse(문자열): 문자열을 JS에서 사용가능한 객체, 배열 등으로 변환시킨다.
  • 배열.forEach(함수): 배열의 각 항목마다 함수를 실행한다. 기본적으로 현재 처리하고 있는 항목을
    첫번째 매개변수(item)로 함수에 전달한다.
    • 화살표 함수를 사용하면 좀 더 짧게 쓸 수 있다.
    • ex) 배열.forEach((item) => console.log(item))

새로운 항목을 추가하면 예전 것을 덮어 씌우는 현상이 발생하는데, 예전 정보도 유지하기 위해 toDoslet으로 선언하고 로컬스토리지에 toDos 항목이 있으면 parsedToDos에 복원한다.

const TODOS_KEY = 'todos';

let toDos = [];

function saveToDos() {
  localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));  
}

const savedToDos = localStorage.getItem(TODOS_KEY)

 if (savedToDos !== null) {
   const parsedToDos = JSON.parse(savedToDos);
   toDos = parsedToDos;
   parsedToDos.forEach(paintToDo);
 }

✅결과 화면 (새로 입력하면 예전 값에 덮어 씌우지 않고 새로 추가되고 있음.)

Deleting To Dos

❌버튼을 눌러서 항목을 삭제하면 로컬 스토리지에서도 삭제되도록 한다.각 항목을 구별하기 위해 항목을 객체로 만들고 각 항목에 ID와 내용을 부여한다.ex) { text:newTodo, id: Date.now() } Date.now()를 이용해서 랜덤한 ID를 갖게 함.

  • Date.now():밀리초(1000분의 1초)를 랜덤으로 리턴하는 함수
    • ex)16700154578597

그리고 항목을 화면에 그리는 paintToDo에 객체를 매개변수로 전달하고 li요소의 id에 항목의 id를 저장한다.

filter함수를 이용해서 배열에서 지우고자 하는 항목을 제외하고 새 배열을 만든다. 지우고자 하는 항목(li)의 id와 같은 것을 제외한 나머지 항목만을 보여준다.

  • 배열.filter(함수):배열의 각 항목(item)마다 함수를 실행하면서 함수에서 true를 리턴하는 항목만 포함하는 새 배열을 리턴한다. filter는 함수의 첫 번째 인자로 배열 원소를 넘겨준다. 기존의 배열을 변경시키지 않는다.(immutable)
function deleteToDo(event){
  const li = event.target.parentElement;
  li.remove();
  //console.log(li);
  toDos = toDos.filter((toDo) => toDo.id !== Number(li.id));
  saveToDos();
}

function paintToDo (newTodo) {
  const li = document.createElement("li");
  li.id = newTodo.id;
  
  const span = document.createElement("span");
  span.innerText = newTodo.text;
  //...
}  

function handleToDoSubmit(event) {
  event.preventDefault();  
  
  const newTodo= toDoInput.value;
  toDoInput.value="";
  const newTodoObj = {
    text:newTodo,
    id: Date.now(),
  };
  toDos.push(newTodoObj);
  paintToDo(newTodoObj);
  saveToDos();
}

li.id는 문자열이고 toDo.id는 숫자이므로 parseInt()를 이용하여 문자열인 li.id를 숫자로 변환한다. 마지막에 saveToDos를 한 번 더 불러야 하는것도 잊지 말자.

✅ 결과 화면 (❌ 버튼을 누르면 로컬스토리지에서 삭제됨)

8.WEATHER

Geolocation

이제 navigator를 이용해서 사용자의 위치(geolocation)을 가져오고 화면에 날씨를 보여줄거임.

  • navigator.geolocation.getCurrentPosition(success, 실패 시 실행할 함수) : 브라우저에서 사용자의 위치 좌표를 리턴함. 사용자의 위치를 성공적으로 받아왔을 때 실행할 함수(success)는 GeolocationPosition 객체(Wifi,위치,GPS등)를 인자로 받음.

위치 정보에서 위도(position.coords.latitude)와 경도(position.coords.longitude)를 가져옴. openWeather API를 이용해서 위도와 경도 정보로 어느 지역에 있는지 확인할 수 있음(무료) 계정을 만들고 API key를 받는다. (openWeather 사이트)

function onGeoOk(position) {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
  console.log("You live in", lat, lng);
}

function onGeoError() {
  alert("Can't find you. No weather for you.")  
}
    
navigator.geolocation.getCurrentPosition(onGeoOk, onGeoError);

Weather API

아래와 같이 openWeather에서 API탭 > Current Weather Data (API doc)를 이용하면 현재 장소명과 날씨를 가져올 수 있다. API는 다른 서버와 소통할 수 있는 방법이다.

  • https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}
    • 위의 url에서 lat,lon,API key 자리에 변수를 넣고 요청하면 날씨와 나라,지역등을 얻어올 수 있다.
    • API key : openWeather에서 발급받은 키를 복사해서 붙여넣기

fetch로 위 url을 호출하면 API 결과를 코드로 얻어올 수 있다. 아래와 같이 호출 결과를 개발자 도구의 Network탭에서 확인할 수 있다.

fetch는 promise인데, promise는 바로 작동하지 않고 일정 시간이 지나고 서버의 응답을 받아온다. .then으로 응답을 받은뒤, 응답을 JSON 객체를 받아올 수 있다. response.json()으로 내용을 JavaScript 객체로 추출해서 값을 다룰 수 있게 된다.

온도를 화씨 단위가 아닌 섭씨 단위로 얻기 위해 url 끝에 &units=metric를 추가한다.

const API_KEY ="openWeather에서 받아온 키"

function onGeoOk(position) {
  const lat = position.coords.latitude;
  const lon = position.coords.longitude;
  const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}&units=metric`
  fetch(url).then(response => response.json())
    .then((data) => {
      const weather= document.querySelector("#weather span:first-child");
      const city = document.querySelector("#weather span:last-child");
      city.innerText = data.name;
      weather.innerText =`${data.weather[0].main} / ${data.main.temp}`
    });
}

function onGeoError() {
  alert("Can't find you. No weather for you.")  
}
    
navigator.geolocation.getCurrentPosition(onGeoOk, onGeoError);
// html의 명언부분 밑에 다음과 같은 요소도 만들어준다. 
<div id="weather">
    <span></span>
    <span></span>
</div>

✅ 결과 화면

profile
Frontend Developer

0개의 댓글