[유데미x스나이퍼팩토리] 10주 완성 프로젝트 캠프 18일차
🍊 React
1. React
UI 컴포넌트를 만들고 재사용하는데 매우 용이
- UI 컴포넌트 생성
- 만들어진 컴포넌트를 통해 사용자와 인터렉션
- 화면을 업데이트 하는데 최적화
메타(구 Facebook)에서 관리하고 있는 오픈소스 프로젝트
회사가 사라지지 않는 한 꾸준한 관리와 업데이트를 기대 가능
createElement 함수
이 함수를 사용하여 React 엘리먼트를 만들 수 있음
React 엘리먼트는 React에서 UI를 구성하는 데 사용되는 작은 조각이다.
- 첫 번째 인수는 생성할 요소의 타입: ‘div’, ‘h1’, ‘p’와 같은 HTML 요소 타입
- 두 번째 인수는 요소의 속성: className, style, onClick과 같은 속성
- 세 번째 인수는 요소의 자식(children): 요소 내부에 다른 요소나 텍스트를 포함시킬 수 있음
Ex) const container = React.createElement(“div”, null, “hello”)
리액트 쉽게 사용해보기
<!-- React JS는 어플리케이션이 아주 Interactive 하도록 만들어주는 Library -->
<script src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<!-- React DOM은 React element들을 HTML body에 둘 수 있도록 해줌 -->
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<!-- 바벨은 JSX를 브라우저가 이해 할 수 있는 형태로 바꾸어 줌 -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
React CDN은 개발 단계에서 사용하기 좋음
별도의 빌드 과정 없이 React 애플리케이션을 빠르게 테스트하고 공유가능
그러나 프로덕션 환경에서는 보다 성능이 우수한 빌드 도구를 사용하여 React 애플리케이션을 번들링하고 서비스하는 것이 좋다.
JSX(JS + XML)?
const element = <h1>hello world</h1>
와 같은 형태로 우리가 만들고자 하는 컴포넌트 안에 들어가는 HTML 요소들을, JS 안에서 실제 HTML의 모습과 동일하게 사용할 수 있도록 지원함
JS에 등장한 template literal과 매우 유사
리액트 컴포넌트
React 앱은 컴포넌트로 만들어짐. 컴포넌트는 고유한 논리와 모양이 있는 UI(사용자 인터페이스)의 일부
컴포넌트는 버튼만큼 작을 수도 있고 전체 페이지만큼 클 수도 있음
React 컴포넌트는 마크업을 반환하는 JavaScript 함수
function MyButton() {
return(
<button>I'm a button</button>
);
}
export default function MyButton() {
return(
<div>
<h1>Welcome to my app</h1>
<MyButton/>
</div>
);
}
2. 과제 - Momentum(할일 목록, 내 위치 날씨 + Username) 완성
HTML
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Momentum</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" />
<link rel="stylesheet" href="./css/style.css">
<script defer src="./js/main.js"></script>
</head>
<body>
<header>
<!-- weather -->
<div id="weather">
<span class="CurrIcon"></span>
<span class="CurrTemp"></span>
<span class="City"></span>
</div>
</header>
<main>
<div class="inner">
<!-- clock -->
<div class="clock">
<h1 id="time"></h1>
</div>
<!-- user -->
<div class="greeting">
<form class="hidden" id="login_form">
<input required maxlength="15" type="text" placeholder="What is your name?">
<button>Log in</button>
</form>
<h2 class="hidden" id="greetings"></h2>
</div>
<!-- todo -->
<div class="todo">
<form id="todo_form">
<input required type="text" placeholder="Write a To Do and Press Enter.">
</form>
<ul id="todo_list"></ul>
</div>
</div>
</main>
</body>
</html>
CSS
body {
height: 100vh;
background: url("https://picsum.photos/1920") no-repeat center fixed;
background-size: cover;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
li {
list-style: none;
}
.hidden {
display: none !important;
}
header {
height: 5%;
backdrop-filter: contrast(0.5);
font-weight: bold;
display: flex;
justify-content: end;
align-items: center;
}
#weather {
margin-right: 10%;
color: #fff;
}
main {
width: 100%;
height: 95%;
display: flex;
justify-content: center;
align-items: center;
}
main .inner {
width: 30%;
padding: 100px;
border: 5px solid #fff;
backdrop-filter: blur(10px);
text-align: center;
}
.clock {
color:#fff;
font-size: 26px;
margin-bottom: 20px;
text-shadow: 1px 2px rgb(0 0 0 / 40%);
}
input {
border: 0;
background-color: rgb(225, 225, 225, 0.8);
border-radius: 5px;
padding: 10px 20px;
}
#login_form {
display: flex;
justify-content: space-between;
}
#login_form input {
width: 75%;
}
.greeting button {
border: 0;
background-color: rgb(225, 225, 225, 0.8);
border-radius: 5px;
padding: 10px 20px;
}
#greetings {
color:#fff;
line-height: 45px;
border-bottom: 1px solid #fff;
text-shadow: 1px 2px rgb(0 0 0 / 40%);
}
.todo {
margin-top: 20px;
}
.todo input {
width: 100%;
}
#todo_list{
margin-top: 20px;
}
#todo_list li {
display: flex;
justify-content: space-between;
background-color: rgb(0, 0, 0, 0.5);
border-radius: 5px;
padding: 10px 20px;
margin-bottom: 10px;
color: #fff;
}
#todo_list button {
border: 0;
background-color: transparent;
cursor: pointer;
}
JS
//weather
const weatherIcon = {
'01': 'fas fa-sun',
'02': 'fas fa-cloud-sun',
'03': 'fas fa-cloud',
'04': 'fas fa-cloud-meatball',
'09': 'fas fa-cloud-sun-rain',
'10': 'fas fa-cloud-showers-heavy',
'11': 'fas fa-poo-storm',
'13': 'far fa-snowflake',
'50': 'fas fa-smog',
};
function displayWeather(data) {
const iconCode = data.weather[0].icon.substr(0, 2);
const temperature = Math.floor(data.main.temp) + '℃';
const city = data.name;
const currIcon = document.querySelector('.CurrIcon');
currIcon.innerHTML = '<i class="' + weatherIcon[iconCode] + '"></i>';
const currTemp = document.querySelector('.CurrTemp');
currTemp.prepend(temperature);
const cityElement = document.querySelector('.City');
cityElement.innerHTML = city;
}
function getWeather(latitude, longitude) {
const API_KEY = 'd126eafe75d75dc8f3cb05542db8df4d';
fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${API_KEY}&units=metric`
)
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log(data);
displayWeather(data);
});
}
function handleGeoSuccess(pos) {
const latitude = pos.coords.latitude;
const longitude = pos.coords.longitude;
getWeather(latitude, longitude);
}
function handleGeoError() {
console.log("Error!");
}
function init() {
navigator.geolocation.getCurrentPosition(handleGeoSuccess, handleGeoError);
}
init();
// 시계
const time = document.getElementById('time');
function getTime() {
const date = new Date();
const hours = String(date.getHours()).padStart(2, "0"); // padStart(길이, 왼쪽부터 채워넣음)
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0")
time.innerText = `${hours}:${minutes}:${seconds}`;
}
getTime();
setInterval(getTime, 1000);
// login && 인사말
const loginForm = document.getElementById("login_form");
const loginInput = document.querySelector("input");
const greetingWords = document.querySelector("#greetings");
const HIDDEN = "hidden";
const USERNAME_KEY = "username";
function LoginSubmit(e) {
e.preventDefault();
loginForm.classList.add(HIDDEN);
const username = loginInput.value;
localStorage.setItem(USERNAME_KEY, username);
paintGreeting(username);
}
// 시간에 따라 인사말 처리
function paintGreeting(username) {
const date = new Date();
const time = date.getHours();
let greetingWord = "";
if (0 <= time && time < 5) {
greetingWord = "Good day";
} else if (5 <= time && time < 9) {
greetingWord = "Good morning";
} else if (9 <= time && time < 17) {
greetingWord = "Good afternoon";
} else {
greetingWord = "Good evening";
}
greetingWords.innerHTML = `${greetingWord} ${username} :D`;
greetingWords.classList.remove(HIDDEN);
}
const savedUsername = localStorage.getItem(USERNAME_KEY);
if(savedUsername === null) {
loginForm.classList.remove(HIDDEN);
loginForm.addEventListener('submit', LoginSubmit);
} else {
paintGreeting(savedUsername);
}
// TodoList
const toDo = document.getElementById("todo_form");
const toDoList = document.getElementById("todo_list");
const toDoInput = toDo.querySelector("input");
let toDos = [];
const TODO_KEY = "todo";
function TodoSubmit(e) {
e.preventDefault();
const newToDo = toDoInput.value;
toDoInput.value = "";
const ToDoObj = {
text: newToDo,
id: Date.now(),
}
toDos.push(ToDoObj);
paintToDo(ToDoObj);
saveToDo();
}
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 deleteToDo(e) {
const li = e.target.parentElement;
li.remove();
toDos = toDos.filter((toDo) => toDo.id !== parseInt(li.id));
saveToDo();
}
function saveToDo() {
localStorage.setItem(TODO_KEY, JSON.stringify(toDos));
}
toDo.addEventListener("submit", TodoSubmit)
const savedToDos = localStorage.getItem(TODO_KEY);
if(savedToDos !== null) {
const parsedToDos = JSON.parse(savedToDos);
toDos = parsedToDos;
parsedToDos.forEach(paintToDo);
}
3. 결과
UserName
시간마다 바뀌는 인사말
할 일 목록
삭제
4. 회고
몸살이 온거 같아 수요일에는 오프라인 수업을 듣지 못하고 온라인 수업을 들었다.. 🥲
첫 React 수업인데 온라인으로 들어서 매우 아쉬웠다.. 금요일에 가면 열심히 들어야지…
React를 처음 들어가서 그런건지 그렇게까지 어렵거나 그런건 없었던거 같다 조금 헷갈리는 정도? 나중가서 컴포넌트 엄청 나누어지면 머리아프겟지?
과제는 저번에 이어서 Momentum를 완성하는 거였다. 저번에 UserName받아서 해야 하는지 몰라가지고 안했었는데 이번에 만들어뒀다!
날씨 api는 처음 배우고 처음 써봤는데 다른 api쓰는거랑 비슷해서 다를건 없어보였다. 다음에도 쓸 일이 있었으면 좋겠당
금요일에는 뭘 배울지 기대된당 흐흐ㅡㅎ 그리고 이번 과제는 뭔가 잘 된거 같아서 좋다! 앞으로 재밌는 과제 내주심 좋겠당
본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.
#프로젝트캠프 #프로젝트캠프후기 #유데미 #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #리액트 #react #부트캠프 #리액트캠프
Leave a comment