본문 바로가기

JavaScript

20210720 JavaSciprt DeepDive 16 : 타이머, 디바운스, 스로틀, 비동기 프로그래밍(블로킹, 이벤트 루프, 태스크 큐, 콜 스택, 힙), Ajax, JSON, XMLHttpRequest, REST API

JavaScript Deep Dive 16


📄 용어 및 중요사항 정리


타이머


  • 호출 스케줄링(scheduling a call) :
    • 함수를 명시적으로 호출하지 않고 일정 시간이 경과된 이후에 호출되도록 함수 호출을 예약하는 것
    • 타이머 함수 : 스케줄링을 구현하게 도와주는 함수로 전역 객체의 메서드인 호스트 객체 임
      • setTimeout, setInterval
      • 비동기 처리 방식으로 동작 함
  • 싱글 스레드(single thread) : JS 엔진은 단 하나의 실행 컨텍스트 스택을 갖기 때문에 두 가지 이상의 태스크를 동시에 실행 불가함

타이머 함수


  • setTimeout(callback[, delay, param1, param2, ...])
    • 두번째 인수로 전달받은 시간(delay)으로 단 한번 첫번째 인수로 전달 받은 콜백(callback)을 호출하는 타이머 생성
      • callback : 시간 만료후 실행될 callback 함수
      • delay : 타이머 만료시간 (ms 단위), 생략시 기본값 0
        • delay 시간이 콜백함수가 그 시간에 호출되는 것을 보장하지 않음
        • 4ms 이하일 경우 최소 지연 시간 4ms
      • params : 호출 스케줄링된 콜백 함수에 전달해야 할 인수가 존재하는 경우 전달할 인수
    • return : 생성된 타이머를 식별할 수 있는 고유한 타이머 id 반환
      • 반환된 id는 clearTimeout에서 사용됨
      • 브라우저 환경: Number, Node.js: Object
  • clearTimeout(setTimeoutId) : setTimeout id를 인수로 받아서 해당 setTimeout 타이머의 호출 스케줄링을 취소 함

  • setInterval(callback[, delay, param1, param2, ...])
    • 두번째 인수(delay)로 받은 시간 마다 반복하여 첫번째 인수로 전달 받은 콜백(callback)을 호출하는 타이머 생성
    • callback, delay, params, return은 setTimeout과 같음
  • clearInterval(setIntervalId) : setInterval id를 인수로 받아서 해당 setInterval 타이머의 호출 스케줄링을 취소 함

디바운스와 스로틀


  • 디바운스, 스로틀 : 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 과도한 이벤트 핸들러의 호출 방지하는 프로그래밍 기법
    • 연속 발생 이벤트 : scroll, resize, input, mousemove 등

  • 디바운스(debounce) :
    • 짧은 시간 간격으로 발생하는 이벤트를 그룹화해서 마지막에 한 번만 이벤트 핸들러가 호출되도록 함
    • 짧은 시간 간격으로 이벤트가 연속 발생시, 이벤트 핸들러 호출하지 않다가 일정 시간 동안 이벤트가 발생하지 않으면 핸들러가 한번만 호출되도록 함
    • 클로저를 활용해서, 타이머 id를 기억하여 타이머가 생성되었는지 체크하여 새로운 요청시 기존 타이머 취소하고 새롭게 타이머를 만드는 방식
      • resize 이벤트 처리, 입력 필드 자동완성 UI, 버튼 중복 클릭 방지
      • 라이브러리 : Underscore / Lodash debounce 함수 사용 권장
const debounce = (callback, delay) => {
  let timerId;
  return (event) => {
    if (timerId) clearTimeout(timerId);
    timerId = setTimeout(callback, delay, event);
  };
};
// callback : 처리할 일(이벤트 핸들러)
// delay : 이벤트 핸들러를 호출하는데 계속 새로 설정하는 시간(요청을 한번만 인식할 시간)

  • 스로틀(throttle) :
    • 짧은 시간 간격으로 연속해서 발생하는 이벤트를 그룹화해서 일정 시간 단위로 이벤트 핸들러가 호출되도록 호출 주기를 만듦
    • delay가 경과하기 이전에 이벤트 발생시, 아무것도 하지 않고 delay 시간 경과시 이벤트 발생시 콜백함수 호출하고 새로운 타이머 재설정
    • 시간 간격으로 콜백 함수가 호출됨
      • scroll 이벤트 처리, 무한 스크롤 UI 구현
      • 라이브러리 : Underscore / Lodash throttle 함수 사용 권장
const throttle = (callback, delay) => {
  let timerId;
  return (event) => {
    if (timerId) return;
    timerId = setTimeout(
      () => {
        callback(event);
        timerId = null;
      },
      delay,
      event
    );
  };
};



비동기 프로그래밍


  • 싱글 스레드 : JS 엔진과 같이 한번에 하나의 태스크만 실행할 수 있는 동작 방식
  • 동기 처리(synchronous) : 앞선 태스크가 종료 할때 까지 이후 태스크가 대기하는 방식
    • 태스크(task) : 실행 중인 실행 컨텍스트를 제외한 실행 대기 중인 모든 실행 컨텍스트
      • 현재 실행중인 실행 컨텍스트가 스택에서 제거되면, 비로소 태스크의 실행 컨텍스트가 실행 됨
    • 블로킹(blocking) : 싱글 스레드 방식에 의해서, 앞의 긴시간이 걸리는 작업의 경우 뒤에 있는 함수가 실행 중단이 되는 것
    • 태스크 실행 순서가 보장 됨

  • 비동기 처리(asynchronous) : 현재 실행 중인 태스크가 종료 되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식
    • 태스크 실행 순서가 보장되지 않음
    • 콜백 패턴 : 비동기 처리를 수행하는 비동기 함수의 전통적인 패턴 -> 콜백 헬을 발생시킴
      • 콜백 헬 : 가독성 나쁨, 예외처리 곤란, 한번에 여러개의 비동기 처리 곤란
    • 비동기 함수 :
      • setTimeout, setInterval, HTTP 요청, 이벤트 핸들러 (브라우저에서 제공하는 API이기 때문에, 비동기가 가능함)

이벤트 루프와 태스크 큐


  • 멀티 스레드 : 브라우저 , 싱글 스레드 : 자바스크립트 엔진
    • 비동기 처리는 브라우저에서 제공하는 엔진과 자바스크립트 엔진의 병렬적 처리 덕분에 가능케 함

  • 자바스크립트 엔진
    • 비동기 함수의 콜백의 평가와 실행 담당
    • 콜 스택 : 실행 컨텍스트가 추가되고 제거되는 스택 자료 구조(실행 컨텍스트 스택)
    • : 객체가 저장되는 메모리 공간(실행 컨텍스트가 객체를 참조함)
      • 객체가 저장되어 있기 때문에 메모리가 동적 할당 되어 메모리 공간이 구조화 되어 있지 않음

  • 브라우저 환경, Node.js 환경
    • 호출 스케줄링을 위한 타이머설정, 콜백 함수의 등록은 브라우저 또는 Node.js에서 담당
    • 태스크 큐(task queue, event queue, callback queue)
      • 비동기 함수의 콜백 함수, 이벤트 핸들러가 일시적으로 보관되는 영역
    • 이벤트 루프 :
      • 브라우저에 내장되어 있는 기능 중 하나
      • 콜 스택에 현재 실행중인 실행 컨텍스트가 있는지, 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인함
      • 콜 스택이 비어 있고, 태스크 큐에 대기중인 함수 존재시 이벤트 루프가 선입선출(FIFO) 방식으로 태스크 큐에 대기중인 함수를 콜 스택으로 이동시킴

  • 비동기 처리 과정
    1. 전역 실행 컨텍스트 생성 -> 콜 스택 푸시
    2. 비동기 처리 함수 호출 -> 비동기 처리 함수 실행 컨텍스트 생성 -> 콜 스택 푸시
    3. 비동기 처리 함수 실행 -> 콜백 함수 호출 스케줄링 -> 함수 종료(콜 스택 팝)
    4. 병렬 처리
    • 병렬01 : 타이머 설정후 타이머 만료시 브라우저가 콜백 함수를 태스크 큐에 푸시 -> 콜백 함수 대기
    • 병렬02 : 다른 함수 호출, 실행 컨텍스트 생성 -> 콜 스택 푸시 (현재 실행중인 실행 컨텍스트가 됨) -> 함수 종료(콜 스택 팝)
    1. 전역 코드 실행 종료 -> 전역 실행 컨텍스트 콜 스택에서 팝
    2. 콜 스택이 비어 있음을 이벤트 루프가 감지 -> 콜백함수 실행 컨텍스트 생성 후 태스크 큐의 콜백함수 콜스택에 푸시 -> 콜백 종료(콜 스택 팝)



Ajax, JSON, XMLHttpRequest


  • Ajax : JS를 사용하여 브라우저가 서버에게 비동기 방식으로 데이터를 요청후 서버 응답을 받아 웹페이지를 동적으로 갱신하는 프로그래밍 방식
    • WebAPI인 XMLHttpRequest 객체 기반 동작
  • 전통적인 서버 통신의 단점
    • 변경할 필요가 없는 부분까지 포함된 완전한 HTML을 서버로 부터 매번 다시 받아 불필요한 데이터 통신 발생
    • 변경할 필요 없는 부분까지 다시 렌더링
    • 서버 응답이 있을 때 까지 블로킹 됨
  • Ajax 통신 장점
    • 변경에 필요한 부분만 전송받아 변경, 불필요한 데이터 통신 발생 X
    • 변경이 없는 부분은 렌더링 하지 않음 -> 화면 공백 현상 X
    • 비동기 방식 동작으로 인해 서버에게 요청 후 블로킹 발생 X

  • JSON : 클라이언트와 서버 간의 HTTP 통신을 위한 텍스트 데이터 포맷(언어 독립형 데이터 포맷)
    • 표기방식 : JS 객체 리터럴과 유사, 키-값 구조의 순수한 텍스트
      • 큰따옴표 사용 의무 O
    • JSON.stringify(object, filter함수, indent) : 객체, 배열을 JSON 포맷의 문자열로 변환(직렬화)
      • object : 직렬화할 객체, 배열 대상
      • filter함수 : 객체의 각 key-value를 인수로 받아 함수가 처리하여 return 하는 value를 직렬화의 item으로 함
      • indent : 직렬화한 결과가 indent 숫자를 무엇으로 가질지 설정
      • return : 변환된 문자열
    • JSON.parse(json) : JSON 포맷의 문자열을 객체, 배열로 변환함 (역직렬화)
      • json : 직렬화한 JSON 문자열
      • return : JSON 문자열을 변화한 배열 또는 객체

  • XMLHttpRequest
    • JS를 사용하여 HTTP 전송할때 필요한 XMLHttpRequest 객체(Web API)
    • new XMLHttpRequest() : XMLHttpRequest 객체 생성자 함수
    • XMLHttpRequest 객체 프로토타입 프로퍼티
      • readyState : HTTP 요청의 현재 상태를 나타내는 정수
        • UNSENT(0), OPENED(1), HEADERS_RECEIVED(2), LOADING(3), DONE(4)
      • status : HTTP 응답 상태(HTTP 상태 코드)
      • statusText : HTTP 응답 메세지를 나타내는 문자열
      • responseType : HTTP 응답 타입 (document, json, text, blob, arraybuffer ...)
      • response : HTTP 응답 몸체
      • responseText : 서버가 전송한 HTTP 요청에 대한 응답 문자열
    • XMLHttpRequest 객체의 이벤트 핸들러 프로퍼티
      • onreadystatechange : readyState 프로퍼티 값이 변경된 경우
      • onloadstart : HTTP 요청에 대한 응답을 받기 시작한 경우
      • onprogress : HTTP 요청에 대한 응답을 받는 도중 주기적으로 발생
      • onabort : abort 메서드에 의해 HTTP 요청이 중단된 경우
      • onerror : HTTP 요청에 에러가 발생한 경우
      • onload : HTTP 요청이 성공적으로 완료한 경우
      • ontimeout : HTTP 요청 시간이 초과한 경우
      • onloadend : HTTP 요청이 완료된 경우(성공, 실패 시 발생)
    • XMLHttpRequest 객체 메서드
      • open : HTTP 요청 초기화
      • send : HTTP 요청 전송
      • abort : 이미 전송된 HTTP 요청 중단
      • setRequestHeader : 특정 HTTP 요청 헤더의 값을 설정
      • getRequestHeader : 특정 HTTP 요청 헤더의 값을 문자열로 반환
    • XMLHttpRequest 객체 정적 프로퍼티
      • UNSENT : 값 0, open 메서드 호출 이전
      • OPENED : 값 1, open 메서드 호출 이후
      • HEADERS_RECEIVED : 값 2, send 메서드 호출 이후
      • LOADING : 값 3, 서버 응답중(응답 데이터 미완성 상태)
      • DONE : 값 4, 서버 응답 완료

  • HTTP 요청 전송

    1. XMLHttpRequest.prototype.open 메서드로 HTTP 요청 초기화
    2. 필요에 따라 XMLHttpRequest.prototype.setRequestHeader 메서드로 특정 HTTP 요청의 헤더값 설정
    3. XMLHttpRequest.prototype.send 메서드로 HTTP 요청 전송

  • XMLHttpRequest.prototype.open(method, url[, async])
    • method : HTTP 요청 메서드("GET", "POST", "PUT", "DELETE" 등)
    • url : 요청을 전송할 URL
    • async : 비동기 요청 여부, 옵션(기본값 true)

  • HTTP 요청 메서드
HTTP 요청 메서드 종류 목적 페이로드
GET index/retrieve 모든/특정 리소스 취득 X
POST create 리소스 생성 O
PUT replace 리소스의 전체 교체 O
PATCH modify 리소스의 일부 수정 O
DELETE delete 모든/특정 리소스 삭제 X

  • XMLHttpRequest.prototype.send(payload)
    • open 메서드로 초기화된 HTTP 요청을 서버에 전송
    • GET 요청 메서드 : URL 일부분인 쿼리 문자열로 서버에 전송
      • GET 메서드인 경우, payload 인수는 무시됨 요청 몸체 null 설정
    • POST 요청 메서드 : 페이로드를 요청 몸체에 담아 전송
      • payload가 객체인 경우 반드시 직렬화 하여 전달해야 함

  • XMLHttpRequest.prototype.setReauestHeader(헤더 종류, 헤더 값)
    • 특정 HTTP 요청의 헤더 값을 설정
    • 'Content-type' : 요청 몸체에 담아 전송할 데이터의 MIME 타입 정보 표현
      • MIME(Multipurpose Internet Mail Extensions) : 웹을 통해서 여러 형태의 파일 전달할 때 쓰임
      • MIME type :
        • text - 서브타입 : text/plain, text/html, text/css, text/javascript
        • application - 서브타입 : application/json, application/x-www-form-urlencode
        • multipart - 서브타입 : multipart/formed-data
    • 'Accept' : 서버가 응답할 데이터의 MIME 타입 지정
    • 클라이언트가 요청시 Content-type은 클라이언트가 전송하는 데이터의 타입, Accept는 서버가 전송하는 데이터의 타입

  • HTTP 응답 처리
    • send메서드를 통해서 HTTP요청 후 서버 응답이 올 때를 캐치해야 함
    • XMLHttpRequest 객체의 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 할당
    • 방법01 : 이벤트(onreadystatechange) 발생을 캐치하여 이벤트 핸들러 함수로 HTTP 응답 처리 함
      • 이벤트 핸들러 함수에서, readyState 값을 확인 -> 요청 성공 완료 상태를 확인
      • status 값을 확인 -> 정상인지 에러인지 확인
    • 방법02 : 이벤트(onload) 발생을 캐치핳여 이벤트 핸들러 함수로 HTTP 응답 처리함
      • readyState 값을 확인할 필요 없음, onload 발생은 요청이 성공적으로 완료 했을때 발생하므로
      • 핸들러 함수 내부에서 status 값을 확인하여 error와 응답data를 구분하여 어떻게 처리할지 설정
const xhr = new XMLHttpRequest();

xhr.open("GET", "https://jsonplaceholder.typicode.com/todos/1");
xhr.send();
xhr.onload = () => {
  if (xhr.status === 200) {
    console.log(JSON.parse(xhr.response));
  } else {
    console.log("Error", xhr.status, xhr.statusText);
  }
};



REST API


  • REST(REpresenational State Transfer) : HTTP 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처

  • REST API : REST를 기반으로 서비스 API를 구현한 것을 의미

  • REST API 구성
    • 자체 표현 구조로 구성되어 REST API만으로 HTTP 요청의 내용을 이해할 수 있음
    • 자원 : 자원 -> URI(엔드 포인트)
    • 행위 : 자원에 대한 행위 -> HTTP 요청 메서드
    • 표현 : 자원에 대한 행위의 구체적 내용 -> 페이로드

  • REST API 설계 원칙
    1. URI는 리소스를 표현해야 함
      • 리소스 식별 이름은 동사보다 명사를 사용
    2. 행위에 대한 정의는 HTTP 요청 메서드로 표현해야 함
      • 리소스에 대한 행위는 URI가 아니라 HTTP 요청 메서드로 표현