[JS CS] 동기/비동기와 AJAX

작성:    

업데이트:

카테고리:

태그: , ,

AJAX

  • Asynchronous JavaScript And XML (비동기식 JavaScript와 XML)
  • 서버와 통신하기 위해 XMLHttpRequest 객체를 활용
  • JSON, XML, HTML 그리고 일반 텍스트 형식 등을 포함한 다양한 포맷을 주고 받을 수 있음
  • AJAX의 X가 XML을 의미하지만, 더 가벼운 용량과 JS의 일부라는 장점 때문에 JSON을 더 많이 사용


AJAX 특징

  • 페이지 전체를 reload(새로 고침)하지 않고서도 수행되는 비동기성
  • 서버의 응답에 따라 전체 페이지가 아닌 일부분만을 업데이터 가능

  • AJAX의 주요 두 가지 특징은 아래의 작업을 할 수 있게 해줌
  1. 페이지 새로고침 없이 서버에 요청
  2. 서버로부터 데이터를 받고 작업을 수행


AJAX 배경

  • 특정 기술이 아닌 기존의 여러 기술을 사용하는 새로운 접근법을 설명하는 용어
  • 기존 기술을 잘 활용할 수 있는 방식으로 구성 및 재조합한 새로운 접근법


XMLHttpRequest 객체

  • 서버와 상호작용하기 위해 사용되며 전체 페이지의 새로 고침 없이 데이터를 받아올 수 있음
  • 사용자의 작업을 방해하지 않으면서 페이지 일부를 업데이트 가능
  • 주로 AJAX 프로그래밍에 사용
  • 이름과 달리 XML 뿐만 아니라 모든 종류의 데이터를 받아올 수 있음
  • 생성자 : XMLHttpRequest


const request = new XMLHttpRequest();
const URL = 'https://jsonplaceholder.typicode.com/todos/1/' 

request.open('GET', URL)
request.send()

const todo = request.response
console.log(`data: ${todo}`)
  • console에 todo 데이터가 출력되지 않음
  • 데이터 응답을 기다리지 않고 console.log()를 먼저 실행했기 때문


Asynchronous JavaScript

동기식

  • 순차적, 직렬적 Task 수행
  • 요청을 보낸 후 응답을 받아야만 다음 동작이 이루어짐(blocking)


<button>버튼</button>

<script>
  const btn = document.querySelector('button')

  btn.addEventListener('click', function() {
    const pElem = document.createElement('p')
    pElem.innerText = 'sample text'
    document.body.appendChild(pElem)
  })
</script>
  • 버튼 클릭 후 alert 메시지의 확인 버튼을 누를 때까지 문장이 만들어지지 않음
  • alert 이후의 코드는 alert의 처리가 끝날 때까지 실행되지 않음
  • JavaScript는 single threaded이기 때문!


비동기식

  • 병렬적 Task 수행
  • 요청을 보낸 후 응답을 기다리지 않고 다음 동작이 이루어짐(non-block)


const request = new XMLHttpRequest();
const URL = 'https://jsonplaceholder.typicode.com/todos/1/' 

request.open('GET', URL)
request.send()

const todo = request.response
console.log(`data: ${todo}`)
  • 요청을 보내고 응답을 기다리지 않고 다음 코드 실행
  • 결과적으로 변수 todo에는 응답 데이터가 할당되지 않고 빈 문자열 출력
  • JS는 왜 기다려주지 않는 방식으로 동작하는가? JS는 single threaded 이기 때문!!


왜 비동기를 사용하는가?

사용자 경험

  • 매우 큰 데이터를 동반하는 앱이 있다고 가정
  • 동기식 코드라면 데이터를 모두 불러온 뒤 앱이 실행
  • 데이터를 모두 불러올 때까지는 앱이 모두 멈춘 것처럼 보임

  • 비동기식 코드라면 데이터를 요청하고 응답받는 동안, 앱 실행을 함께 진행
  • 데이터를 불러오는 동안 지속적으로 응답하는 화면을 보여줌으로써 더욱 쾌적한 UX 제공
  • 많은 웹 API 기능은 현재 비동기 코드를 사용하여 실행


Thread

  • 프로그램이 작업을 완료하기 위해 사용할 수 있는 단일 프로세스
  • 각 thread는 한 번에 하나의 작업만 수행
  • 다음 작업을 시작하려면 반드시 앞의 작업이 완료되어야 함
  • 컴퓨터 CPU는 여러 코어를 가지고 있으므로 한 번에 여러 가지 일을 처리 가능


JS와 Single Thread

  • 컴퓨터가 여러 개의 CPU를 가지고 있어도 main thread라 불리는 단일 스레드에서만 작업 수행
  • 이벤트를 처리하는 Call Stack이 하나인 언어라는 의미


Single Thread 문제 해결 방법

  1. 즉시 처리하지 못하는 이벤트들은 다른 곳(Web API)로 보내서 처리
  2. 처리된 이벤트들은 처리된 순서대로 대기실(Task queue)에 줄을 세워 놓고
  3. Call Stack이 비면 담당자(Event loop)가 대기줄에서 가장 오래된(제일 앞의) 이벤트를 Call Stack으로 보냄


Event loop를 기반으로 하는 동시성 모델

가. Call Stack

  • 요청이 들어올 때마다 해당 요청을 순차적으로 처리하는 Stack(LIFO) 형태의 자료 구조


나. Web API(Browser API)

  • JS 엔진이 아닌 브라우저 영역에서 제공하는 API
  • setTimeout(), DOM events 그리고 AJAX로 데이터를 가져오는 시간이 소요되는 일들을 처리


다. Task Queue(Event Queue, Message Queue)

  • 비동기 처리된 callback 함수가 대기하는 Queue(FIFO) 형태의 자료 구조
  • main thread가 끝난 후 실행되어 후속 JS 코드가 차단되는 것을 방지


라. Event Loop ⭐

  • Call Stack이 비어 있는지 확인
  • 비어 있는 경우 Task Queue에서 실행 대기 중인 callback 함수가 있는지 확인
  • Task Queue에 대기중인 callback 함수가 있다면 가장 앞에 있는 callback함수를 Call Stack으로 push


Zero delays

  • setTimeout(callback, 0)
  • 실제로 0ms 후에 callback 함수가 시작된다는 의미가 아님
  • delay(지연)는 JS가 요청을 처리하는 데 필요한 최소 시간이기 때문(보장된 시간이 아님)
  • 기본적으로 setTimeout 함수에 특정 시간제한을 설정했더라도 대기중인 메시지의 모든 코드가 완료될 때까지 대기해야 함
  • 즉, setTimeout의 대기 시간을 0ms로 하더라도 Call Stack만 쓰는 작업을 모두 마친 뒤에 FIFO로 작업을 실행


순차적인 비동기 처리

  • Web API로 들어오는 순서는 중요하지 않고, 어떤 이벤트가 먼저 처리되느냐가 중요
  • 이를 해결하기 위해 순차적인 비동기 처리를 위한 2가지 작성 방식


Async callbacks

  • 백그라운드 실행을 시작할 함수를 호출할 때 인자로 지정된 함수
  • ex. addEventListener()의 두 번째 인자(callback)


promise-style

  • Modern Web APIs에서의 새로운 코드 스타일
  • XMLHttpRequest 객체를 사용하는 구조보다 조금 더 현대적인 버전


Callback Function

  • 다른 함수에 인자로 전달된 함수
  • 외부 함수 내에서 호출되어 일종의 루틴 또는 작업을 완료
  • 동기식, 비동기식 모두 사용

  • 그러나 비동기 작업이 완료된 후 코드 실행을 계속하는 데 주로 사용
  • 이 경우를 비동기 콜백(asynchrounous callback)이라고 함


JavaScript 함수와 일급 객체

  • 일급 객체 First Class Object(일급 함수)
  • 다른 객체들에 적용할 수 있는 연산을 모두 지원하는 객체


일급 객체의 조건

  • 인자로 넘길 수 있어야 함
  • 함수의 반환 값으로 사용할 수 있어야 함
  • 변수에 할당할 수 있어야 함


Async callbacks

  • 백그라운드에서 코드 실행을 시작할 함수를 호출할 때 인자로 지정된 함수
  • 백그라운드 코드 실행이 끝나면 callback 함수를 호출하여 작업이 완료되었음을 알리거나, 다음 작업을 실행하게 할 수 있음
  • ex. addEventListener()의 두 번째 매개변수

  • callback 함수를 다른 함수의 인수로 전달할 때, 함수의 참조를 인수로 전달할 뿐이지 즉시 실행되지 않고, 함수의 body에서 “called back”됨
  • 정의된 함수는 때가 되면 callback 함수를 실행하는 역할


callback 사용의 이유

  • callback 함수는 명시적인 호출이 아닌 특정 루틴 혹은 action에 의해 호출되는 함수
  • Django의 경우 “요청이 들어오면”, event의 경우 “특정 이벤트가 발생하면” 이라는 조건으로 함수를 호출할 수 있었던 건 “Callback function” 개념 때문에 가능
  • 비동기 로직을 수행할 때 callback 함수는 필수
  • 명시적 호출이 아니라 다른 함수의 매개변수로 전달하여 해당 함수 내에서 특정 시점에 호출


callback 지옥

  • 순차적인 연쇄 비동기 작업을 처리하기 위해 “callback 함수를 호출하고, 그 다음 callback 함수를 호출하고,…“의 패턴이 지속적으로 반복
  • 여러 개의 연쇄 비동기 작업을 할 때 마주하는 상황
  • callback Hell(콜백 지옥) 또는 pyramid of doom(파멸의 피라미드)라고 함
  • 디버깅 또는 코드 가독성이 떨어지고 통제하기 어려움PI는


callback 지옥의 해결

  1. Keep your code shallow (코드의 깊이를 얕게 유지)
  2. Modularize (모듈화)
  3. Handle every single error (모든 단일 오류 처리)
  4. Promise callbacks (Promise 콜백 방식 사용)

댓글남기기