221018.til

Universe·2022년 10월 18일
0

study

목록 보기
36/49

Promise

직역하자면 ‘약속’ 이라는 뜻.
자바스크립트의 promise 는 코드 디자인 패턴이다.

기본 형태는 이렇다.

let 약속 = new Promise(function(res,rej){

});

약속
	.then(function(){
	프로미스가 성공을 리턴할 경우 실행할 코드
})
	.catch(function(){
	프로미스가 실패를 리턴할 경우 실행할 코드
})
	.finally(function(){
	프로미스의 성공, 실패 여부와 상관없이 실행할 코드
})

promise 는 성공과 실패를 판정하는 역할을 한다.
어떤 조건을 만족하면 성공, 조건을 달성하지 못했다면 실패를 리턴하도록 설계한다.
res는 resolved, rej는 reject 의 약자로, 프로미스의 파라미터를 작명할 수 있으나 관습적으로 쓴다고 한다.
판정을 내리는건 프로미스 내부의 콜백함수의 실행으로써 결정된다.

함수 내부에서 예를들어,

let 약속 = new Promise(function(res,rej){
	res();
});

이런식으로 프로미스를 디자인했다면 이 프로미스는 반드시 성공을 리턴한다.
반드시 약속.then 의 함수를 실행한다는 뜻.
반대로 rej() 으로 디자인했다면 반드시 실패를 리턴하므로 .catch 부분의 함수를 실행하게 된다.

아래의 예시를 보자.

let 약속 = new Promise(function(res,rej){
	매우 어려운 연산을 진행하는 함수();
	rej();
});

원래는 매우 어려운 연산을 진행하는 함수가 성공 했을 때 성공을 판정해주어야 하는데,
실수로 rej() 를 실행하여 실패 를 리턴하는 프로미스가 만들어졌다고 가정해보자.
그렇다면 매우 어려운 연산을 진행하는 함수의 성공 / 실패여부 더 나아가
실행여부 조차 상관없이 해당 프로미스는 반드시 실패를 리턴한다.
이는 본래의 설계와 어긋나게 되므로 이런 부분은 프로미스를 사용할 때 주의해야 할 점이다.

let 약속 = new Promise(function(res,rej){
	let 전달 = 매우 어려운 연산을 진행하는 함수();
	res(전달);
});

약속.then(function(결과){
	console.log(결과);
})

이런식으로 프로미스 안에서 res 혹은 rej 함수의 인자로 특정 값을 집어넣게되면
해당 값을 then 혹은 catch 내부에서 전달받아 사용할 수 있게된다.
프로미스의 또 다른 특징으로 상태를 가변적으로 가진다는 점에 있다.

var pm = new Promise(function (res, rej) {
        setTimeout(function () {
          res();
        }, 10000);
      });

      pm.then(function () {
        console.log(2);
      });

// 콘솔에 2가 출력되기 전 pm : Promise {<pending>}
// 콘솔에 2가 출력된 후(성공을 리턴할 경우) pm : Promise {<fulfilled>: undefined}
// 실패를 리턴할 경우 pm : Promise {<rejected>: undefined}

프로미스는 출력해보면 오브젝트 형식과 비슷하게 되어있는데,
대기중일때는 pending, 성공일 때는 fulfilled, 실패일 때는 rejected 의 상태를 가진다.

주의할 점. 프로미스는 동기적 처리를 비동기적 처리가 가능하게 해주는 문법이 아니다.
콜백함수의 대체품으로써 단지 코딩 디자인 패턴 중 하나일 뿐이다.
콜백함수를 써도 프로미스가 할 수 있는 모든 코드를 수행할 수 있다.
다만, 자바스크립트의 fetch()나 jQuery 의 $.ajax() 같이 프로미스를 리턴하는 함수 처럼,
함수의 설계가 프로미스를 전제로 하는 경우가 많으므로 제대로 쓰는 방법을 익히는게 중요할 것 같다.

Promise chaining

프로미스도 콜백함수 처럼 꼬리를 물고 물리는 느낌을 주는 코드를 짤 수 있다.
풀어서 설명하자면,

  1. 첫번째 프로미스가 성공 / 실패여부를 판단한다.
  2. 성공했다면 then으로
  3. then 자리에 두번째 프로미스로 성공 / 실패여부 판단.
  4. 성공했다면 다음 then으로.

코드로 보면 이런 느낌이다.

		  function ajax요청(url) {
        return new Promise(function (res, rej) {
          $.get(url).done(function (e) {
            res(e);
          });
        });
      }

			let 약속 = ajax요청("ajax 파일이 있는 첫번째 url");

      약속.then(function (e) {
        console.log(e);
        return ajaxPlease("ajax 파일이 있는 두번째 url");
      }).then(function (e) {
        console.log(e);
      });

새로운 프로미스를 리턴하는 함수를 설계한다.
함수의 역할은 url 을 파라미터로 받고 해당 url에서 ajax요청이 성공했다면 데이터를 넣어서
성공(res()) 을 리턴하는 프로미스.
요청을 받아오면 첫번째 약속.then 으로 이동.
함수를 실행한 후리턴으로 새로운 프로미스를 만든다.
그 프로미스에 .then 을 달아서 새로운 성공여부를 판별하는 식.
이렇게 설계하면 좀 더 직관적으로 순서를 부여할 수 있다.

Async / await

무려 ES8 문법.
promise 의 대체품이다.
예를들어,

async function func(){
	let result = 어려운계산...;
	return result;
}

func().then(function(result){
	어려운 계산이 성공하면 계산결과로 실행되는 함수...;
})

async 키워드는 function 앞에 붙혀 사용하는데, 함수 실행후에 promise 오브젝트로 리턴한다.
따로 new Promise() 처럼 설계하지 않아도 프로미스의 기능을 하기때문에
바로 .then 키워드로 성공시 실행할 함수를 설계할 수 있다.
다만, 이러한 경우에는 오로지 성공했을 경우만 설계할 수 있기때문에 주의.
return Promise.reject() 로 실패판정을 해주는 방법도 있긴하다.

async function func() {
        let prom = new Promise((res, rej) => {
          let result = 100;
          res(result);
        });
        let data = await prom;
        console.log(`해당 결과값은 ${data}입니다.`);
      }
      func();

async 와 함께 쓰는 await 문법.
await 는 async 내부의 프로미스가 끝날 때 까지 기다려준다.
성공했을 경우 res() 로 데이터를 전달받아 쓸 수 있다.

prom.then(function(data){
	console.log(`해당 결과값은 ${data}입니다.`);
})

await 를 then 으로 바꿔보면 이런 식의 코드가 나온다.
await 는 프로미스가 실패를 판정했을 때( rej 를 리턴할 때) 멈춘다.
말 그대로 함수를 정지한다.
따라서 실패판정이 났을 경우에 어떤식으로 에러를 해결할 지 명시해주어야 한다.

async function func() {
        let prom = new Promise((res, rej) => {
          let result = 100;
          rej(result);
        });
       try {
				let data = await prom;
        console.log(`해당 결과값은 ${data}입니다.`);
			} catch {
				console.log(`데이터를 받아올 수 없습니다.`)
			} 	
     }
      func();

그럴 때 쓸 수 있는 문법이 try, catch.
쉽게 생각해서

try { 잘 되면 이거 } catch { 안되면 이거 }

이런식의 운용이다.

for in / for of

자바스크립트에서 반복문의 역할은 두가지가 있다.
첫째로 코드를 반복해서 실행하고 싶을 때,
두번째로는 자료형에 담긴 데이터를 하나하나 꺼내서 쓰고싶을때.
for in 반복문은 오브젝트를 꺼내서 쓰고 싶을때 쓸 수 있는 반복문이다.

let obj = {name:'soo', age:20}

for(let e in obj){
	console.log(obj[e])
}

여기서의 obj 는 상단의 오브젝트를 뜻하며, e 는 오브젝트 내부의 키 값을 대신한다.
반복문은 해당 오브젝트의 key 값의 갯수만큼 반복한다.
for in 반복문의 특징으로는 해당 오브젝트의 부모요소의 prototype에 저장된 요소도 출력해 준다는 점이다.

아래의 예시를 보자.

class A {
        constructor(firstName, age) {
          this.firstName = firstName;
          this.age = age;
        }
      }
      A.prototype.lastName = "park";
      let B = new A("jaerong", 10);

      for (let e in B) {
        console.log(`${e} = ${B[e]}`);
      }

이름과 나이를 받는 class A 를 만들었다.
class A의 프로토타입은 ‘park’ 이라는 성을 갖는다.
그리고 ‘jaerong’ 이라는 이름과 10 이라는 나이를 가진 오브젝트 하나를 A에서 상속받아 만들었다.
그 후 for in 반복문을 돌려보면 분명히 lastName 속성을 정의해준 적 없음에도 같이 출력이 된다.

이러한 경우에

class A {
        constructor(firstName, age) {
          this.firstName = firstName;
          this.age = age;
        }
      }
      A.prototype.lastName = "park";
      let B = new A("jaerong", 10);

      for (let e in B) {
				if(B.hasOwnProperty(e)){
					console.log(`${e} = ${B[e]}`);
				}
      }

이런식으로 hasOwnProperty 키워드를 사용하여 조건문을 만들면
직접 가지고 있는 key 값만 출력해줄 수 있다.

for of 반복문도 알아보자.

let arr = [1,2,3,4,5];
for(let e of arr){
	console.log(e)
}

배열에서 사용하면 forEach 반복문과 매우 유사해보인다.
for of 반복문은 iterable 자료형에만 적용가능한 반복문이다.
iterable 은 직역하자면 ‘반복 가능한’ 이라는 뜻을 담고 있는 것 같다.
바꿔 말하자면 반복이 가능한 그 어떤 자료형에도 사용할 수 있는 반복문 이라는 것이다.
이 점은 굉장히 훌륭하다.
예를들어, 전에 배웠던 arguments 자료형의 경우 array 자료형이 아니라서 forEach 를 적용할 수 없었다.

function func(a,b,c){
	console.log(arguments);
	for(let i=0; i < arguments.length; i++) {
		console.log(arguments[i]);	
	}
}

이런식으로 썼던 반복문을

function func(a, b, c) {
        for (let e of arguments) {
          console.log(e);
        }
      }

이런 식으로 바꿀 수 있다.

Nodelist 에도 적용할 수 있는데,
Nodelist는 예를들자면 querySelectorAll 로 찾아온 요소들이 담긴 자료형이다.
이런식의 활용이 가능하다.
여러개의 버튼이 있는데 버튼을 누르면 해당버튼의 백그라운드를 바꿔주는 기능을 추가하고 싶다면

const $buttons = document.querySelectorAll(".buttons");

			for (let i = 0; i < $buttons.length; i++) {
        $buttons[i].addEventListener("click", function () {
          this.style.background = "pink";
        });
      }

이렇게 짜거나,

			$buttons.forEach((e) => {
        e.addEventListener("click", function () {
          this.style.background = "pink";
        });
      });

이런식으로 짰어야 했는데

      for (e of $buttons) {
        e.addEventListener("click", function () {
          this.style.background = "pink";
        });
      }

한가지 방법이 더 늘었다.

Symbol 자료형

ES6 새로운 자료형.

let sym = Symbol('explain');

이런식으로 선언한다.

만든 심볼 자료형을

let sym = Symbol('explain');
let man = { name:'soo'}
man[sym] = `hello, i'm symbol`

console.log(man)
// {name: 'soo', Symbol(explain): "hello, i'm symbol"}

오브젝트에 자료를 추가하듯 부여할 수 있다.
심볼 자료형은 for in 반복문으로 찾을 수 없기 때문에 비밀스러운 key 값으로 사용할 수 있다.

let man = { name:'soo', [sym] : `i'm symbol!`}

오브젝트 자료형에 직접 대괄호를 사용하여 심볼을 부여할 수도 있다.
이 심볼의 설명부분을 같게 만들어도 같은 심볼 취급이 되지 않는데,

let a = Symbol('explain');
let b = Symbol('explain');

console.log(a === b)
// false

Symbol.for() 키워드를 사용하면 같은 취급을 해준다.
정확히는 explain 부분을 위에서부터 쭉 찾아서 검색한 뒤 위치를 리턴한다고 한다.

let a = Symbol.for('explain');
let b = Symbol.for('explain');

console.log(a === b)
// true

Map / Set

Map 자료형은 오브젝트와 마찬가지로 key, value 를 갖는 자료형이다.

let man = new Map();
man.set('name','soo')

console.log(man)
// Map(1) {'name' => 'soo'}

오브젝트 자료형과 다른점은 화살표로 저장된다는 점이다.
따라서 자료의 연관성을 표현하기 위해 사용한다.
또한 오브젝트 자료형은 key 값으로 오직 문자만 사용 가능했는데
Map 자료형은 key, value 값 모두 어떤 자료형도 쓸 수 있다.

man.get('name');
// 'soo'
man.size
// 1
man.delete('name');
// true, Map(0) {size: 0}

get 메소드로 value 를 가져올 수도,
size 로 해당 자료형의 키의 갯수를 알아볼 수도,
delete 로 삭제할 수도 있다.

Set 자료형은 배열과 비슷하다.
중복을 허용하지 않는 배열을 만들 때 유용하다.

let mans = new Set(['soo', 'soo', 'kim', 'lee'])

console.log(mans)
// Set(3) {'soo', 'kim', 'lee'}

Set 자료형을 array 로 만들고 싶다면

let mansArr = [...mans]
console.log(mansArr)
// (3) ['soo', 'kim', 'lee']

이렇게 사용할 수 있다.

Web components

커스텀 HTML 태그를 만들 수 있다.

자바스크립트로 구현할 수 있는 브라우저 기본 기능.

class 클래스 extends HTMLElement {
	connectedCallback(){
		let tag =	document.createElement('label');
		this.appendchild(tag)
		// 혹은
		this.innerHTML = `
			<label> 와 </label><input>
		`
	}
}
customElements.define('작명', 클래스);

HTML 태그를 묶어서 컴포넌트 단위로 쓸 수 있다.
innerHTML 로 안에 직접 넣을 수도 있고 appendchild 로 생성하고 넣을수도 있다.
appendchild 가 속도적인 측면에서는 조금 더 낫다고 한다.
일종의 함수 문법과 비슷하다.
HTML 의 중복을 제거할 수 있고, 다른 페이지에서도 재활용 할 수 있다.

파라미터 문법도 구현할 수 있다.

	
		<div class="container"">
      <custom-input option="이름"></custom-input>
      <custom-input option="나이"></custom-input>
      <custom-input option="취미"></custom-input>
    </div>

    <script>
      class OptionInputClass extends HTMLElement {
        connectedCallback() {
          let option = this.getAttribute("option");
          this.innerHTML = `<label>${option}<lebel><input>`;
        }
      }
      customElements.define("custom-input", OptionInputClass);
    </script>

getAttribute 키워드를 사용하여 현재 요소의 어떤 정의된 속성(Attribute)을 가져올 수 있다.
따라서 option 을 파라미터로 쓰는 함수를 구현할 수 있다.
static get observedAttributes() 함수나,
attributeChangedCallback() 함수로
attribute 가 변경될 때 특정 코드를 실행하는 기능도 구현할 수 있다.
이런 요소들에서 영감을 받아 조금 더 쉽고 확장된 기능을 사용할 수 있게 만들어주는 라이브러리가
바로 React, Vue 같은 것들이다.







강의 총평

강의 내용을 이해하는데 시간이 생각보다 오래걸려서 계획했던 시간보다 조금 더 걸렸다.
확실히 문법공부는 무언가를 만든다거나 하는 일보다는 지루하지만
그럼에도 새로운 내용들을 많이 알게되어 좋았다.
특히 promise 나 async 같은 문법은 과거에 기능구현을 할 때, 필요했는데 몰라서 쓰지 못했던 부분이었다.
그때는 어떻게 콜백함수를 막 섞어서 구현했던 것 같다.
당시에는 순서가 왜 뒤바뀌는지, 어디가 틀린건지, 틀렸다면 어떻게 고쳐야 하는지
전혀 알지 못했으므로 많이 답답했던 기억이 있다.

복습을 많이했다. 조금만 더 제대로 이해하고 싶은데~ 하는 막연한 불안 때문이다.
도움이 많이 됐던 것 같다.

내일부터는 프로젝트도 있을테고 조금 바빠질 테지만
일정이 시작하기 전에는 React의 기초정도는 끝내고 갈 수 있었으면 좋겠다.
web component 기능을 직접 써보니까 이거 좀 흥미롭고 재미있다.
왜 react, vue 같은 라이브러리를 찾는지 직접 느껴보고 싶다.
오랜 숙원을 끝낸 기분이라서 후련하다.
🥰

profile
Always, we are friend 🧡

0개의 댓글