Published on

Vanilla JS로 크롬앱 만들기 정리

글쓴이

    📌 목차

    javascript
    • Why JS?
    • Welcome to Js : Variable, Array, Object
    • Js on the browser
    • Login
    • Clock
    • Quotes and Background
    • TODO list
    • WEATHER

    Why JS?

    자바스크립트는 강력하다.

    1. 모든 브라우저에 자바스크립트가 내장되어 있기 때문이다. 웹개발을 하고 싶다면 자바스크립트를 공부해야 한다.
    2. 자바스크립트로 아주 멋있는 프로젝트( ex) 3d.js 사용)를 할 수 있고, 프론트엔드 뿐 아니라 앱개발, 백엔드 개발에도 쓰인다.

    Welcome to Js

    Variable

    • 변수는 값을 담거나 유지하는 역할을 한다.
    • 변수의 네이밍은 camel case가 일반적이다.
    • letconst를 사용해 변수를 선언하지만, var는 사용하지 않아야 한다. 언어가 값의 변화를 체크해주지 않아 위험하다.

    Array

    • 하나의 그룹으로 관리하고자하는 데이터를 묶을 수 있는 그릇이다.
    • 타입에 상관 없이 Array를 구성할 수 있다.
    • 0 부터 시작하는 index를 통해 값에 접근하고, 없는 인덱스에 접근하면 undefined가 출력된다.
    const arr = [1,"a",null,undefined,1.0];
    

    Object

    때로는 단순히 순서가 정해진 하나의 그룹으로 관리하는 것이 적절하지 않을 수도 있다. Coder가 있고, Coder는 name과 쓸 수 있는 programming language, age가 있다고 가정한다.

    const coder = ['Tina','java','kotlin',26]

    위 정보는 coder와 어떤 연관성이 있는지 명확하지 않다. 그래서 의미와 값을 함께 가진 Object가 해당 데이터를 나타내기 적당하다.

    coder.name = 'Tina'
    coder.lang = ['java','kotlin','python']
    coder.age = 26

    위 내용을 자바스크립트로 표현하면 다음과 같다.

    const coder = {
        name : "Tina",
        age : 26,
        lang : ['java','kotlin','python']
    }
    console.log(coder.name); // Tina
    console.log(coder["name"]); // Tina
    coder.lang.push('js');  // ['java','kotlin','python','js']
    

    Js on the browser

    document

    html 을 js의 관점에서 볼 수 있는 object이다. 자바스크립트가 기본으로 제공하는 document를 통해 dom요소에 접근할 수 있다.

    document.body 
    document.location
    document.title
    ... 
    

    document를 기반으로, 웹페이지의 요소를 읽어오거나, 수정하거나, 추가할 수 있게된다. console창에 위와 같이 입력하면 현재 페이지의 타이틀이 바뀌는 것을 볼 수 있다.

    document.title = "내맘대로 타이틀"  
    

    요소를 가져오는 방법

    getElementById 등의 함수도 있지만 querySelectorquerySelectorAll을 더 많이 사용한다. 요소를 구체적으로 명시하기 편하기 때문이다.

    const h1 = document.querySelector('.hello:first-child h1');
    

    이벤트 추가하기

    function handleH1Click() {
        h1.innerHTML = "Mouse is clicked!";
        const currentColor = h1.style.color;
        let newColor;
        if(currentColor === 'violet'){
            newColor = 'tomato';
        }
        else {
            newColor = 'violet';
        }
        h1.style.color = newColor;
    }
    function handleH1Enter() {
        h1.innerHTML = "Mouse is here!";
        h1.style.color = 'blue';
    }
    function handleH1Leave() {
        h1.innerHTML = "Mouse is out!";
        h1.style.color = 'skyblue'
    }
    h1.addEventListener("click",handleH1Click);
    h1.addEventListener("mouseenter",handleH1Enter);
    h1.addEventListener("mouseleave",handleH1Leave);
    console.dir(h1);
    

    console.dir을 통해 on으로 시작하는 함수를 찾으면 해당 요소에서 사용할 수 있는 다양한 이벤트를 찾을 수 있다.

    window

    windowdocument와 같이 javascript에서 기본으로 제공하는 객체이다. window는 현재 자바스크립트가 돌아가고 있는 웹 페이지의 을 의미한다.

    function handleWindowResize() {
        document.body.style.backgroundColor = "#ccc";
    }
    function handleWindowCopy() {
        alert("copied!");
    }
    function handleOffline() {
        alert("SOS no wifi");
    }
    function handleOnline() {
        alert("connected wifi");
    }
    window.addEventListener("resize",handleWindowResize);
    window.addEventListener("copy",handleWindowCopy);
    window.addEventListener("offline",handleOffline);
    window.addEventListener("online",handleOnline);
    

    css

    위 예시에서는 js에서 직접적으로 스타일을 바꿔줬지만, 스타일은 css에서 전담하는 것이 권장된다고 한다.

    body {
        color: cornflowerblue;
    }
    .active {
        color : tomato;
    }
    

    스타일을 javascript에서 html요소를 접근하는 방식으로 풀어나가는 것이 보다 적절하다. 아래는 contains, remove, add를 이용하는 방법을 toggle을 통해 한 줄로 줄인 모습이다.

    function handleH1Click() {
        // const activeClass = "active";
        // if(h1.classList.contains(activeClass)===activeClass) {
        //     h1.classList.remove(activeClass);
        // }
        // else {
        //     h1.classList.add(activeClass);
        // }    
    
        h1.classList.toggle("active")
    
    }
    

    Login

    사용자에게 이름을 입력 받는 코드를 작성하였다. 사용자의 입력값은 언제든 믿으면 안되기 때문에 언제든지 유효성 검사를 하는 것은 좋은 습관이다.

    const loginInput = document.querySelector("#login-form input");
    const loginButton = document.querySelector("#login-form button");
    
    function onLoginBtnClick() {
        const value = loginInput.value;
        if (value === "") {
            alert("please write your name");
        } else if (value.length > 15) {
            alert("Your name is too long");
        }
    
    }
    
    loginButton.addEventListener("click", onLoginBtnClick);
    

    다만, 이번 케이스에서는 html에서 기본으로 지원하는 requiredmaxlength 옵션을 사용할 수도 있다. 값을 입력하지 않으면 입력하라는 자체 메세지가 뜨고, 최대길이를 넘어가면 더이상 입력되지 않는다.

    <form id="login-form">
        <input required maxlength="15" type="text" placeholder="What is your name?"/>
        <button>Log In</button>
    </form>
    

    아래는 유저의 input을 받아 해당 input을 이용하는 코드이다.

    ...
    .active {
        color : tomato;
    }
    
    .hidden {
        display: none;
    }
    ...
    <body>
        <form id="login-form">
        <input required maxlength="15" type="text" placeholder="What is your name?"/>
        <button>Log In</button>
        </form>
        <h1 class="hidden" id="greeting"></h1>
    </body>
    
    const greeting = document.querySelector("#greeting");
    const HIDDEN_CLASS = "hidden"
    function onLoginsubmit(event) {
        event.preventDefault();
        console.log(event);
    
        const userName = loginInput.value;
        loginForm.classList.add(HIDDEN_CLASS);
        greeting.classList.remove(HIDDEN_CLASS);
        
        greeting.innerHTML = `Hello ${userName}!`;
    }
    

    event.preventDefault()는 기본적으로 브라우저에 내장된 작업을 실행하지 않겠다는 뜻이다.

    submit되면 창을 새로고침하고, html checkbox를 체크되는 등의 default 액션을 방지하고 프로그래머의 논리에 따라 작업을 수행하겠다는 뜻이다.

    localStorage

    setItem, getItem, removeItem을 이용해 key-value의 형태로 값을 저장할 수 있다. 저장된 데이터는 개발자도구의 Application > Local Storage메뉴를 참조하면 된다. 아래 코드는 username이 localStorage 저장여부에 따라 보이는 요소가 달라지도록 한 것이다.

    const greeting = document.querySelector("#greeting");
    const HIDDEN_CLASS = "hidden";
    const USERNAME_KEY = "username";
    
    const savedUsername = localStorage.getItem(USERNAME_KEY);
    if (savedUsername === null) {
        loginForm.classList.remove(HIDDEN_CLASS);
        loginForm.addEventListener("submit", onLoginsubmit);
    } else {
      paintGreetings();
    }
    
    function paintGreetings() {
        loginForm.classList.add(HIDDEN_CLASS);  
        greeting.classList.remove(HIDDEN_CLASS);
        greeting.innerText = `Hello ${savedUsername}!`;
    }
    
    function onLoginsubmit(event) {
      event.preventDefault();
    
      const userName = loginInput.value;
      localStorage.setItem(USERNAME_KEY, userName);
      paintGreetings(userName);
    }
    

    Clock

    interval vs timeout

    interval은 액션의 일정한 주기를 의미한다. 아래 코드는 1초마다 console에 hello는 메세지가 띄워진다.

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

    timeout은 액션이 일정한 시간 뒤에 이루어지는 것을 의미한다. 사용방식은 interval과 비슷하지만 한번만 실행된다.

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

    Date

    js에서는 기본적인 시간 관련 객체를 제공한다.

    new Date();

    const clock = document.getElementById("clock");
    function getClock() {
        const date = new Date();
        const hours =date.getHours();
        const mins = date.getMinutes();
        const secs = date.getSeconds();
        const time = `${hours}:${mins}:${secs}`;
        console.log(time);
        clock.innerText =time;
    
    }
    getClock();
    setInterval(getClock,1000);
    

    padStart() , padEnd()

    const clock = document.getElementById("clock");
    function getClock() {
        const date = new Date();
        const hours =date.getHours().toString().padStart(2,"0");
        const mins = date.getMinutes().toString().padStart(2,"0");
        const secs = date.getSeconds().toString().padStart(2,"0");
        const time = `${hours}:${mins}:${secs}`;
        clock.innerText =time;
    }
    getClock();
    setInterval(getClock,1000);
    

    Quotes and Background

    Math

    js에서 제공하는 수학관련 함수들이 있다.

    • Math.floor() : 내림
    • Math.round() : 반올림
    • Math.ceil() : 올림
    • Math.random() : 0~1 사이의 랜덤한 실수
    • Math.abs(a) : a의 절댓값
    • Math.pow(x,y) : x^y

    Math.random() 사용하기

    <div id="quotes">
        <span></span>
        <span></span>
    </div>
    
    const quote = document.querySelector("#quotes span:first-child");
    const author = document.querySelector("#quotes span:last-child");
    
    const randomNum = Math.floor(Math.random() * quotes.length);
    const todaysQuote = quotes[randomNum].quote;
    const todaysAuthor = quotes[randomNum].author;
    
    quote.innerText = todaysQuote;
    author.innerText = todaysAuthor;
    

    createElement, appendChild 사용하기

    const images = ["0","1","2","3","4","5"];
    
    const randomImg = Math.floor(Math.random() * (images.length-1));
    const todaysImg = `${images[randomNum]}.jpeg`;
    
    const bgImage = document.createElement("img");
    bgImage.src = `img/${todaysImg}`;
    document.body.appendChild(bgImage);
    

    TODO list

    JSON.stringify, JSON.parse

    stringify는 자바스크립트의 객체, 배열을 JSON string으로 변환시켜주는 함수이다. localStorage에는 배열이나 객체가 저장될 수 없기에 해당 함수를 사용한다. parse는 string을 자바스크립트 데이터로 바꿔주는 함수이다.

    const TODOS_KEY = "todos";
    let todos = [];
    const saveTodosStr = localStorage.getItem(TODOS_KEY);
    
    function saveTodos() {
        localStorage.setItem(TODOS_KEY,JSON.stringify(todos));
    }
    function deleteTodo(event) {
        const li = event.target.parentElement;
        todos = todos.filter((todo)=> todo.id !== parseInt(li.id)) ;
        li.remove();
        saveTodos();
    }
    
    function paintTodo(newTodo) {
        console.log(newTodo);  
        const li = document.createElement("li");
        const span = document.createElement("span");
        const button = document.createElement("button");
        span.innerText = newTodo.text;
        button.innerText = "x";
        li.id = newTodo.id;
        li.appendChild(span);
        li.appendChild(button);
        button.addEventListener("click",deleteTodo);
        toDoList.appendChild(li);   
    }
    
    function handleTodoSubmit(e) {
        e.preventDefault();
        const newTodo = toDoInput.value;
        toDoInput.value = "";
        const idStr = Date.now().toString();
        console.log(newTodo,idStr);
    
        const newTodoObj = {
            id : idStr,
            text : newTodo,
        };
        if(todos===null) todos =[];
        todos.push(newTodoObj);
        paintTodo(newTodoObj);
        saveTodos();
    }
    

    WEATHER

    navigator는 브라우저에 대한 다양한 정보를 제공하는 객체이다.

    const weather = document.querySelector("#weather span:first-child");
    const city = document.querySelector("#weather span:last-child");
    const API_KEY = "blahblah";
    
    function onGeoOk(position) {
      const lat = position.coords.latitude;
      const lng = position.coords.longitude;
      
      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) => {
          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);
    

    궁금증

    todo array is null ? (파일 상단에서 초기화를 해줬음에도: let todos =[];)

    defer 옵션으로 js를 로딩한 상태에서 파일 상위에 초기화한 todo array가 null 로 떠서 해당 에러가 나는 줄에 초기화 코드(if(todos===null) todos =[];)를 넣어주니 해결되었다. todo null과 관련된 부분은 let todos = []와 filter뿐이다. 이전에 동일한 로직으로, 한 js파일에서 개발한 코드에서는 잘 돌아갔는데, 로딩시점의 문제인지 추측만 하는 중이다.

    발전을 위한 링크