[CodeStates-Section2]U3.JS/Node 비동기

hameee·2022년 11월 26일
0

CodeStates_Frontend_42기

목록 보기
16/39

1.후기

 비동기의 방대한 개념을 머리에 쏟아붓느라 눈코 뜰 새 없이 바빴던 날들이었다. 생소한 단어, 헷갈리는 개념들로 애를 먹었지만 계속 들여다보니 잠들기 1시간 전쯤에 번뜩 이해되는 순간들이 있었다. 물론 다음날 "이게 아니라고...? 이게 이거라고...?" 하며 몇 번의 교정 과정을 거치긴 했지만 처음 접했을 때보다는 비동기와 가까워진 느낌이다. 지금 이해가 부족한 부분은 미래의 나에게 넘기는 걸로... 더 보면 토할 것 같다.

2.새롭게 알게 된 것

Chapter1. 고차함수 리뷰
Chapter2. 비동기
-1. 비동기 호출
 -2. 비동기 JavaScript
 -3. Callback
 -4. Promise, async/await
 -5. 타이머 API

Chapter3. Node.js
-1. Node.js 모듈 사용법
 -2. Node.js 공식 문서 가이드

Chapter4. fetch API
-1. fetch를 이용한 네트워크 요청

<Chapter1. 고차함수 리뷰>

1) 고차 함수, 콜백 함수, 커링 함수
고차 함수: 콜백 함수를 받는 함수 or 커링 함수
콜백 함수: 다른 함수의 전달 인자로 전달되는 함수
커링 함수: 함수를 리턴하는 함수

<Chapter2. 비동기>

1. 비동기 호출

synchronous(동기): response를 돌려주기 전까지 다른 request들은 block되는 방식
asynchronous(비동기): request를 보내고 해당 request에 대한 response를 처리하지 못했음에도, 다른 request를 받을 수 있는 방식(non-block)

동기 예시 코드.

function waitSync(ms) {
  let start = Date.now();
  let now = start;
  while (now - start < ms) {
	now = Date.now();
  }
} // 현재 시각과 시작 시각을 비교하며 ms 범위 내에서 무한 루프를 도는 blocking 함수

function drink(person, coffee) {
  console.log(person + '는 ' + coffee + '를 마십니다');
}

function orderCoffeeSync(coffee) {
  console.log(coffee + '가 접수되었습니다');
  waitSync(4000);
  return coffee;
}

let customers = [{
  name: 'Steve',
  request: 'Caffe Latte'
}, {
  name: 'John',
  request: 'Americano'
}];

// call synchronously
customers.forEach(function(customer) {
  let coffee = orderCoffeeSync(customer.request);
  drink(customer.name, coffee);
});

비동기 예시 코드.

function waitAsync(callback, ms) {
  setTimeout(callback, ms);
} // 특정 시간 이후에 callback 함수가 실행되게끔 하는 브라우저 내장 기능

function drink(person, coffee) {
  console.log(person + '는 ' + coffee + '를 마십니다');
}

let customers = [{
  name: 'Steve',
  request: 'Caffe Latte'
}, {
  name: 'John',
  request: 'Americano'
}];

function orderCoffeeAsync(menu, callback) {
  console.log(menu + '가 접수되었습니다');
  waitAsync(function() {
    callback(menu);
  }, 4000);
}

//call asynchronously
customers.forEach(function(customer) {
  orderCoffeeAsync(customer.request, function(coffee) {
    drink(customer.name, coffee);
  });
});

2. 비동기 JavaScript

장점: 비동기의 실행 시간이 더 빠름
단점: 실행 순서 랜덤
해결: Callback 함수, Promise, async/await

3. Callback

비동기 함수의 순서를 제어하기 위해 사용

// 일반 비동기 함수
const print = (string) => {
  setTimeout(
    () => {
      console.log(string)
    }, Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  printString("A")
  printString("B")
  printString("C")
}

printAll(); // 랜덤하게 출력
// 콜백을 사용하여 순서 제어한 비동기 함수
const print = (string, callback) => {
  setTimeout(
    () => {
      console.log(string);
      callback();
    }, Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  printString("A",() => {
  	printString("B", () => {
	  printString("C", () => {})
    })
  })
}

printAll();// A B C 순서대로 출력

비동기 함수(콜백X): WebAPI에서 처리 후 실행 끝나면 결괏값 반환
비동기 함수(콜백O): setTimeout에서 설정한 시간 종료 후 Queue에 넘겨짐. Stack이 비었을 때 stack으로 옮겨 실행(Stack이 비어있지 않다면 setTimeout에서 설정한 시간보다 더 걸릴 수 있음)
참고 영상: https://www.youtube.com/watch?v=j0Viy3v97gY

참고. Stack, Queue

Stack: Last In First Out(나중에 들어온 게 먼저 나감)
Queue: 처리할 메시지의 대기열, 각의 메시지에는 메시지를 처리하기 위한 함수가 연결돼있음. First In First Out(먼저 들어온 게 먼저 나감).

콜백 함수 에러.

이벤트 루프 영상: https://www.youtube.com/watch?v=8aGhZQkoFbQ
영상 내 사이트: http://latentflip.com/loupe

장점: 비동기 함수 순서 제어 가능
단점: callback hell, 에러 처리 번거로움
해결: Promise

4. Promise, async/await

1) Promise

콜백 함수를 인자로 받지 않고 "executor를 인자로 받는 Promise 인스턴스 객체"를 리턴하는 함수 작성(executor의 인자가 resolve, reject)
->executor가 성공적으로 실행되면 resolve(), 실패하면 reject()가 실행됨(둘 중 하나만 실행)
->resolve()/reject()의 결과값을 .then(onfulfilled, onrejected)의 onfulfilled(성공 콜백)/onrejected(실패 콜백)의 인자로 받아 실행됨(둘 중 하나만 실행)
->.then의 반환 값: 새로운 Promise 객체, onfulfilled/onrejected의 반환 값: 새로운 Promise 객체의 resolve/reject의 인자로 들어감
->resolve의 인자로 들어온 경우: 다음 .then의 onfulfilled의 인자로 들어간 후 반환할 새로운 Promise의 상태 판정, reject의 인자로 들어온 경우: .then의 onrejected 인자로 들어간 후 새로운 Promise의 reject 인자로 들어감

참고. Promise 객체의 states와 result
states: pending -> settled(fulfilled or rejected))
result: undefined -> resolve() 값 or reject() 값

참고. Promise.prototype.then(onFulfilled, onRejected)
Promise.prototype.then(undefined, onRejected) = Promise.prototype.catch(onRejected)

장점:.then으로 이어 나가 callback hell을 막을 수 있음, 에러 처리 한 번에 할 수 있음
단점: Promise hell이 생길 수 있음
해결 return을 사용해 Promise Chaining 형성

Promise Hell.

Promise chaining.

// Promise chaining 예시
const printString = (string) => {
  return new Promise ((resolve, reject) => {
    setTimeout(() => {
      console.log(string)
      resolve()
    }, Math.floor(Math.random() * 100) + 1);
  })
}
const printAll = () => {
  printString ("A")
  .then(() => {
    return printString("B")
  })
  .then(() => {
    return printString("C")
  })
}
printAll()// A B C 순서대로 출력

참고. Promise.all()
-요소 전체가 Promise인 배열(엄밀히 따지면 이터러블 객체이지만, 대개는 배열임)을 받고 새로운 프라미스를 반환.
-배열 안 Promise가 모두 처리되면 새로운 Promise가 이행되는데, 배열 안 Promise의 결괏값을 담은 배열이 새로운 Promise의 result가 됨.
-Promise.all 에 전달되는 Promise가 어느 하나라도 거부되는 순간 모든 요청이 거부됨. 따라서 직전 Promise가 거부된 직후에, 그다음 요청이 then 메서드를 따라가는 것이 아니라, 모든 Promise 요청이 거부되면서 catch 메서드를 따라가게 됨.

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

참고. Promise.all & map
Promise.all -> 배열의 모든 요소가 settled 상태(fulfilled or rejected)가 될 때까지 기다려줌
map -> Promise의 settled 상태가 되기를 기다리지 않고 pending 상태로 반환
해결 -> Promise.all(map()), map을 Promise.all로 감싸 배열의 모든 요소가 settled 상태가 될 때까지 기다리게 함

참고 사이트:
(Promise)

(Promise.all, map)

2) async/await
Promise의 syntactic sugar(문법 설탕, 사람 또는 작성하는 사람이 편하게 디자인된 문법)

5. 타이머 API

setTimeout(callback, millisecond): 일정 시간 후에 함수를 실행
clearTimeout(timerId): setTimeout 타이머를 종료
setInterval(callback, millisecond): 일정 시간의 간격을 가지고 함수를 반복적으로 실행
clearInterval(timerId): setInterval 타이머를 종료

<Chapter3. Node.js>

1. Node.js 모듈 사용법

Node.js: 비동기 이벤트 기반 JavaScript 런타임
모듈: 어떤 기능을 조립할 수 있는 형태로 만든 부분.

1) Node.js 내장 모듈(built-in module)
-사용법: require

const fs = require('fs'); // 파일 시스템 모듈을 불러옵니다
const dns = require('dns'); // DNS 모듈을 불러옵니다

-내장 모듈 예시
fs(File System) 모듈: PC의 파일을 읽거나 저장하는 등의 일을 할 수 있게 도와줌.

파일을 읽을 때: readFile
파일의 저장: writeFile

2) 3rd-party 모듈(3rd-party module)
-정의: 해당 프로그래밍 언어에서 공식적으로 제공하는 빌트인 모듈(built-in module)이 아닌 모든 외부 모듈
-사용법: npm 사용

npm install underscore // node_modules에 underscore 모듈 설치
const _ = require('underscore'); // 내장 모듈처럼 사용

2. Node.js 공식 문서 가이드

1) fs.readFile(path[, options], callback)

출처: https://nodejs.org/dist/latest-v16.x/docs/api/fs.html#fsreadfilepath-options-callback

-path

파일 이름을 전달인자로 받음. 네 가지 종류의 타입을 넘길 수 있지만 일반적으로 문자열(<string>)의 타입을 받음.

fs.readFile('/etc/passwd', ..., ...)

-option(대괄호 있으므로 선택적 전달인자)

문자열 또는 객체 형태로 받음. 문자열로 전달할 경우 인코딩을 받음.

// <options가 문자열인 경우>
// /etc/passwd 파일을 'utf8'을 사용하여 읽습니다.
fs.readFile('/etc/passwd', 'utf8', ...);

// <options가 객체인 경우>
let options = {
  encoding: 'utf8', // utf8 인코딩 방식으로 엽니다
  flag: 'r' // 읽기 위해 엽니다
}

// /etc/passwd 파일을 options를 사용하여 읽습니다.
fs.readFile('/etc/passwd', options, ...)            

-callback

함수 형태로 받음. 파일을 읽고 난 후에 비동기적으로 실행되는 함수.
매개변수: err, data
err: null(정상 작동 시)/Error(에러 발생 시)
data(파일 내용): 문자열(options에서 encoding을 받은 경우)/Buffer(options에서 encoding을 받지 않은 경우)

encoding에 따른 data 반환 예시.

options가 문자열

options가 객체

encoding 안 받은 경우

2)
javascript: single-thread
node.js, 브라우저: muli-thread

3)
_filename: 현재 실행 중인 파일 경로
_dirname: 현재 실행 중인 폴더 경로

4) JSON(JavaScript Object Notation)
-경량의 DATA-교환 형식
-사람이 읽고 쓰기에 용이하며, 기계가 분석하고 생성함에도 용이

json.parse(): json 문자열 -> javascript 값, 객체 반환
json.stringify():javascript 값, 객체 -> json 문자열 반환
Response.json(): body text -> Promise 반환(resolve의 값: JavaScript 형태)

참고 사이트:
https://www.json.org/json-ko.html
https://moneytech.kr/42

<Chapter4. fetch API>

1. fetch를 이용한 네트워크 요청

-fetch API는 특정 URL로부터 정보를 받아오는 역할을 함. 비동기적으로 이루어짐.
-Node.js 환경에는 fetch API가 내장 모듈로 제공되지 않음.

2. axios

Axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리. Axios는 Fetch API보다 사용이 간편하면서 추가적인 기능들이 포함되어 있음.

axios.get("url"[,config])

참고. fetch & axios

예시.
Get 요청: 읽기(게시판에서 글 목록이나 내용을 보여주는 경우)

// Promise ver
fetch('https://koreanjson.com/users/1', { method: 'GET' })
  .then((response) => response.json())
  .then((json) => console.log(json))
  .catch((error) => console.log(error));

// axios ver
axios
  .get('https://koreanjson.com/users/1')
  .then((response) => {
    const { data } = response;
    console.log(data);
  })
  .catch((error) => console.log(error));

Post 요청: 수행(게시판에서 글의 내용을 저장, 수정하는 경우)

// Promise ver
fetch('https://koreanjson.com/users', {
  method: 'POST',
  headers: {
    // JSON의 형식으로 데이터를 보내준다고 서버에게 알려주는 역할
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ nickName: 'ApeachIcetea', age: 20 }),
})
  .then((response) => response.json())
  .then((json) => console.log(json))
  .catch((error) => console.log(error));

// Axios ver
axios
  .post('https://koreanjson.com/users', { nickName: 'ApeachIcetea', age: '20' })
  .then((response) => {
    const { data } = response;
    console.log(data);
  })
  .catch((error) => console.log(error));

참고 사이트(Get, Post): https://velog.io/@songyouhyun/Get%EA%B3%BC-Post%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%A5%BC-%EC%95%84%EC%8B%9C%EB%82%98%EC%9A%94

0개의 댓글