자바스크립트 비동기 처리와 AJAX·API의 관계

동기(Synchronous)와 비동기(Asynchronous)
동기 코드는 어떻게 실행될까?
동기 코드는 작성된 순서대로 실행된다. 앞선 작업이 끝나야 다음 작업을 시작하므로, 오래 걸리는 작업이 있으면 뒤의 코드도 함께 기다린다.
동기 코드가 실행을 막는 경우
const body = document.body;
body.textContent = "My name is Wonkook!";
window.alert("Blocking Code Execution!"); // Blocking
body.textContent = "My name is Kiwi!";
두 번째 줄은 body의 텍스트를 'My name is Wonkook!'으로 바꾼다. 이어서 alert 창이 열리고, 사용자가 창을 닫을 때까지 다음 줄은 실행되지 않는다. 다만 DOM 변경이 화면에 그려지는 시점은 별개이므로 첫 번째 텍스트가 alert 전에 보인다고 보장할 수는 없다. 창을 닫으면 마지막 줄이 실행되어 텍스트가 'My name is Kiwi!'로 바뀐다.
이 예제에서 실행을 막는 코드는 alert다.
setTimeout은 어떻게 비동기로 실행될까?
const body = document.body;
body.textContent = "Guess my name";
setTimeout(() => {
body.textContent = "My name is Kiwi!";
}, 0);
body.style.backgroundColor = "yellow";
setTimeout의 타이머는 브라우저가 관리한다. 지정한 지연 시간이 지나면 콜백이 태스크 큐(Task Queue)에 들어가고, 콜 스택이 비었을 때 메인 스레드에서 실행된다.
실행 순서
body의 텍스트를 'Guess my name'으로 설정한다.body의 배경색을 'yellow'로 바꾼다.- 타이머 콜백이 실행되며 텍스트를 'My name is Kiwi!'로 바꾼다.
지연 시간이 0이어도 콜백은 현재 실행 중인 동기 코드가 끝난 뒤에 실행된다.
브라우저의 fetch나 XMLHttpRequest도 응답을 기다리는 동안 JavaScript 실행을 계속할 수 있는 비동기 API를 제공한다. 네트워크 계층이 요청을 처리하고 결과가 준비되면 콜백이나 Promise의 후속 작업이 메인 스레드에서 실행된다. 다만 그 안에서 오래 걸리는 동기 연산을 실행하면 메인 스레드는 다시 막힌다. 콜백 함수나 이벤트 리스너를 사용했다는 사실만으로 코드가 자동으로 비동기가 되는 것도 아니다.
정리
비동기 프로그래밍의 핵심은 시간이 걸리는 작업과 그 결과를 처리할 코드의 실행 순서를 조율하는 데 있다.
AJAX란?
Asynchronous JavaScript and XML
AJAX는 페이지를 다시 불러오지 않고 서버와 비동기로 데이터를 주고받아 화면 일부를 갱신하는 기법이다. 이름에는 XML이 남아 있지만 JSON, XML, 텍스트 등 여러 형식의 데이터를 사용할 수 있다. 오늘날에는 주로 JSON을 주고받는다.
API란?
API(Application Programming Interface)는 다른 소프트웨어의 기능이나 데이터를 사용할 수 있도록 정한 인터페이스다. 라이브러리 함수와 브라우저의 DOM API도 API에 포함되지만, 여기서는 네트워크로 호출하는 웹 API를 다룬다. 웹 API는 어떤 주소와 방식으로 요청하고 어떤 형태의 응답을 받는지를 정한다.
API는 공개 범위에 따라 공개 API와 비공개 API로 나눌 수 있다. 공개 API도 API 키, 인증, 사용량 제한이나 요금이 필요할 수 있다.
우리가 사용하는 서비스도 SNS 간편 로그인, 지도, 날씨 같은 기능을 API로 연결한다.
용도별 공개 API는 public-apis 목록에서 찾아볼 수 있다.
웹의 요청과 응답
요청-응답 모델(클라이언트-서버 아키텍처)
그림처럼 클라이언트가 서버에 요청(Request)을 보내면 서버는 처리 결과를 응답(Response)으로 돌려준다. 이를 요청-응답 모델(Request-Response Model) 또는 클라이언트-서버 아키텍처(Client-Server Architecture)라고 한다.
요청과 응답 사이에는 어떤 과정이 있을까?
브라우저가 URL을 받아 서버의 응답을 화면에 그리기까지의 흐름을 단계별로 살펴보자.
DNS 조회와 서버 연결
브라우저는 URL의 호스트 이름에 대응하는 IP 주소를 DNS로 조회한다. 이미 캐시된 결과가 있으면 이를 사용할 수 있다. URL은 다음 요소로 나눌 수 있다.
scheme://host:port/path?query#fragment
DNS가 조회하는 대상은 이 가운데 host다. URL 전체를 IP 기반 URL로 바꾸는 것이 아니며, 브라우저는 원래 호스트 이름을 HTTP 요청과 TLS 인증서 검증 등에 계속 사용한다. 포트를 생략했다면 HTTPS는 443, HTTP는 80을 기본으로 사용한다.
전송 계층과 패킷
HTTP/1.1과 HTTP/2는 TCP를 사용한다. TCP(Transmission Control Protocol)는 데이터의 순서와 재전송을 관리하고 IP(Internet Protocol)는 패킷의 주소 지정과 전달을 담당한다. HTTP/3는 UDP 위의 QUIC을 사용하므로 모든 HTTP 통신이 TCP를 거치는 것은 아니다.
HTTP/1.1이나 HTTP/2에서 데이터가 이동하는 흐름을 단순화하면 다음과 같다.
- HTTP 메시지는 TCP가 다루는 바이트 스트림으로 전달되고, TCP는 이를 세그먼트로 나눠 순서와 재전송을 관리한다.
- 각 TCP 세그먼트는 IP 패킷에 실려 전송되고, 수신 측에서 다시 순서대로 조립된다.
- 패킷 교환망에서는 패킷마다 경로가 달라질 수 있으며, IP가 목적지까지의 주소 지정과 전달을 맡는다.
HTTP 요청
GET /resource HTTP/1.1
// Start line: HTTP method + request target + HTTP version
Host: api.example.com
User-Agent: Mozilla/5.0
Accept-Language: en-US
// HTTP request headers (many different possibilities)
<BODY>
// Request body (only when sending data to server, e.g. POST)
HTTP 요청은 시작 줄(Start line), 헤더(headers), 필요한 경우 실제 데이터를 담는 본문(body)으로 구성된다. HTTPS는 HTTP 통신을 TLS로 보호한다. 요청 메서드나 상태 코드의 의미가 바뀌는 것은 아니다. SSL은 TLS의 전신이며 더 이상 사용하면 안 되는 구식 프로토콜이다. 위 예제는 HTTP/1.1 메시지를 설명하기 위한 것이며 주석은 실제 메시지에 포함되지 않는다.
HTTP 응답
HTTP/1.1 200 OK
// Start line: HTTP version + status code + status message
Date: Mon, 18 Jan 2021 12:00:00 GMT
Content-Type: text/html
Transfer-Encoding: chunked
// HTTP response headers (many different possibilities)
<BODY>
// Response body (most responses)
응답도 시작 줄, 헤더, 본문으로 구성된다. 404 같은 상태 코드는 요청의 처리 결과를 알려준다. 200번대는 성공, 400번대는 클라이언트 요청 오류, 500번대는 서버 오류를 나타낸다.
파싱 및 렌더링 과정
HTML 문서를 요청했다면 브라우저는 응답을 파싱하면서 CSS, JavaScript, 이미지 등 필요한 리소스를 불러와 화면을 구성한다. 문서 이름이 반드시 index.html일 필요는 없다. AJAX로 JSON을 받았다면 HTML 문서를 새로 로드하는 대신 애플리케이션 코드가 그 데이터로 화면을 갱신한다.
글과 그림 ⓒ Wonkook Lee
주요 참고자료 Jonas Schmedtmann - Asynchronous JavaScript: Promises, Async/Await and AJAX
