connecting dots

Recap | Beginner Live Class Recap 4회차 본문

Live Class/Beginner

Recap | Beginner Live Class Recap 4회차

dearsuhyun 2024. 8. 22. 19:28

4-1. async와 await

callback hell & Promise hell을 개선하기 위해 사용

/* Callback Hell */
getData (function (x) {
  getMoreData (x, function (y) {
    getMoreData (y, function (z) {
      ...
    });
  });
});
/* Promise Hell */
fetch('https://example.com/api')
  .then(response => response.json())
  .then(data => fetch(`https://example.com/api/${data.id}`))
  .then(response => response.json())
  .then(data => fetch(`https://example.com/api/${data.id}/details`))
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

 

async

function 앞에 async를 붙이면 해당 함수는 항상 promise를 반환한다

함수를 정의할 때 async를 함수 앞에 붙이면, “이 함수는 비동기적인 함수이고 Promise를 반환한다”라고 선언하는 것 ! 

반환 값이 Promise 생성 함수가 아니어도 반환되는 값을 Promise 객체에 넣어주는 것이다.

 

await란 ?

--> promise가 처리될 때 까지 함수 실행을 기다린다(promise가 처리되면 그 결과와 함께 실행이 재개됨)

프라미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않는다 !

 

--> await는 async 함수 안에서만 동작한다 (일반 함수에는 사용 불가)

Promise를 반환하는 함수 앞에 await를 붙이면, 해당 Promise의 상태가 바뀔 때까지 코드가 기다린다.

즉 Promise가 성공 상태 또는 실패 상태로 바뀌기 전까지는 다음 연산을 시작하지 않는 것이다.

 

예제

함수를 호출하고, 함수 본문이 실행되는 도중에 (*)로 표시한 줄에서 실행이 잠시 '중단’되었다가 프라미스가 처리되면 실행이 재개된다.

이때 프라미스 객체의 result 값이 변수 result에 할당됩니다. 따라서 위 예시를 실행하면 1초 뒤에 '완료!'가 출력된다.

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("완료!"), 1000)
  });

  let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)

  alert(result); // "완료!"
}

f()

 

await는 최상위 레벨 코드에서 작동하지 않는다

 

최상위 코드: 함수나 블록 내부에 포함되지 않은, 스크립트의 가장 바깥쪽에 있는 코드들

// 최상위 레벨 코드에선 문법 에러가 발생함
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();

 

하지만 async 함수로 코드를 감싸면 최상위 레벨 코드에도 await를 사용할 수 있다

(async function func1() {
    const res = await fetch(url); // 요청을 기다림
    const data = await res.json(); // 응답을 JSON으로 파싱
    console.log(data);
})();

 

기본적으로 await는 async 함수 내에서 작동하지만,

자바스크립트의 ES2022(ECMAScript 2022) 버전부터는 모듈에서 최상위 await를 사용할 수 있다.

즉, import 또는 export 키워드를 사용하여 정의된 모듈 파일에서는 async 함수 없이도 최상위에서 await를 사용할 수 있다.

// Top Level에선 async funciton 정의없이 곧바로 await 키워드 사용이 가능하다
const res = await fetch(url); // 요청을 기다림
const data = await res.json(); // 응답을 JSON으로 파싱
console.log(data);
출처: https://inpa.tistory.com/entry/JS-📚-비동기처리-async-await [Inpa Dev 👨‍💻:티스토리]

 

주의 !
거의 대부분의 프로젝트에서 번들과 기타 빌드 과정을 거치기 때문에 await만 사용하는 경우,
해당 코드가 온전한 자바스크립트 모듈로 동작하지 않을 수 있다 !
그래서 대부분은 async 함수 안에서 사용하도록 명시하는 것이 안전하다 !
그럼에도 유용할 수 있다고 볼 수 있는 경우는 HTML에 자바스크립트 모듈을 직접 연결해서 사용하는 경우가 있다.
<script type="module" src="..." defer>​

 

4-2. URL 구성 요소

출처 : 박영웅 강사님 블로그 https://www.heropy.dev/p/QOWqjV

 

1. https://

프로토콜(Protocol):

https://는 웹 브라우저와 서버가 서로 데이터를 주고받을 때 사용하는 방법을 나타낸다.

여기서 https는 보안이 강화된 프로토콜로, 데이터를 암호화해서 안전하게 전송한다. http://는 보안이 없는 일반적인 프로토콜이다.

 

2. www.heropy.dev

도메인 이름(Domain Name):

www: 웹 사이트의 서브도메인(subdomain)입니다. 일반적으로 웹사이트의 기본 주소를 가리킨다.

heropy: 이 부분은 웹사이트의 이름. 사람들이 쉽게 기억할 수 있는 주소로, heropy는 이 웹사이트의 이름.

.dev: 최상위 도메인(TLD)이라고 하며, .com, .org, .net처럼 도메인 이름의 마지막 부분이다.

.dev는 개발자나 개발 관련 사이트에서 많이 사용한다.

전체 호스트 이름: www.heropy.dev는 이 웹사이트가 어디에 있는지를 나타내는 주소.

웹 브라우저가 이 주소를 보고 해당 사이트에 접속하게 됩니다.

 

3. /p/QOWqjV/

경로(Path):

이 부분은 웹 서버에서 특정 페이지나 파일의 위치를 나타낸다.

/p/QOWqjV/는 서버 내에서 특정 페이지나 자원을 찾기 위한 경로. 폴더나 파일의 위치를 지정한다고 생각하면 된다.

 

4. ?key=value&a=12&b=34

쿼리 문자열(Query String):

? 다음에 오는 부분이 쿼리 문자열입니다.

여기에는 서버에 추가적으로 전달하고 싶은 데이터를 담을 수 있습니다.

예를 들어, key=value, a=12, b=34와 같이 key=value 형식으로 데이터가 전달됩니다.

여러 개의 데이터는 &로 구분됩니다.

 

5. #h1-title

프래그먼트(또는 해시, Fragment/Hash):

# 뒤에 오는 부분은 웹 페이지 내의 특정 위치로 이동하게 해줍니다.

예를 들어, #h1-title은 페이지 내에서 idh1-title인 위치로 스크롤을 이동하게 합니다.

 

4-3. 메소드 체이닝(Method Chaining)

객체지향 프로그래밍에서 객체의 메소드를 연속적으로 호출할 수 있는 패턴

하나의 메소드가 호출된 후, 그 결과로 반환된 객체에 다시 메소드를 호출하는 방식으로 동작한다.

 

가능 예시

const number = [1, 2, 3]
number.forEach()
// number는 변수가 참조하는 이름일 뿐 
// [1, 2, 3].forEach()와 같음

 

function number() {
  return [1, 2, 3]
}
number().forEach()
// number 함수가 호출되면 그 자리에 [1, 2, 3]이 남음
// 항상 함수 호출되는 자리에는 어떠한 데이터가 남아있을까를 생각해야 함

 

const number = [1, 2, 3]
number().map(n => n).forEach()

// 1. number().map(n => n).forEach()
// 2. [1, 2, 3].map(n=>n).forEach()
// 3. [1, 2, 3].forEach()

 

불가능 예시

[1, 2, 3].forEach().map(n=>n)

// undefined.map(n => n)
// forEach()는 undefined를 반환하므로 undefined에 대해 map 메소드를 호출하려고 하면 오류 발생
// undefined는 map 메소드를 가지지 않기 때문에 뒤에 map 체이닝될 수 없음

 

cf.

forEach()

• 역할: 배열의 각 요소에 대해 주어진 함수를 실행합니다.
• 반환값: undefined

map()
• 역할: 배열의 각 요소에 대해 주어진 함수를 실행하고, 그 결과를 새로운 배열로 반환합니다.
• 반환값: 변형된 새로운 배열

 

4-4. instanceof 연산자

자바스크립트에서 객체가 특정 클래스나 생성자 함수에 의해 생성된 인스턴스인지 확인할 때 사용하는 연산자

왼쪽에 있는 객체오른쪽에 있는 클래스나 생성자 함수의 인스턴스인지 여부를 true 또는 false로 반환한다.

object instanceof Constructor(생성자함수 또는 클래스)

 

예제

class Car {
  constructor(model) {
    this.model = model;
  }
}

let myCar = new Car("Toyota");

console.log(myCar instanceof Car); // true

 

instanceof의 동작 방식

instanceof객체의 프로토타입 체인을 따라가면서, 해당 객체가 특정 생성자 함수 또는 클래스의 인스턴스인지 확인합니다. 즉, 객체의 프로토타입 체인이 생성자 함수의 prototype 속성을 포함하고 있는지를 확인하는 것입니다.

function Animal() {}
function Dog() {}

Dog.prototype = Object.create(Animal.prototype);

let myDog = new Dog();

console.log(myDog instanceof Dog);    // true
console.log(myDog instanceof Animal); // true

 

1. 객체와 프로토타입

객체(Object): 자바스크립트에서 모든 것은 객체로 이루어져 있습니다. 객체는 여러 속성(프로퍼티)과 메서드(함수)를 가질 수 있습니다.
프로토타입(Prototype): 자바스크립트의 모든 객체는 또 다른 객체인 “프로토타입”을 가지고 있습니다. 이 프로토타입 객체는 해당 객체가 사용할 수 있는 속성이나 메서드를 상속(물려받음)하는 역할을 합니다.

2. 프로토타입 체인이란?

프로토타입 체인은 여러 객체들이 서로 연결된 “연쇄 구조”를 의미합니다. 특정 객체에서 속성이나 메서드를 찾을 때, 자바스크립트는 그 객체에 해당 속성이 있는지 먼저 찾습니다. 만약 해당 속성이 없다면, 그 객체의 프로토타입에서 다시 찾아보는 과정을 거칩니다. 이 과정을 계속 반복해, 최상위 프로토타입 객체인 Object.prototype에 도달할 때까지 찾습니다.

이 과정에서 연결된 객체들의 연쇄 구조가 바로 프로토타입 체인입니다.
let person = {
  name: "Alice",
  greet: function() {
    console.log("Hello!");
  }
};

let student = {
  study: function() {
    console.log("Studying...");
  }
};

// student의 프로토타입을 person으로 설정
student.__proto__ = person;

console.log(student.name); // "Alice"
student.greet();           // "Hello!"
student.study();           // "Studying..."​

 

student 객체는 study라는 메서드만 가지고 있지만 student.__proto__를 person 객체로 설정했기 때문에, student는 이제 person의 속성(name)과 메서드(greet)를 사용할 수 있습니다.

3. 프로토타입 체인의 끝

모든 객체의 프로토타입 체인은 최종적으로 Object.prototype 객체에서 끝납니다. Object.prototype은 모든 객체의 최상위 프로토타입이므로, 이 객체에서 찾을 수 없는 속성이나 메서드는 정의되어 있지 않은 것으로 간주됩니다.
console.log(student.toString()); // "[object Object]"​

여기서 toString() 메서드는 student 객체나 person 객체에 정의되어 있지 않지만, 자바스크립트는 계속 프로토타입 체인을 따라 올라가 Object.prototype.toString 메서드를 찾아서 실행합니다.

 

 

- Array와 instanceof

arr는 배열(Array)이므로 arr instanceof Array true를 반환합니다.

arr자바스크립트의 모든 배열이 사실 객체(Object)이기도 하기 때문 arr instanceof Object true를 반환합니다.

let arr = [];

console.log(arr instanceof Array);    // true
console.log(arr instanceof Object);   // true

 

배열이 객체라는 의미

자바스크립트에서 배열도 사실 객체입니다. 배열은 객체처럼 키-값 쌍을 사용해 데이터를 저장하지만, 특별히 인덱스(숫자 키)를 사용해 데이터를 관리합니다.
let fruits = ["Apple", "Banana", "Cherry"];​
let fruits = {
  0: "Apple",
  1: "Banana",
  2: "Cherry"
};​

배열의 각 요소는 숫자(0, 1, 2…)를 키로 가지는 객체의 속성으로 저장됩니다. 그래서 자바스크립트에서는 배열도 객체의 일종이라고 할 수 있는 것입니다.

 

주의) 다른 스코프 (예: iframe 등에서)에서 생성된 객체에 대해 instanceof를 사용할 때는 예상치 못한 결과가 나올 수 있습니다. 이는 자바스크립트의 각 실행 환경(스코프)이 자체적인 내장 객체(예: Array, Object 등)를 가지고 있기 때문입니다.

let iframe = document.createElement('iframe');
document.body.appendChild(iframe);

let arr = new iframe.contentWindow.Array();

console.log(arr instanceof Array); // false
console.log(arr instanceof iframe.contentWindow.Array); // true

 

arr1은 메인 페이지의 Array 생성자로부터 생성된 배열입니다.

arr2iframe 내부의 Array 생성자로부터 생성된 배열입니다.

arr1 instanceof Arraytrue를 반환합니다. 이는 arr1이 메인 페이지의 Array 생성자로부터 생성되었기 때문입니다.

그러나 arr2 instanceof Arrayfalse를 반환합니다. 이는 arr2iframe 내부의 Array 생성자로부터 생성되었기 때문에, 메인 페이지의 Array와는 다른 생성자이기 때문입니다.

 

다른 스코프란 자바스크립트에서 실행 환경이나 컨텍스트가 서로 다른 경우를 의미합니다. 예를 들어, 웹 페이지에서 실행되는 자바스크립트 코드와 그 안에 삽입된 iframe 내부에서 실행되는 자바스크립트 코드는 서로 다른 스코프에서 실행됩니다. 각 스코프는 자바스크립트의 내장 객체(예: Array, Object, Function 등)를 자체적으로 관리합니다.

스코프란?
스코프는 변수가 정의되고 접근할 수 있는 범위를 의미합니다. 예를 들어, 함수 안에서 선언된 변수는 그 함수 내에서만 접근할 수 있는 것처럼, 특정 실행 환경에서 실행되는 코드들은 그 환경의 스코프를 따릅니다.

iframe과 다른 스코프
iframe은 HTML 문서 안에 또 다른 HTML 문서를 삽입할 수 있는 요소입니다. iframe은 독립적인 문서로 취급되며, 해당 iframe 내부에서 실행되는 자바스크립트 코드도 메인 페이지와는 독립적인 스코프를 가집니다.

예를 들어, 메인 페이지에서 사용하는 Array 객체와 iframe 내부에서 사용하는 Array 객체는 서로 다른 Array 생성자를 참조할 수 있습니다. 이는 각각의 스코프가 자체적으로 내장 객체를 관리하기 때문입니다.

https://ko.javascript.info/async-await

 

async와 await

 

ko.javascript.info

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await

 

📚 자바스크립트 Async/Await 개념 & 문법 정복

자바스크립트 비동기 처리 3가지 방식 자바스크립트는 싱글 스레드 프로그래밍 언어기 때문에 멀티 작업을 하기 위해선 비동기 처리 방식이 자주 쓰인다. 비동기 처리는 백그라운드로 동작되기

inpa.tistory.com

 

반응형