NomadCoder의 “Creating a Chrome App with Vanilla JS”를 수강하면서 배웠거나 추가로 발견한 내용을 요약한 것입니다.
만들다
설정
프롬프트에 할 일을 입력하고 키나 엔터를 누르면 할 일 데이터가 변수에 저장되고 사용자가 프롬프트에 입력한 값은 지워져 화면에 보이지 않는다.
// HTML
<body>
<form class="hidden" id="login-form">
<input type="text" maxlength="15" placeholder="Whai is your name?" required />
<button>Log In</button>
</form>
<h2 id="clock">00:00:00</h2>
<h1 class="hidden" id="greeting"></h1>
<form id="todo-form">
<input type="text" placeholder="Write a To Do and Press Enter" required />
</form>
<ui id="todo-list"></ui>
<div id="quote">
<span></span>
<span></span>
</div>
<script src="http://oliviakim.m/./js/greetings.js"></script>
<script src="./js/clock.js"></script>
<script src="./js/quotes.js"></script>
<script src="./js/background.js"></script>
<script src="./js/todo.js"></script>
</body>
// JavaScript
const toDoForm = document.querySelector('#todo-form');
const toDoInput = toDoForm.querySelector('#todo-form input');
const toDoList = document.querySelector('#todo-list');
function handleToDoSubmit(event) {
// submit 이벤트의 새로고침을 막는다.
event.preventDefault();
// newTodo 변수에 현재 입력한 value를 넣는다.
const newTodo = toDoInput.value;
// 화면에 보이는 value를 초기화시킨다.
toDoInput.value="";
}
toDoForm.addEventListener('submit', handleToDoSubmit);
작업 추가
사용자가 입력한 작업 데이터를 화면에 그리는 기능을 만듭니다.
// JavaScript
// to do를 그리는 역할을 담당한다.
function paintToDo(newTodo) {
// li, span html 요소를 만든다.
const li = document.createElement('li');
const span = document.createElement('span');
// li 안에 span 요소를 넣는다.
li.appendChild(span);
// span에는 newTodo 값을 넣는다.
span.innerText = newTodo;
// html에 작성했던 toDoList(ul) 안에 li를 넣는다.
toDoList.appendChild(li);
}
작업 삭제(1)
사용자가 작업을 완료하면 버튼을 클릭하여 작업을 지우고 화면에서 해당 항목을 지웁니다.
// JavaScript
// to do를 지운다.
function deleteToDo(event) {
// 어떤 li를 지워야하는지 찾아내기 위해 click된 button의 부모 요소(li)를 찾는다.
const li = event.target.parentElement;
// 해당 요소를 지운다.
li.remove();
}
// to do 요소를 만들어 html에 그린다.
function paintToDo(newTodo) {
const li = document.createElement('li');
const span = document.createElement('span');
span.innerText = newTodo;
// 작성한 투 두 리스트를 삭제할 수 있는 버튼 요소를 만든다.
const button = document.createElement('button');
// button 안에 X (삭제) 이모지를 넣는다.
button.innerText="❌";
button.addEventListener('click', deleteToDo);
// append는 맨 마지막에 들어가야 한다! (값을 가진 상태로)
li.appendChild(span);
li.appendChild(button);
toDoList.appendChild(li);
}
작업 저장
사용자가 입력한 작업을 일회성이 아닌 완료될 때까지 저장하려면,로컬 메모리‘ 값을 저장합니다. 로컬 저장소에서는 페이지를 새로고침하고 브라우저/운영 체제를 다시 시작해도 데이터가 사라지지 않습니다. 이때 로컬 스토리지 키와 값은 문자열이어야 합니다.
// JavaScript
const toDoForm = document.querySelector('#todo-form');
const toDoInput = toDoForm.querySelector('#todo-form input');
const toDoList = document.querySelector('#todo-list');
// 저장할 to Dos의 배열
const toDos = ();
console.log(toDos)
function saveToDos() {
localStorage.setItem('todos', toDos);
}
// to do를 지운다.
function deleteToDo(event) {
const li = event.target.parentElement;
li.remove();
}
// to do 요소를 만들어 html에 그린다.
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);
}
// to do 데이터를 받는다.
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value="";
// to Dos 배열에 newTodo 값을 push한다.
toDos.push(newTodo);
paintToDo(newTodo);
saveToDos();
}
toDoForm.addEventListener('submit', handleToDoSubmit);
지금까지 구현된 경우 발생하는 로컬 스토리지 관련 문제
로컬 저장소는 텍스트만 저장할 수 있습니다. (‘in local storage’, ‘in’, ‘data’)와 같이 나중에 데이터를 쉽게 삽입하고 빼기 위해 배열 모양쉽게 액세스할 수 있도록 인덱싱해야 합니다.
브라우저가 가진 기능을 즉시 사용합니다. JSON.문자열화()! 이 방법은 JavaScript 값 또는 개체를 JSON 문자열로 변환합니다. (그래서 JSON이란 무엇인가? JSON은 자바스크립트 객체, 배열 등 데이터를 표현하는 텍스트 기반 방식이다.) 따라서 JSON.stringify()를 사용하면 개체와 배열을 JSON 형식 문자열로 변환하여 텍스트 값만 저장할 수 있는 로컬 저장소에 저장할 수 있습니다.
// 기존 코드
function saveToDos() {
localStorage.setItem('todos', toDos);
}
// JSON.stringify() 이용
function saveToDos() {
localStorage.setItem('todos', JSON.stringify(toDos));
}
JSON.stringify()의 힘을 빌려 데이터를 로컬 스토리지에 저장하면 .
저장된 값을 볼 때 질문이 생겼습니다.
인덱스 순서 0, 1, 2로 텍스트만 저장하는 로컬 저장소의 값을 계속 표시하는 방법은 무엇입니까?
키와 값은 로컬 저장소에 있어야 합니다. 선해야한다 JSON을 사용하면 개체를 작성할 수 있습니다.아!
작업이 로드됨
사용자가 입력한 값을 로컬 저장소에 저장하는 작업을 마쳤습니다. 그러나 해당 내용이 화면에 표시되지 않으므로 해당 기능이 구현됩니다. 로컬 저장소에 저장된 값을 검색하려면 JSON.구문 분석JSON으로 저장된 내역을 가져옵니다.
// JavaScript
const TODOS_KEY = 'todos';
...
// local storage에 있는 값을 가져와 화면에 그린다.
const savedToDos = localStorage.getItem(TODOS_KEY);
if(savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos);
// item을 인자로 넘겨주지 않아도, forEach가 자동적으로 각 요소를 하나씩 실행하며 넘긴다.
// 기존에 만들어뒀던 to do 요소를 화면에 그려주는 함수인 paintToDo를 호출한다.
parsedToDos.forEach(paintToDo);
}
작업 삭제(2)
앞의 코드에서 사용자가 입력한 작업인 li를 삭제하면 화면에서 삭제되지만 로컬 저장소의 값은 삭제되지 않습니다. 이때 로컬 저장소의 값을 삭제하려면 삭제하려는 ‘값’이 무엇인지 정확히 알아야 합니다. 현재 값 또는 시퀀스 번호를 알 수 없습니다.
따라서 로컬 저장소에 저장할 때
- 일반 배열 형태가 아닌 “오브젝트” 형태로 데이터를 저장하도록 수정합니다.
- 데이터 저장 시 중복되지 않는 각각의 ID 값을 지정하여 저장합니다.
ID 값을 저장하려면 Date.now()를 사용하십시오. Date.now()는 ms(1/1000)초를 반환하는 함수입니다.
// JavaScript
const toDoForm = document.querySelector('#todo-form');
const toDoInput = toDoForm.querySelector('#todo-form input');
const toDoList = document.querySelector('#todo-list');
const TODOS_KEY = 'todos';
// 저장할 to Dos의 배열
// 배열 값이 변경되어야 하므로 기존 const -> let으로 변경
let toDos = ();
// local storage에 입력한 to do를 저장한다.
function saveToDos() {
localStorage.setItem(TODOS_KEY, JSON.stringify(toDos));
}
// to do를 지운다.
function deleteToDo(event) {
const li = event.target.parentElement;
li.remove();
// 삭제 버튼을 클릭한 외의 li는 남겨두고 다시 새 배열로 반환한다.
toDos = toDos.filter(toDo => toDo.id !== Number(li.id));
// local storage에도 해당 내용을 반영할 수 있도록 saveToDos() 함수를 호출한다.
saveToDos();
}
// to do 요소를 만들어 html에 그린다.
function paintToDo(newTodo) {
const li = document.createElement('li');
// date.now()로 생성한 값을 li요소의 id값으로 부여한다.
li.id = newTodo.id;
const span = document.createElement('span');
// span에는 text값만 출력한다.
span.innerText = newTodo.text;
const button = document.createElement('button');
button.innerText="❌";
button.addEventListener('click', deleteToDo);
li.appendChild(span);
li.appendChild(button);
toDoList.appendChild(li);
}
// to do 데이터를 받는다.
function handleToDoSubmit(event) {
event.preventDefault();
const newTodo = toDoInput.value;
toDoInput.value="";
// 기존과 같이 text만 저장하지 않고, id값도 같이 저장한다.
const newTodoObj = {
text: newTodo,
id: Date.now(),
}
toDos.push(newTodoObj);
paintToDo(newTodoObj);
saveToDos();
}
toDoForm.addEventListener('submit', handleToDoSubmit);
// local storage에 있는 값을 가져와 화면에 그린다.
const savedToDos = localStorage.getItem(TODOS_KEY);
if(savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos);
// item을 인자로 넘겨주지 않아도, forEach가 자동적으로 각 요소를 하나씩 실행하며 넘긴다.
parsedToDos.forEach(paintToDo);
}
날씨
사용자의 현재 위치 정보를 JavaScript로 얻을 수 있는 Geolocation API와 Weather API를 결합하여 사용자 위치의 날씨를 표시합니다.
(참조) 날씨 API
날씨 API – OpenWeatherMap
빠르고 사용하기 쉬운 날씨 API를 사용하려면 로그인하십시오. OpenWeather 제품 사용을 시작하려면 One Call API 3.0을 권장합니다. 더 많은 기능을 보려면 전문 컬렉션에 포함된 제품을 참조하십시오.
openweathermap.org
// HTML
<body>
<form class="hidden" id="login-form">
<input type="text" maxlength="15" placeholder="Whai is your name?" required />
<button>Log In</button>
</form>
<h2 id="clock">00:00:00</h2>
<h1 class="hidden" id="greeting"></h1>
<form id="todo-form">
<input type="text" placeholder="Write a To Do and Press Enter" required />
</form>
<ui id="todo-list"></ui>
<div id="quote">
<span></span>
<span></span>
</div>
<div id="weather">
<span></span>
<span></span>
</div>
<script src="http://oliviakim.m/./js/greetings.js"></script>
<script src="./js/clock.js"></script>
<script src="./js/quotes.js"></script>
<script src="./js/background.js"></script>
<script src="./js/todo.js"></script>
<script src="./js/weather.js"></script>
</body>
// JavaScript
function onGeoSuccess(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=weatherAPI에서 발급받은 본인의 고유 API값&units=metric`;
// fetch는 promise이다
// promise는 당장 뭔가 일어나지 않고 시간이 좀 걸린 뒤에 일어난다.
fetch(url).then(response => response.json()).then(data => {
const location = document.querySelector('#weather span:first-child');
const weather = document.querySelector('#weather span:last-child');
location.innerText = data.name;
weather.innerText = `${data.weather(0).main} / ${data.main.temp}℃`;
});
}
function onGeoFail() {
alert("Can't find you. No weather for you.");
}
navigator.geolocation.getCurrentPosition(onGeoSuccess, onGeoFail);
API KEY는 사용자별로 다르게 할당되는 고유한 값입니다. 따라서 타인에게 노출될 경우 해킹으로 인해 예상치 못한 보안 문제나 과금 문제가 발생할 수 있습니다.
저도 이 코드를 Github에 푸시하고 있어서 API 키를 노출하지 않고 코드를 작성해야 했습니다. 대부분 .env라는 별도의 환경변수 파일을 만들어서 그 파일에 저장해놔서 github에 올라가지 않는데, .env로만 구성된 단순한 파일이라 별도의 .env를 만들어야 하는 부담이 있었습니다. HTML과 CSS, js. 같은 문제가 있는 사람의 블로그 게시물을 찾아 다음과 같이 해결했습니다.
1. apikey.js 파일을 생성하고 주어진 키 값을 파일에 삽입합니다. (파일명은 자유)
const config = {
'apikey': '부여받은API키값입력'
};
2. index.html에 apikey.js 파일을 연결합니다.
// HTML
<body>
...
<script src="http://oliviakim.apikey.js"></script>
<script src="http://oliviakim.m/./js/greetings.js"></script>
<script src="./js/clock.js"></script>
<script src="./js/quotes.js"></script>
<script src="./js/background.js"></script>
<script src="./js/todo.js"></script>
<script src="./js/weather.js"></script>
</body>
Weather.js에서 해당 KEY의 이름을 지정하고 apikey.js에서 KEY 값이 저장됩니다. 다시 말해서 Wheather.js는 apikey.js에 있는 값을 다운받아 사용해야 하므로 apikey.js를 연결하는 위치는 weather.js보다 위에 있어야 합니다! apikey.js 파일을 먼저 읽고 데이터를 기반으로 Wheather.js 파일을 읽기 때문입니다.
3. whater.js에서 적절한 API 키를 상수로 지정하여 사용합니다.
const WEATHER_API = config.apikey;
4. gitignore 파일을 생성하고 apikey.js를 추가하여 파일이 github에 업로드되지 않도록 합니다.
Vanilla JS로 크롬 앱 구축 강의 완료! 나는 강의보다 연습을 더 좋아하기 때문에 강의를 듣는 것을 별로 좋아하지 않는다. 강의를 마쳤으니 최대한 코드를 보지 않고 직접 구현하여 마무리하도록 하겠습니다. 끝까지 싸워라
(참고 자료)
https://nomadcoders.co/javascript-for-beginners
Vanilla JS로 Chrome 앱 빌드 – Nomad Coders
초보자를 위한 자바스크립트
nomadcoders.co
https://en.javascript.info/localstorage#ref-539
localStorage 및 sessionStorage
de.javascript.info
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
JSON.stringify() – 자바스크립트 | MDN
JSON.stringify() 메서드는 JavaScript 값 또는 개체를 JSON 문자열로 변환합니다. 함수에 전달되면 replacer는 변환 전에 선택적으로 값을 변환할 수 있으며 배열로 전달되면 지정된 속성만 결과에 포함됩니다.
developer.mozilla.org
https://developer.mozilla.org/en/docs/Web/API/Navigator/geolocation
Navigator.geolocation – 웹 API | MDN
읽기 전용 Navigator.geolocation 속성은 인터넷에서 장치를 찾는 데 사용할 수 있는 Geolocation 개체를 반환합니다. 웹 사이트 또는 웹 앱은 지리적 위치를 사용하여 결과 화면을 개인화할 수 있습니다.
developer.mozilla.org
https://velog.io/@kimjumpsun_code/Github%EC%97%90-API-Key-%EC%88%A8%EA%B8%B0%EA%B8%B0
Github에서 API 키 숨기기
이번 5월 노마드 챌린지에 참여하기 전에 이미 API 버튼을 그대로 눌렀습니다. 좋은 소식은 비공개로 업로드되었다는 것입니다.
velog.io