바닐라JS로 크롬앱 만들기 수강 완료!👏🏻

이민선(Jasmine)·2022년 11월 30일
0
post-thumbnail

생활코딩을 수강하며 HTML, CSS를 익혔고 JAVASCRIPT도 나름 익숙해졌으니 나만의 웹페이지 만들기를 본격적으로 시작해보았다. 노마드코더 바닐라JS 강의를 수강하며 웹페이지에 로그인 폼, 시계, todo list, 날씨 등을 구현하고 랜덤으로 사진과 명언을 띄우는 방법도 알게되었다.

특히 로그인 폼과 todo list가 상대적으로 배워야 할 게 많았다. 날씨 구현에 필요한 프로미스 부분은 아직 JAVASCRIPT에서도 진도를 커버하지 못한 부분이기 때문에 얼른 엘리코딩 진도 끝까지 빼고 다시 들여다봐야겠다. 그래도 니꼬쌤의 구현 로직을 차근차근 따라하다보면 어느새 완성이 되어 있다!

쨔잔!
훨씬 더 예쁘게 만들 수 있을 것 같긴 하다. 사실 바닐라JS만으로도 훨씬 예쁘게 만드는 게 가능은 하지만, 나는 얼른 리액트를 배워서 더 예쁜 앱을 만들어보고 싶다는 생각이기에 우선은 이 쯤까지 진행했다.

그래도 노마드코더 바닐라 JS 수강하면서 배운 게 무척 많기 때문에
이 곳에도 HTML, CSS, JAVASCRIPT 코드와 ⭐️핵심 포인트⭐️를 기록하려고 한다.

HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../css/style.css" >
<title>Momentum App</title>
</head>
<body>
  <form  id="login-form" >
    <input 
      required
      maxlength="15"
      type="text"
      placeholder="what is your name?"
    />
    <input type="submit" value="Log In" />
  </form>
  <h1 class="hidden" id="greeting"></h1>
  <h2 id="clock">00:00:00</h2>
  <div id="weather">
    <span></span>
    <span></span>
    </div>
  <form id="todo-form">
    <input type="text" placeholder="Write a To Do and Press Enter" style="width:200px"/ required />
  </form>
  <ul id="todo-list">
    <li>
      <span></span>
      <span></span>
    </li>
  </ul>
  <div class="quote">
    <span></span>
    <span></span>
  </div>  
  <script src="./greetings.js"></script>
  <script src="./clock.js"></script>
  <script src="./quotes.js"></script>
  <script src="./background.js"></script>
  <script src="./todo.js"></script>
  <script src="./weather.js"></script>
</body>
</html>

JAVASCRIPT import하는 데에 시간을 많이 잡아먹었다. 그 이유는 상대경로라는 개념을 몰랐었기 때문이다.

  • 상대경로 : 현재 웹페이지의 소속 폴더를 기준점으로 상대를 찾는 것
  <script src="js/greetings.js"></script>

했는데 오류가 나왔다. 띠용? 2시간 순삭 ^^
다행히 구글링을 통해 js파일명 앞에 ./을 붙여서 해결!
.은 현재 웹페이지가 소속된 폴더를 의미한다.

JAVASCRIPT

1. greetings

const loginForm = document.querySelector("#login-form");
const loginInput = document.querySelector("#login-form input");
const greeting = document.querySelector("#greeting");

const HIDDEN_CLASSNAME = "hidden";
const USERNAME_KEY = "username";

function onLoginSubmit(event) {
event.preventDefault();
loginForm.classList.add(HIDDEN_CLASSNAME);
localStorage.setItem(USERNAME_KEY,loginInput.value); 
paintGreetings();
}

function paintGreetings(){
    loginForm.classList.add(HIDDEN_CLASSNAME);
    const username = localStorage.getItem(USERNAME_KEY);
    greeting.innerText = `Hello ${username}`;
    greeting.classList.remove(HIDDEN_CLASSNAME);
}

const savedUsername = localStorage.getItem(USERNAME_KEY);

if(savedUsername === null){
    loginForm.classList.remove(HIDDEN_CLASSNAME);
    loginForm.addEventListener("submit",onLoginSubmit);
} else {
    paintGreetings();
}

⭐️

  • querySelector를 이용하여 id나 class로 element 불러오기
  • localStorage에 key, value 저장, 불러오기, 삭제하기
  • HTML에서 input의 유효성 검사(required, 글자수 제한 등등)를 작동시키기 위해서는 form 안에 들어있어야 한다는 점을 기억할 것!
  • 다만, form 안에 있을 경우 input에 값을 입력할 때마다 새로 고침된다. 새로고침되는 게 싫다면? event.preventDefault()를 사용!

2.clock

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

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.innerText = `${hours}:${minutes}:${seconds}`;
}
getClock();
setInterval(getClock, 1000);

⭐️

  • setInterval함수
  • padStart : 최소 몇 자리까지 나타낼 것인가? 만약 앞자리가 비어있다면 무엇으로 채울래?!

3.quote

const quotes = [
    { quote: "모든 인생은 실험이다 . 더많이 실험할수록 더나아진다.",
      author:  "-랄프 왈도 에머슨",
    },
    { quote: "길을 잃는 다는 것은 곧 길을 알게 된다는 것이다.",
      author: "-동아프리카속담",
    },
    { quote: " -겨울이 오면 봄이 멀지 않으리",
      author: "-셸리",
    },
    { quote: "한 번 실패와 영원한 실패를 혼동하지 마라.",
      author: "-F.스콧 핏제랄드",
    },
    { quote: "최고에 도달하려면 최저에서 시작하라.",
    author: "-P.시루스",
    },
    { quote: "작은 기회로 부터 종종 위대한 업적이 시작된다",
    author: "-데모스테네스",
    },
    { quote: "A room without books is like a body without a soul",
    author: "-Marcus Tullius Cicero",
    },
    { quote: "Life is either a daring adventure or nothing at all.",
    author: "-Helen Keller",
    },
    { quote: "The greatest glory in living lies not in never falling, but in rising every time we fall.",
    author: "-Nelson Mandela",
    },
    { quote: "The dice is cast.",
    author: "-Julius caesar",
    },
    { quote: "요플레는 설탕 없는 게 가장 맛있다.",
    author: "Jasmine",
    },
];

const quote = document.querySelector(".quote span:first-child");
const author = document.querySelector(".quote span:last-child");
const todaysQuote = quotes[Math.floor(Math.random()*quotes.length)];

quote.innerText = todaysQuote.quote;
author.innerText = todaysQuote.author;

명언은 인터넷으로 간단히 검색! 나름 자극되고 의미있는 것으로 골랐다. ㅋㅋㅋㅋ(마지막 뭔데 ㄱ-)

⭐️

  • 랜덤함수 사용하여 배열에 있는 명언 중 하나 랜덤으로 띄우기
    Math.random은 0에서 1 사이의 값을 출력하기 때문에 배열의 원소 개수를 곱해주어야 한다.

4.background

const images = [
    "0.jpg",
    "1.jpg",
    "2.jpg",
    "3.jpg",
    "4.jpg",
    "5.jpg",
]

const chosenImage = images[Math.floor(Math.random()*images.length)];
console.log(chosenImage);
const bgImage = document.createElement("img");

bgImage.src = `../img/${chosenImage}`
document.body.appendChild(bgImage);

img 폴더에 사진 저장 후 파일명으로 배열 생성.
명언과 마찬가지로 랜덤함수 사용. 동일한 로직을 또 사용하니 복습이 되어 좋다! (난 말하는 금붕어다)
VS code에 사진 어떻게 저장하는지 헤맸던 나 실화? ㅋㅋㅋ
그냥 드래그 해오면 된다 ^^;;

⭐️

  • getElementByID가 가능하다? createElement도 가능하다!

5.todo

제일 복잡한 뚜두리스트 만들기 ..ㅎㅎ 인강 듣고 이해하는 시간만 6시간 걸렸다 ^^

뚜두리스트 삭제 파트를 들을 때 나의 머릿속 현황
코드를 살펴보자 차근차근~

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

const TODOS_KEY = "todos"

let toDos = [];

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

function deleteToDo(event) {
 const li = event.target.parentElement; 
 li.remove();
 toDos = toDos.filter((toDo)=> toDo.id !== parseInt(li.id));
 saveToDos();
}

function paintToDo(newTodo) {
  const li = document.createElement("li");
  li.id = newTodo.id;
  const span = document.createElement("span");
  span.innerText = newTodo.text;
  const button = document.createElement("button");
  button.innerText = "-"
  button.addEventListener("click", deleteToDo)
  li.appendChild(span);
  li.appendChild(button);
  toDoList.appendChild(li);
}

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

toDoForm.addEventListener("submit",handleToDoSubmit);

const savedToDos =localStorage.getItem(TODOS_KEY);
console.log(savedToDos);

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

}

⭐️

  • localStroage에 이미 저장되어있는 todo를 가져올 때 JSON.parse를 통해 배열 형태로 가져올 것! forEach함수를 돌려야 하니까!

  • deleteToDo함수에서 event.target은 X버튼을 의미. parentElement는 클릭된 element(X버튼)의 부모인 li를 의미한다.

  • form을 submit할 때 default는 새로고침이다.

  • handelToDoSubmit함수의 toDos.push(newTodoObj); 에서 toDos가
    localStotage에서 불러온 toDos라는 것을 명시적으로 입력해주지 않으면, 새로 입력하는 toDos를 빈 문자열에 push하게 된다. 그럼 새로고침할 때 이미 저장된 todo들이 덮어씌우지게 된다.
    따라서 localStorage에서 불러온 toDos를 JSON파싱할 때 toDos = parsedToDos; 라고 명시적으로 입력할 것!

-새로 입력하는 todo들에게 고유의 id를 부여하여 객체 형태로 저장할 수 있다. 니꼬쌤은 고유의 id를 Date.now()로 지정하셨다. 아주 편리하고 신박하다.

6.weather

const API_KEY = "14b5b66cc716410d017d9bd77a4015e6";

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

⭐️

  • openweathermap.org에 들어가서 회원가입을 하면 내 고유의 API키를 얻을 수 있다.
    -navigator.geolocation.getCurrentPosition(onGeoOK, onGeoError)을 활용하여 위치 정보를 얻을 수 있을 때와 없을 때 실행시킬 함수를 각각 인자로 전달한다.

CSS

.hidden {
    display: none;
}
body {
    margin : 0px;
    background-color: rgb(134, 220, 225);
    }

h1 {
    text-align: center;
}
h2 {
    text-align: center;
    padding-bottom: 1px;
}
form {
    text-align: center;
    padding-top: 15px;

}
   
button {
    width: 2px
    height: 1px;
}
#weather {
    text-align: center;
    padding-top: 0px;

}

.quote {
    text-align:right;
}


ul{
   list-style:none;
   text-align: center;
   }

img {
    width: 100%;
    height: 100%
}

-todolist에서 각각의 todo 앞에 두꺼운 중간점(마커)이 마음에 안들어서 list-style을 none으로 지정했다.
-hidden 클래스는 사용자가 로그인을 하기 전에는 greeting에 지정, 로그인을 한 뒤에는 로그인 폼에 지정하여 display : none 을 활용했다.

이상! 나의 바닐라 JS 수강하며 만든 웹페이지였다.
사실 지금 새로운 웹페이지를 하나 더 만드는 중이다.
니꼬쌤 코드를 따라서 입력하는 거랑 내가 구현하고 싶은 것을 찾아서 입력하는 거랑 난이도 차이가 크다.
좀 더 향상된 실력으로 새로운 웹페이지를 완성하면 또 올려보는 것으루 ~!
byebye~ (^^)/

profile
기록에 진심인 개발자 🌿

0개의 댓글