[JS CS] 동기/비동기와 AJAX
작성:    
업데이트:
카테고리: JS CS
태그: FE Language, JS, JS CS
AJAX
- Asynchronous JavaScript And XML (비동기식 JavaScript와 XML)
- 서버와 통신하기 위해 XMLHttpRequest 객체를 활용
- JSON, XML, HTML 그리고 일반 텍스트 형식 등을 포함한 다양한 포맷을 주고 받을 수 있음
- AJAX의 X가 XML을 의미하지만, 더 가벼운 용량과 JS의 일부라는 장점 때문에 JSON을 더 많이 사용
AJAX 특징
- 페이지 전체를 reload(새로 고침)하지 않고서도 수행되는 비동기성
-
서버의 응답에 따라 전체 페이지가 아닌 일부분만을 업데이터 가능
- AJAX의 주요 두 가지 특징은 아래의 작업을 할 수 있게 해줌
- 페이지 새로고침 없이 서버에 요청
- 서버로부터 데이터를 받고 작업을 수행
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 문제 해결 방법
- 즉시 처리하지 못하는 이벤트들은 다른 곳(Web API)로 보내서 처리
- 처리된 이벤트들은 처리된 순서대로 대기실(Task queue)에 줄을 세워 놓고
- 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 지옥의 해결
- Keep your code shallow (코드의 깊이를 얕게 유지)
- Modularize (모듈화)
- Handle every single error (모든 단일 오류 처리)
- Promise callbacks (Promise 콜백 방식 사용)
댓글남기기