과제의 요구사항은 총 3개로, 하나의 페이지에 구현하는 것이다
추가 요구사항은 목요일 과제로 진행될 예정이다
앱 전체를 div id=app
으로 감싸준 뒤, 구현해야할 기능별로 section
을 나눠주었다
사용자의 이름을 입력받을 섹션에는 기본적으로 hide
라는 class
를 주어 숨김처리하고 있다(추후에 js
로 컨트롤하여 이 클래스를 제거해준다)
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello World?</title>
<link rel="stylesheet" href="./style/reset.css" />
<link rel="stylesheet" href="./js-assignment-css.css" />
<script defer src="./js-assignment-js.js"></script>
</head>
<body>
<div id="app">
<!-- weather -->
<section id="weather"></section>
<!-- clock -->
<section id="clock"></section>
<!-- greeting -->
<section id="greeting" class="hide">
<form class="login-box">
<div class="user-box">
<input type="text" id="userName" />
<label for="userName">이름을 입력해주세요</label>
</div>
</form>
</section>
<!-- todo -->
<section id="todo">
<form>
<!-- <input type="text" name="" id="" /> -->
</form>
</section>
</div>
</body>
</html>
전에는 background-image
를 쓰지않고 이미지태그 하나, 백그라운드 어두운처리용 div
를 썼었는데 그러지 않아도 어둡게 처리할 수 있다
background-image: linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.75)), url("IMG_PATH")
스크립트는 IIFE
로 즉시실행을 해준다(없어도 실행은 되지만 전역변수의 사용을 피하기위해 사용하였다)
(() => {
console.log("IIFE");
...
})()
랜덤 배경화면은 준비된 이미지들의 PATH들을 담은 배열과 Math.random()
을 활용하여 background
스타일의 url
을 갈아끼워주는 방식으로 구현했다
// random background
const DEFAULT_PATH = "./assets/";
const imgPaths = [
"bg-bicycle.jpeg",
"bg-bunny.jpeg",
"bg-forest.jpeg",
"bg-japan.jpeg",
"bg-ocean.jpg",
"bg-wall.jpeg",
];
const App = document.getElementById("app");
App.style.backgroundImage = `linear-gradient(rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0.75)), url(${DEFAULT_PATH + imgPaths[Math.floor(Math.random() * imgPaths.length)]
})`;
인사의 경우, 로컬스토리지를 활용해 값이 있는 경우와 없는 경우에 렌더링되어야 하는 요소가 다르다
이를 구현하기위해 렌더링을 해주는 함수는 밖으로 빼주었다
분리된 함수를 통해, 값을 가져왔을 때(로컬스토리지로부터) 있다면 인사를 렌더링하고 없다면 form
의 hide
클래스를 제거하여 유저로부터 이름을 받는다
// greeting
let userName = localStorage.getItem("userName");
const $loginForm = document.querySelector(".login-box");
const showGreeting = () => {
$loginForm.remove();
userName = localStorage.getItem("userName");
const $sectionGreeting = document.getElementById("greeting");
const $h1 = document.createElement("h1");
$h1.textContent = userName + "님 안녕하세요";
$sectionGreeting.appendChild($h1);
};
if (userName) showGreeting();
else {
document.getElementById("greeting").classList.remove("hide");
$loginForm.addEventListener("submit", (e) => {
e.preventDefault();
const userName = document.getElementById("userName").value.trim();
if (userName) {
document.getElementById("userName").value = "";
// blur(): Remove focus from a text input
document.getElementById("userName").blur();
localStorage.setItem("userName", userName);
showGreeting();
}
});
}
시계의 경우 핵심은 1초마다 현재시간을 렌더링해야 하는것이 필수였다
이를 위해 시간을 지정하면 콜백함수를 실행시키는 setInterval
를 활용했다
약간의 문제점은 setInterval
은 지정한 시간 이후부터 실행이 되기에 약간의 딜레이가 생긴다
이를 해결하기 위해 시간을 출력하는 함수를 분리하고, setInterval
이전에 showTime
을 한번만 실행해주면 조금 더 부드럽게 출력이 된다
// clock
const $sectionClock = document.getElementById("clock");
const $h2 = document.createElement("h2");
const showTime = () => {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
const AMPM = hours > 12 ? "PM" : "AM";
const addZero = (num) => {
let result = String(num);
return result.length === 1 ? "0" + result : result;
};
$h2.textContent = `${AMPM} ${addZero(hours)} : ${addZero(
minutes
)} : ${addZero(seconds)}`;
$sectionClock.appendChild($h2);
};
showTime();
setInterval(showTime, 1000);
setInterval
을 활용해 1000ms
의 시간마다 현재시간을 보여준다localStoarge
에 userName
이라는 key
를 가진 value
가 없다면, form
의 hide
라는 class
를 제거하여 폼을 보여준 뒤 입력받을 수 있도록 해주고, 해당 value
가 있다면 인사말을 남긴다
본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.