다진 Javascript (2)

Kyle·2022년 5월 26일
0

Javascript

목록 보기
2/11
post-thumbnail

✔모던 자바스크립트 2장

참고 - https://poiemaweb.com/

01. 비동기적 처리, Call Back

1) 비동기적 처리

  • 흐름이 멈추지 않기 때문에 동시에 여러가지 작업 처리 가능, 기다리는 과정에서 다른 함수 호출 가능
  • 쉽게 설명하자면 "나 기다리지 말고 먼저 할거 하고 있어! 난 서버에 너가 요청한 데이터 가지러 갔다올게!"
    하고 계속 다음줄을 수행해 나가는 것
  • 응답을 기다리지 않고 다음 작업을 진행, 실행이 모두 끝난 후 응답을 받아서 효율적으로 일처리 가능
console.log('1');
setTimeout(function() {
		console.log('2');
}, 1000);
console.log('3');
  1. 1 출력
  2. 브라우저 API인 setTimeout이 있으므로, 브라우저에게 1초 뒤에 출력하라는 요청이 들어왔음을 전달
  3. 요청을 전달하고 기다리지 않고 바로 다음 코딩(3 출력) 실행
  4. 브라우저에서 1초의 시간이 지나고 콜백함수 실행

2) Call Back

setTimeout(function() {
		console.log('2');
}, 1000);

//동일한 의미
setTimeout(() => console.log('2'), 1000);
  • 전달하는 이 함수는 바로 실행되는 것이 아니라 setTimeout이라는 함수 안에 하나의 만든 함수를 전달
  • 1초가 지난 이후에 실행해달라는 의미, 다시 불러달라는 의미에서 CallBack 이라고 붙여진 이름

3) 동기적 Call Back

function printImmediately(print) {
	print();
}

console.log('1');
setTimeout(() => console.log('2'), 1000);
console.log('3');

printImmediately(() => console.log('hello'));
  • 출력순서

     1 - 3 - hello - 2
     

4) 비동기적 Call Back

function printImmediately(print) {
	print();
}

function printWithDelay(print, timeout) {
		setTimeout(print, timeout);
}

console.log('1'); //동기 
setTimeout(() => console.log('2'), 1000);  //비동기 
console.log('3');  // 동기

printImmediately(() => console.log('hello'));  //동기
printWithDelay(() => console.log('async callback'), 2000);  //비동기 
  • 출력순서

     1 - 3 - hello - 2 - async callback
     

02. 콜백 지옥과 Promise

1) Callback 지옥

  • 순차적이지 않은 방법 내에서 프로그램이 진행이 되는 경우 콜백함수 통해서 구현
  • 하지만 무한 콜백지옥에 빠질 수 있음

콜백지옥 EXAMPLE

function delay(sec, callback) { 
		setTimeout(() => {
				callback(new Date().toISOString());
		}, sec * 500);
}

delay(1, (result) => {
		console.log(1, result);
		delay(1, (result) => {
				console.log(1, (result) => {
						delay(1, (result) => {
								console.log(1, result);
						});
				});
		});
});
  • delay라는 함수에는 초의 값과 callback함수가 들어가게 됨
  • delay라는 함수에서 1초 후에 result콜백함수에 의해서 1과 현재시간 출력
  • 1초후에 result 콜백함수에 의해서 1과 현재시간 출력
  • 또 1초 후에 result 콜백함수에 의해서 1과 현재시간 출력

⇒ 그러므로 가독성이 상당히 좋지 못한 코드가 되어버림 / 그래서 PROMISE의 개념 등장!

2) PROMISE

const promise = new Promise(function(resolve, reject) {
	if (성공이면) {
		resolve(성공값);
	} else {
		reject(실패메세지);
	}
});
  • resolve와 reject를 받게 되는 콜백을 남기게 됨

  • 포인트 2가지 : 1) STATE와 2) PRODUCER, CONSUMER

     - STATE : 프로세스가 기능 수행중인지 성공했는지 실패했는지 
  • PRODUCER VS CONSUMER : 데이터를 제공하는 API와 제공된 데이터를 사용하는 API

  • Producer : promise는 executor라는 콜백함수 전달, executor가 resolve와 reject를 받음

  • Consumer : then, catch, finally를 이용해서 데이터 받아옴

resolve : 할 일을 다 하였을 때 호출하는 것 (기능을 정상적으로 수행하여 데이터 전달)
reject : 할 일을 하다가 에러가 났을 때 호출하는 것

function delay(sec) { 
		return new Promise((resolve, reject) => {
				setTimeout(() => {
						resolve(new Date().toISOString());
				}, sec * 500);
		});
}

delayP(1).then((result) => {
	console.log(1, result);
	return delayP(1);
}).then((result) => {
		console.log(2, result);
		return delayP(1);
}).then((result) => {
		console.log(3, result);
		return delayP(1);
});
  • delayP라는 함수 내에서는 Promise 객체를 리턴하게 됨
  • promise 객체는 resolve와 reject 인자를 받아서 함수 내의 실행이 성공하면 resolve 반환,
    실패하면 reject 반환
  • delayP(1)을 실행하면 result값이 Promise의 반환 값 출력 ( 단, promise반환값은 resolve가 되어야 함)
const promise = new Promise((resolve, reject) => {
	console.log("doing something");
	setTimeout(() => {
		resolve("sojeong");  //성공적으로 기능 수행하면 resolve라는 콜백함수 실행
		reject(new Error("no network"));  //성공적으로 받아오거나 데이터 resolve를 통해 전달
	}, 2000);
});

promise
	.then((value) => {      //원하는 기능 수행하는 callback 함수 
		console.log(value);
	})
	.catch((error) => {     //에러를 핸들링 하려면 catch 사용, 에러발생시 처리방법 콜백함수 등록
		console.log(error);
	})
	.finally(() => {
		console.log("finally");  //promise의 성공과 실패상관없이 호출
	});

03. async/await

1) async/await이란?

  1. 비동기의 🌸이자 하이라이트라고 불리는 async/await

  2. 새로운 것이 아닌 Promise에 조금 더 간편한 API를 제공함

    → class가 프로토타임을 베이스로 하는 것과 같이

  3. 깔끔하게 프로미스를 사용하게 하는 방법의 일종

단, 프로미스에서 async/await 으로 100% 변환해야한다 x

->프로미스 유지

2) 사용하는 이유

promise의 단점을 보완하기 위해.

즉, .catch로 에러를 잡을 때 , 정확히 어디서 발생했는지 알아내기 어렵..

  • 특정 값을 공유해가며 작업을 처리하기 까다로운 요 단점을 극복!

3) 사용법

  • 함수를 선언 할 때 함수 앞부분에 async를 넣어주고
  • Promise 앞부분에 await을 넣어주면 됨.
  1. async

    function fetchUser() {
      return new Promise((resolve, reject) => {
        resolve ('shhhak');
      });
    }
    
    const user = fetchUser();
    user.then(console.log);
    console.log(user);

    <promise만> 예시...

    async function fetchUser() {
        return 'shhhak';
    }
    
    const user = fetchUser();
    user.then(console.log);
    console.log(user);

    <async 사용> 예시...

    💡 번거롭게 Promise를 쓰지 않아도 자동적으로 함수 안에 있는 Promise 블록들이 프로미스로 변환되어진다.
    1. await

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      async function process() {
        console.log('안녕하세요!');
        await sleep(1000); // 1초쉬고
        console.log('반갑습니다!');
      }
      
      process();

      // "안녕하세요"

      // "반갑습니다"

      👆처럼

      해당 프로미스가 끝날 때까지 기다렸다가 다음 작업이 가능하다.

      Next!

      function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      async function getmung() {
      	await delay(2000);
      	return 'mung';
      }
      
      async function getwal() {
      	await delay(2000);
      	return 'wal';
      }
      
      function mungwal() {
      	return getmung().then(mung => {
      		return getwal().then(wal => '${mung} + ${wal}');
      	});
      }
      
      mungwal().then(console.log);

      <Promise로 합체!(?)>

      콜백지옥이 떠오른다.!!

      function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      async function getmung() {
      	await delay(2000);
      	return 'mung';
      }
      
      async function getwal() {
      	await delay(2000);
      	return 'wal';
      }
      
      async function mungwal() {
      	const mung = await getmung();
      	const wal = await getwal();
      	return '${mung} + $(wal)';
      }
      
      mungwal().then(console.log);

      <async를 써보자>

      4)장점

      • 가독성 굿
      • 평소에 쓰던 return도 편하게 사용 ㄱㄴ

      💦주의: await 단독으로 사용은 불가

      → async가 있는 함수 안에서만 사용 가능

      async 함수에서 에러를 발생 시킬때에는 throw 를 사용하고, 에러를 잡아낼 때에는 try/catch 문을 사용한다.

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      async function makeError() {
        await sleep(1000);
        const error = new Error();
        throw error;
      }
      
      async function process() {
        try {
          await makeError();
        } catch (e) {
          console.error(e);
        }
      }
      
      process();

      await sleep()이 부분을 비우면 바로 실행

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      const getDog = async () => {
        await sleep(1000);
        return '멍멍이';
      };
      
      const getRabbit = async () => {
        await sleep(500);
        return '토끼';
      };
      const getTurtle = async () => {
        await sleep(3000);
        return '거북이';
      };
      
      async function process() {
        const dog = await getDog();
        console.log(dog);
        const rabbit = await getRabbit();
        console.log(rabbit);
        const turtle = await getTurtle();
        console.log(turtle);
      }
      
      process();

      getDog 는 1초, getRabbit 은 0.5초, getTurtle 은 3초가 걸리고 . 총 4.5초

      동시에 작업을 시작하고 싶다면, 다음과 같이 Promise.all 을 사용

      async function process() {
        const results = await Promise.all([getDog(), getRabbit(), getTurtle()]);
        console.log(results);
      }

      만약에 여기서 배열 비구조화 할당 문법을 사용한다면 각 결과값을 따로 따로 추출해서 조회 할 수 있음

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      const getDog = async () => {
        await sleep(1000);
        return '멍멍이';
      };
      
      const getRabbit = async () => {
        await sleep(500);
        return '토끼';
      };
      const getTurtle = async () => {
        await sleep(3000);
        return '거북이';
      };
      
      async function process() {
        const [dog, rabbit, turtle] = await Promise.all([
          getDog(),
          getRabbit(),
          getTurtle()
        ]);
        console.log(dog);
        console.log(rabbit);
        console.log(turtle);
      }
      
      process();

      Promise.all 를 사용 할 때에는, 등록한 프로미스 중 하나라도 실패하면, 모든게 실패 한 것으로 간주함.

      이번에는Promise.race 라는 것에 대해서 알아봅시다. 이 함수는 Promise.all 과 달리, 여러개의 프로미스를 등록해서 실행했을 때 가장 빨리 끝난 것 하나만의 결과값을 가져

      function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
      }
      
      const getDog = async () => {
        await sleep(1000);
        return '멍멍이';
      };
      
      const getRabbit = async () => {
        await sleep(500);
        return '토끼';
      };
      const getTurtle = async () => {
        await sleep(3000);
        return '거북이';
      };
      
      async function process() {
        const first = await Promise.race([
          getDog(),
          getRabbit(),
          getTurtle()
        ]);
        console.log(first);
      }
      
      process(); // 토끼

04. HTML과 JavaScript 연동하기

시작 전 알아야 할 점

Vanilla JavaScript란?

→ 별도의 라이브러리/프레임워크를 사용하지 않는 형태

DOM이란?

→ 각 태그에 대한 정보를 지니고 있는 JavaScript 객체

TMI : JS는 95%가 객체로 이루어짐

사용자의 인터랙션 (상호작용)에 따라 동적으로 UI를 업데이트하고 싶다면, JavaScript를 연동해 주어야 한다.

보통 인터랙션이 많은 경우에는 Vanilla JavaScript 를 사용해서 하기에는 코드의 양도 많아지고 코드 관리도 어려운 편이라 보통 React, Vue, Angular 등의 도구를 사용한다.

👊🏻 start

1) HTML과 JS 연동

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <h2 id="number">0</h2> // id 하나
    <div>
      <button id="increase">+1</button> // id 둘
      <button id="decrease">-1</button> // id 셋
    </div>

    <script src="src/index.js"></script> // **<-** 요 부분 
  </body>
</html>

이처럼 id 값을 설정해주면 JavaScript에서 쉽게 해당 DOM을 선택할 수 있다.

2) DOM 선택하기

const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");

console.log(number);
console.log(increase);
console.log(decrease);

getElementById로 DOM 선택 완완 🐣

DOM에 내장된 기능

console.log(number.innerText); // 내용 // **0**
console.log(increase.offsetTop); // top 위치 // **70**
console.log(decrease.id); // id // **decrease**

Element.blah blah😗🥱

여기 에 위 세 가지 기능 외에 다양한 Properties가 소개되어 있다.

3) 이벤트 설정하기

: on이벤트이름 값에 함수를 설정하면 된다.

정말 다양한 DOM 이벤트가 있다.

버튼들이 클릭 됐을 때 콘솔에 텍스트를 출력하는 이벤트

const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");

increase.onclick = () => {
  console.log("increase 가 클릭됨");
};

decrease.onclick = () => {
  console.log("decrease 가 클릭됨");
};

잘 출력되는 것을 볼 수 있다.

👆🏻👆🏻이 렇 게 하 려 면 ?

                           👇👇
const number = document.getElementById("number");
const increase = document.getElementById("increase");
const decrease = document.getElementById("decrease");

increase.onclick = () => {
  const current = parseInt(number.innerText, 10);
  number.innerText = current + 1;
};

decrease.onclick = () => {
  const current = parseInt(number.innerText, 10); // 요기
  number.innerText = current - 1;
};

// 요기 줄에 parseInt 는 문자열을 숫자로 변환해주는 함수이며,

           10 은 10진수로 숫자를 받아오겠다는 의미

4) 모달 만들기

모달이란, 다음 이미지와 같이 기존의 내용을 가리고 나타나는 메시지박스 같은 형태의 UI 를 의미한다.

4-1) HTML

<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <h1>안녕하세요!</h1>
    <p>내용내용내용</p>
    <button id="open">버튼 열기</button>
    <div class="modal-wrapper" style="display: none;">
      <div class="modal">
        <div class="modal-title">안녕하세요</div>
        <p>모달 내용은 어쩌고 저쩌고..</p>
        <div class="close-wrapper">
          <button id="close">닫기</button>
        </div>
      </div>
    </div>
    <script src="src/index.js"></script>
  </body>
</html>
  • id값 설정
  • src로 JavaScript연동
  • display 스타일 사용
style="display: none;" // 모달 열고 닫기

→ modal-wrapper : 모달이 열릴 때 모달의 background 를 감싸는 클래스

→ modal : 모달창을 감싸는 클래스

4-2) CSS

body {
  font-family: sans-serif;
}

.modal-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal {
  background: white;
  padding: 24px 16px;
  border-radius: 4px;
  width: 320px;
}

.modal-title {
  font-size: 24px;
  font-weight: bold;
}

.modal p {
  font-size: 16px;
}

.close-wrapper {
  text-align: right;
}
  • background: rgba(0, 0, 0, 0.5);뒷배경을 회색 50%로 지정 및 가운데 정렬
  • .modal border-radius 버튼 둥글게

4-3) JavaScript

import "./styles.css";

const open = document.getElementById("open");
const close = document.getElementById("close");
const modal = document.querySelector(".modal-wrapper");
open.onclick = () => {
  modal.style.display = "flex";
};
close.onclick = () => {
  modal.style.display = "none";
};
  • import "./styles.css"; css 연동
  • oneclick 이벤트
  • id 가 아닌 클래스로 DOM을 선택하고 싶을 땐 document.getElementsByClassName 또는 document.querySelector를 사용하면 된다. document.querySelector를 사용하여 class 값으로 DOM 을 선택할 때에는 텍스트 앞에 . 을 붙여주어야 한다.

완 성 ! 🥱

profile
불편함을 고민하는 프론트엔드 개발자, 박민철입니다.

0개의 댓글