[모던 자바스크립트 Deep Dive] - 41~44장

Lee Jeong Min·2021년 10월 23일
1
post-thumbnail

이 글은 책 모던 자바스크립트 41장 ~ 44장을 읽고 정리한 글입니다.

41장 - 타이머

호출 스케줄링

일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하려면 타이머 함수 사용! --> 호출 스케줄링

JS에서는 이를 위해 setTimeout, setInterval, clearTimeout, clearInterval 을 제공한다.

setTimeout: 타이머가 만료되면 단 한번 호출,
setInterval: 타이머가 만료될 때마다 반복 호출
clearTimeout, clearInterval: 위 함수들의 각각의 타이머 제거에 사용

JS엔진은 싱글 스레드 이기 때문에(단 하나의 실행 컨텍스트) 타이머 함수는 비동기 처리 방식으로 동작한다.

타이머 함수

setTimeout / clearTimeout

const timeoutID = setTimeout(func|code[, delay, param1, param2, ...]);

delay 인수는 생략한 경우 기본값 0이 지정되며, delay 시간이 설정된 타이머가 만료되면 콜백 함수가 즉시 호출되는 것이 보장받는 것은 아니고 4ms 이하인 경우 최소 지연 시간 4ms가 지정된다.

setTimeout 함수는 타이머를 식별할 수 있는 고유한 id를 반환한다.

id의 반환값

브라우저: 숫자
Node.js: 객체

const timerId = setTimeout(() => console.log('HI!'), 1000);

clearTimeout(timerId);

setInterval / clearInterval

const timerId = setInterval(func|code[, delay, param1, param2, ...]);

setInterval 함수도 마찬가지로 id를 반환하는데 브라우저에서 숫자, Node.js에서 객체를 반환한다.

let count = 1;

const timerId = setInterval(() => {
  console.log(count);
  if (count++ === 5) clearInterval(timerId);
}, 1000);

디바운스와 스로틀

scroll, resize, input, mousemove 같은 이벤트는 짧은 시간 간격으로 연속해서 발생 => 성능에 문제를 줌

디바운스와 스로틀은 짧은 시간안에 여러번 발생하는 이벤트들을 그룹화해서 과도한 이벤트 핸들러의 호출을 방지하는 프로그래밍 기법이다.

    const debounce = (callback, delay) => {
      let timerId;

      return event => {
        if (timerId) clearTimeout(timerId);
        timerId = setTimeout(callback, delay, event);
      };
    };

    const throttle = (callback, delay) => {
      let timerId;

      return event => {
        if (timerId) return;
        timerId = setTimeout(
          () => {
            callback(event);
            timerId = null;
          },
          delay,
          event
        );
      };
    };

event를 사용하지 않더라도 이벤트를 명시적으로 넘겨줌

디바운스

짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트가 호출되도록 함

resize 이벤트 처리나 input 요소의 입력된 값으로 ajax 요청하는 입력 필드 자동완성, 버튼 중복 클릭 방지 처리등에 사용한다.

스로틀

짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 최대 한 번만 호출되도록 함.

delay 시간이 경과했을 때 이벤트가 발생하면 콜백 함수를 호출하고 새로운 타이머를 재설정한다. (delay 시간 간격으로 콜백함수가 호출된다)

--> scroll 이벤트 처리나 무한 스크롤 구현 등에 유용하게 사용된다.

디바운스나 스로틀을 사용할 거면 Underscore나 lodash의 디바운스, 스로틀 함수를 사용하는 것을 권장한다.


42장 - 비동기 프로그래밍

동기 처리와 비동기 처리

JS엔진은 하나의 실행 컨텍스트(콜 스택) 스택을 갖기 때문에 2개 이상의 함수를 동시에 실행할 수 없다. --> 싱글 스레드

이 싱글 스레드는 한 번에 하나의 태스크만 실행할 수 있기 때문에 처리에 시간이 걸리는 태스크를 실행하는 경우 블로킹이 발생한다.

const sleep = (func, delay) => {
  const delayUntil = Date.now() + delay;

  while (Date.now() < delayUntil);

  func();
};

const foo = () => {
  console.log('foo');
};

const bar = () => {
  console.log('bar');
};

sleep(foo, 3000);
bar();

타이머 함수가 아닌 일반 함수 작성된 몇초 후에 실행하는 함수는 동기 처리 방식으로 실행되어 foo가 콘솔에 출력되고나서 bar가 출력됨

setTimeout함수의 경우 태스크를 블로킹 하지 않고 곧바로 실행됨 --> 비동기 처리

비동기 처리의 단점

  • 콜백 헬을 발생시켜 가독성을 나쁘게 한다
  • 비동기 처리 중 발생하 에러의 예외처리가 곤란하다.
  • 여러 개의 비동기 처리를 한 번에 처리하는 데 한계가 있다.

타이머 함수인 setTimeout과 setInterval, HTTP 요청, 이벤트 핸들러는 비동기 처리 방식으로 동작

이벤트 루프와 태스크 큐

자바스크립트의 동시성을 지원하는 것 --> 이벤트 루프

JS 엔진의 크게 2가지 영역으로 구분될 수 있다.

  • 콜 스택: 실행 컨텍스트 스택을 말하며 최상위 실행 컨텍스트가 종료되어 콜 스택에서 제거되기 전까지 다른 어떤 태스크 실행 X

  • 힙: 객체가 저장되는 메모리 공간, 객체는 크기가 정해져 있지 않아서 메모리 공간의 크기를 런타임에 결정하며 구조화 되어있지 않다는 특징을 가지고 있음.

비동기 처리의 코드 평가와 실행을 제외한 모든 처리는 JS엔진을 구동하는 환경인 브라우저 또는 Node.js가 담당한다.(호출 스케줄링을 위한 타이머 설정 및 콜백 함수 등록 --> 브라우저 또는 Node.js가 담당한다.)

  • 태스크 큐: setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역

  • 이벤트 루프: 콜 스택에 현재 실행중인 실행컨텍스트가 존재하는 지 확인하고, 콜스택이 비어있고 태스크 큐에 대기 중인 함수가 존재 시, 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.

setTimeout의 delay가 0인 경우, 지연시간이 4ms 이하이므로 최소 지연 시간 4ms가 지정이 되어 4ms 후에 콜백 함수가 태스크 큐에 푸시되어 대기하게 된다.

비동기 함수인 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기 후, 콜 스택이 비게되면 그때 콜스택에 들어가 실행됨.(브라우저가 타이머 만료되면 콜백 함수를 태스크 큐에 등록해줌)
JS엔진은 싱글스레드로, 브라우저는 멀티 스레드로 동작한다.


43장 - Ajax

Ajax란?

JS를 사용해서 브라우저가 서버에게 비동기 방식으로 데이터를 요청하고, 서버가 응답한 데이터를 수신하여 웹페이지를 동적으로 갱신하는 프로그래밍 방식

Web Api인 XMLHttpRequest 객체를 기반으로 동작하며 이 객체는 HTTP 비동기 통신을 위한 메서드와 프로퍼티를 제공한다.

Ajax 통신의 장점

  • 필요한 데이터만 서버로부터 전송 받아서 효율적
  • 변경할 필요가 없는 부분 재 랜더링X -> 순간적으로 깜박이는 현상 없음
  • 클라이언트와 서버의 통신이 비동기적으로 동작하여 요청을 보낸 후에 블로킹이 발생하지 않는다.

JSON

JSON은 클라이언트와 서버 간의 HTTP 통신을 위한 텍스트 데이터 포맷이다.

JSON의 표기 방식

JS의 객체 리터럴과 유사하게 키와 값으로 구성된 순수한 텍스트

키는 무조건 큰 따옴표로 묶고, 값 중에서 문자열은 반드시 큰 따옴표로 묶어야함.

{
    "name": "식빵",
    "family": "웰시코기",
    "age": 1,
    "weight": 2.14
}

출처: http://tcpschool.com/json/json_basic_structure

JSON.stringify

이 메서드는 객체를 JSON 포맷의 문자열로 변환함

클라이언트가 서버로 객체를 전송하려면 객체를 문자열화 해야하며 이를 직렬화라고 한다.

JSON.stringify(value[, replacer[, space]])

value
JSON 문자열로 변환할 값.

replacer - Optional
문자열화 동작 방식을 변경하는 함수, 이 값이 null 이거나 제공되지 않으면, 객체의 모든 속성들이 JSON 문자열 결과에 포함된다.

space - Optional
가독성을 목적으로 JSON 문자열 출력에 공백을 삽입하는데 사용되는 String 또는 Number 객체. 이것이 Number 라면, 공백으로 사용되는 스페이스(space)의 수를 나타낸다; 이 수가 10 보다 크면 10 으로 제한된다. 1 보다 작은 값은 스페이스가 사용되지 않는 것을 나타낸다. 이것이 String 이라면, 그 문자열(만약 길이가 10 보다 길다면, 첫번째 10 개의 문자)이 공백으로 사용된다. 이 매개 변수가 제공되지 않는다면(또는 null 이면), 공백이 사용되지 않는다.

출처: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#%EA%B5%AC%EB%AC%B8

JSON.parse

JSON 포맷의 문자열을 객체로 변환한다. 서버로부터 받은 JSON 데이터를 객체로 사용하려면 JSON문자열을 객체화 하여 사용한다.

JSON.parse(text[, reviver])

text
JSON으로 변환할 문자열.

reviver - Optional
함수라면, 변환 결과를 반환하기 전에 이 인수에 전달해 변형함.

출처: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#%EA%B5%AC%EB%AC%B8

XMLHtppRequest

JS를 사용하여 HTTP 요청을 전송하려면 XMLHttpRequest 객체를 사용한다. 이 객체는 HTTP 요청 전송 및 응답 수신을 위한 다양한 메서드와 프로퍼티를 제공한다.

XMLHttpRequest 객체 생성

Web API 이므로 브라우저 환경에서만 정상적으로 실행된다.

const xhr = new XMLHttpRequest();

XMLHttpRequest 객체의 프로퍼티와 메서드

중요한 것들만 정리

XMLHttpRequest 객체의 프로토타입 프로퍼티

프로토타입 프로퍼티설명
readyStateHTTP 요청의 현재 상태를 나타내는 정수. (UNSENT: 0, OPENED: 1, HEADERS_RECEIVED: 2, LOADING: 3, DONE: 4)
statusHTTP 요청에 대한 응답 상태를 나타내는 정수
statusTextHTTP 요청에 대한 응답 메시지를 나타내는 문자열
responseTypeHTTP 응답 타입
responseHTTP 요청에 대한 응답 몸체. responseType에 따라 타입이 다름

XMLHttpRequest 객체의 이벤트 핸들러 프로퍼티

이벤트 핸들러 프로퍼티설명
onreadystatechangereadyState 프로퍼티 값이 변경된 경우
onerrorHTTP 요청에 에러가 발생한 경우
onloadHTTP 요청이 성공적으로 완료한 경우

XMLHttpRequest 객체의 메서드

메서드설명
openHTTP 요청 초기화
sendHTTP 요청 전송
abort이미 전송된 HTTP 요청 중단
setRequestHeader특정 HTTP 요청 헤더의 값을 설정

XMLHttpRequest 객체의 정적 프로퍼티

정적 프로퍼티설명
DONE4서버 응답 완료

HTTP 요청 전송

  1. open 메서드로 HTTP 요청 초기화
  2. setRequestHeader 메서드로 특정 HTTP 요청의 헤더 값 설정
  3. send 메서드로 HTTP 요청 전송
const xhr = new XMLHttpRequest();

xhr.open('GET', '/users');

xhr.setRequestHeader('content-type', 'application/json');

xhr.send();

XMLHttpRequest.prototype.open

--> 서버에 전송할 HTTP 요청 초기화

xhr.open(method, url[, async])

method --> GET/POST/PUT/DELETE (HTTP 요청 메서드)
url --> HTTP 요청을 전송할 URL
async --> 비동기 요청 여부, 옵션은 기본값은 true

HTTP 요청 메서드는 클라이언트가 서버에게 요청의 종류와 목적을 알리는 방법

HTTP 요청 메서드종류목적페이로드
GETindex/retrieve모든/특정 리소스 취득X
POSTcreate리소스 생성O
PUTreplace리소스의 전체 교체O
PATCHmodify리소스의 일부 수정O
DELETEdelete모든/특정 리소스 삭제X

XMLHttpRequest.prototype.send

send 메서드는 open 메서드로 초기화된 HTTP 요청을 서버에 전송한다.

  • GET 요청 메서드의 경우 데이터를 URL의 일부분인 쿼리 문자열로 서버에 전송한다.
  • POST 요청 메서드의 경우 데이터(페이로드)를 요청 몸체에 담아 전송한다.

페이로드가 객체인 경우 반드시 JSON.stringify 메서드를 사용하여 직렬화한 다음 전달!

HTTP 요청 메서드가 GET 인 경우 send 메서드에 페이로드로 전달한 인수는 무시되고 요청 몸체는 null로 설정된다.

XMLHttpRequest.prototype.setReuqestHeader

특정 HTTP 요청의 헤더 값을 설정한다.

Content-type은 요청 몸체에 담아 전송할 데이터의 MIME 타입의 정보를 표현한다.

자주사용 되는 MIME 타입

MIME 타입서브타입
texttext/plain, text/html, text/css, text,javascript
applicationapplication/json, application/x-www-form-urlencode
multipartmultipart/formed-data

HTTP 클라이언트가 서버에 요청할 때 서버가 응답할 데이터의 MIME 타입을 accept로 지정할 수 있다. 만약 설정되지 않으면 send 메서드 호출 시 */* 로 설정

HTTP 응답 처리

readystatechange 이벤트를 활용!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const xhr = new XMLHttpRequest();

      xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');

      xhr.send();

      xhr.onreadystatechange = () => {
        if (xhr.readyState !== XMLHttpRequest.DONE) return;

        if (xhr.status === 200) {
          console.log(JSON.parse(xhr.response));
        } else {
          console.error('Error', xhr.status, xhr.statusText);
        }
      };
    </script>
  </body>
</html>

예제 실행 결과 아래와 같은 상태를 확인할 수 있음
load 이벤트 사용시 HTTP 요청이 성공적으로 완료된 경우 발생하므로 xhr.readyState가 XMLHttpRequest.DONE인지 확인할 필요 X


44장 - REST API

HTTP의 장점을 최대한 활용할 수 있는 아키텍처이머 이 REST의 기본원칙을 성실히 지킨 서비스 디자인을 "RESTful"이라고 표현한다.

REST는 HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처이며 REST API는 REST를 기반으로 서비스 API를 구현한 것

REST API의 구성

자원, 행위, 표현의 3가지 요소로 구성된다.

자원(자원) - URI로 표현
행위(자원에 대한 행위) - HTTP 요청 메서드
표현(자원에 대한 행위의 구체적 내용) - 페이로드

REST API 설계 원칙

기본적인 원칙 2가지

  • URI는 리소스를 표현하는데 집중
  • 행위에 대한 정의는 HTTP 요청 메서드를 통해 하는 것
  1. URI는 리소스를 표현해야한다 --> 리소스의 이름은 동사보다 명사!

  2. 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다 --> GET/POST/PUT/PATCH/DELETE를 사용하여 CRUD 구현

JSON Server를 이용한 REST API 실습

실습 내용이라 HTTP 메서드에 대한 내용 정리

GET 요청

todos 리소스에서 모든 리소스 취득

POST 요청

todos 리소스에 새로운 todo를 요청하고 서버로 전송시 setRequestHeader를 사용하여 페이로드의 MIME 타입을 지정해야함

PUT 요청

특정 리소스 전체를 교체할 때 사용. 마찬가지로 MIME 타입 지정 필요

PATCH 요청

특정 리소스의 일부를 수정할 때 사용. MIME 타입 지정 필요

DELETE 요청

todos 리소스에서 id를 사용하여 todo를 삭제

수업 시간 필기

41장

setTimeout이 콜백함수를 호출하는 것이 아닌 브라우저가 콜백함수를 호출!

setTimeout은 몇초 후에 콜백함수를 호출해달라고 요청만 하는 것

setInterval은 clearInterval이 무조건적으로 필요!

이 두 타이머 함수는 호스트 객체이며 반환값이 호스트에 따라 다름(브라우저: 숫자, Node.js: 객체)

디바운스 --> 마지막에 한번
스로틀 --> 일정 기간마다 한번

일반적인 경우 디바운스 --> resize, input, 스로틀 --> 스크롤


42장

비동기 함수 --> 전역 실행컨텍스트가 팝되고 나서 들어옴

while문 옆에 중괄호 없이 그냥 ; 만 찍혀있으면 아무것도 안하고 조건만 본다는 의미

동기 처리방식 --> 실행순서가 보장이 됨

서버통신은 반드시 비동기

컴퓨터 --> 파일을 생성하고 읽는것에 많은 연산 걸림(하드 디스크에 파일이 존재하기 때문에)

백엔드에서 디비에 접근하는 것은 느림

--> 총 합쳐서 서버와 통신하는 행위는 느림

비동기 - 블로킹이라는 키워드가 떠올라야함

태스크 큐를 이벤트 큐라고 부르기도 함

setTimeout, event, http request 모두 다 비동기 처리 방식으로 작동

Click, focus와 같은 메서드로 동작하는 함수는 동기 방식으로 작동

함수 내부에 비동기 로직이 포함되어있으면 비동기 함수


43장 및 express 서버 구축 실습

REST API

HTTP 메서드 URI 페이로드
GET /todos X

express에서 --save -dev 안붙이는 것은 배포용이기때문에

nodemon은 배포용이 아니기 때문에 npm i -D nodemon 으로 설치 (저장 시 자동 서버 껏다 켜줌 --> nodemon)

실습 예제에서 우리가 이벤트 핸들러에서 리턴을 아무리 해줘도 리턴값 못받음 --> 뻘짓

profile
It is possible for ordinary people to choose to be extraordinary.

0개의 댓글