Ajax (Asynchronous Javascript)

Ajax는 서버로 요청을 보내고 응답을 받아 전체 페이지를 그리는 초기의 방식이 아닌, 페이지 일부분만을 갱신할 수 있도록 해 응답성을 향상시킨 기술이다.

  • 페이지 이동 없이 빠르게 화면을 동적으로 변경할 수 있다.
  • 초기에 수신해야하는 데이터의 양을 줄여 빠르게 로딩할 수 있다.

브라우저는 XMLHTTPRequest라는 API로 Ajax를 사용할 수 있도록 제공한다.

XMLHttpRequest

XMLHttpRequest는 브라우저와 웹 서버 간에 통신을 하여 리소스를 가져올 수 있는 API이다. XML 외에 모든 종류의 데이터를 가져올 수 있으며 Http 가 아닌 다른 프로토콜 또한 사용할 수 있으며 옵션을 통해 비동기, 동기 방식 모두를 사용할 수 있다.

요청 환경 구성

const xhr = new XMLHTTPReqeust(); //인스턴스 생성
const method = 'POST';
const url = '/posts';

xhr.open(method, url); // 요청 환경 구성 
//(필수: http 메소드, 요청할 url, 선택: aysncm username, password ... )

서버에 요청

const body = JSON.stringfy({userId: 100});
xhr.send(body); //요청 보냄
xhr.abort(); //요청 중지

헤더 다루기

setRequestHeader() 메서드로 요청에 헤더를 지정할 수 있다.

// Cookie나 Host, Connection 등 보안상으로 문제가 될 수 있는 필드를 수정하는 것은 불가능함
xhr.setRequestHeader('X-Test','a');
xhr.setRequestHeader('X-Test','b');
// X-test: a, b

HTTP POST로 통신할 경우 전송하는 body 데이터의 미디어 타입은 Content-type 필드를 통해 표현한다.

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

getRequestHeader() 또는 getAllRequestHeader() 메서드를 사용해 응답으로 온 헤더 정보를 확인할 수 있다.

이벤트

응답을 다루는 이벤트

  • loadstart : 요청이 시작될 때
  • progress : 요청한 데이터를 받는 동안 주기적으로 발생
  • loadend : 요청이 성공적으로 종료 되었을 때

요쳥에 문제가 생겼을 경우

  • timeout

  • abort

  • error

    HTTP 상태 코드가 4XX, 5XX 일 때 발생하는 것이 아니라 네트워크 장애 요인으로 요청이 완료되지 못했을 때 발생

xhr.addEventListener('load', () => {
	console.log(xhr.status); //상태코드
	console.log(xhr.reponse); // 응답 body
})

readyState 프로퍼티를 통해 현재 요청 상태를 알 수 있다.

  • UNSET
  • OPENED
  • HEADERS_RECEIVED
  • LODAING
  • DONE

을 가지며 각각 0부터 4까지의 숫자를 갖는다.

xhr.addEventListener('readystateChange', () => {
	if (xhr.readyState === xhr.OPENED) {
	}
})

Promise

비동기는 실행 결과를 순서대로 처리하지 않는다. 뒤죽 박죽인 결과 순서를 해결 하기 위해서 콜백을 사용하는데, 이때 중첩된 콜백 함수를 매개변수로 사용하는 경우가 발생한다. 이런 패턴을 콜백 지옥 혹은 파멸의 피라미드라고 부른다.

Promise 객체를 이용하면 비동기 처리 시점을 명확하게 표현할 수 있다.

비동기 작업이 끝난 뒤 결과값을 처리할 수 있는 객체로 Promise 생성자 함수를 통해 인스턴스화하며, 생성자 함수에 전달되는 함수를 실행자라고 한다. 전달된 실행자는 인스턴스가 생성될 때 비동기 작업을 수행한다.

const promise = new Promise((resolve, reject) => {
	resolve(100);
	resovle(200); // 무시
});

실행자 함수는 매개변수로 resolve(), reject()를 가진다. 비동기 작업이 성공적으로 실행되었을 경우 결과값과 함께 resolve() 함수를 호출하며, 실패했을 경우 에러 객체와 함께 reject() 함수를 호출한다. 실행자에서는 둘 중 하나를 반드시 하나를 호출해야하며, 중복 호출은 무시된다.

또한 state 프로퍼티로 상태를 알 수 있는데

  • fulfield
  • rejected
  • pending

이며 각각 상호 배타적이다.

then(), catch(), finally()

Promise 객체는 위 메서드를 통해 연결해서 사용할 수 있다.

  • then()

    Promise 객체를 리턴하고 이행되었을때, 거부되었을때 두 개의 콜백 함수를 인자로 받는다. 필요에 따라 둘 중 하나를 생략할 수 있다.

  • catch()

    에러를 처리하는 메서드

  • finally()

    이행, 거부에 상관 없이 실행되는 메서드. 주로 작업이 완료되었을 때 공통적인 처리 로직을 수행한다.

Promise chaining

then() 메서드에서 반환하는 값은 Promise 객체로 결괏값을 기준으로 이행된다. 이를 이용해 이전 Promise 객체의 결과값을 연속으로 이어 받아 순차적으로 비동기 작업을 처리할 수 있다.

a().then(resutFromA) => b(resultFromB)
	 .then(resultFromB) => c(resutFromC)

정적 메소드

  • resolve() : fulfilled 상태의 Promise 객체 반환

  • reject() : reject 상태의 Promise 객체 반환

  • all(순회 가능한 Promise 객체) : 여러 개의 Promise를 동시에 실행시키고 모든 Promise가 완료되면 결괏값을 배열로 반환한다. 동시에 실행해도 무방한 작업들을 처리할 때 사용한다.

    Promise.all([doA(),doB(),doC(),]).then((result) => {
     value = result;
     return result;
    }).then((total) => {
    	console.log(total);
    })

    배열에 담긴 Promise 중 하나라도 실패할 경우 즉시 거부되며 첫 번째로 실패한 이유를 반환한다.

  • allSettled() :

    all() 메서드만으로는 각각 Promise를 별도로 처리하기 어려운 특성을 보완함

    • 각 Promise가 성공했을 경우 {status: "fulfilled", value: result}
    • 실패했을 경우 {status:"reject", value:result}

    형태의 객체 배열을 반환한다.

  • race()

    가장 먼저 처리되는 Promise 결과(또는 에러)를 반환한다. 나머지는 무시된다.

async, await

promise 체이닝은 콜백 지옥을 개선했지만 이것도 길어질 경우 가독성이 떨어진다.

async, await는 문법적 설탕으로 promise를 더 편리하게 처리할 수 있다.

문법적 설탕 (Syntax Sugar)은 작성하는 사람이 편하도록 디자인된 문법이라는 뜻을 가진다.

awaitasync함수 내에서만 사용되며 함수의 실행을 중지하고 Promise가 이행될 때까지 기다린다.

function getResult() {
  a().then((resultFromA) => b(resultFromA))
  	 .then((resultFromB) => c(resultFromC))
}

async function getResult() {
  const resutFromA = await a();
  const resultFromB = await b();
  const resultFromC = await c();
}

//에러 처리 
async function func() {
  try {
    const respont = await err();
  }catch (e) {
    console.log(e);
  }
}

async function func() {
    const respont = await err();
}
func().catch((e) => {
  console.log(e)
})

Promise.All과 함께 사용하여 작업을 동시에 시작하고 모든 작업이 완료될 때까지 기다리도록 할 수 있다.

async function func() {
	 const response = await Promise.all([asyncFunc(1), asyncFunc(2)])
}

fetch()

XMLHttPRequest로 작성된 코드는 흐름을 이해하기 어려우며 가독성이 떨어진다. 이 단점을 해결하기 위한 새로운 표준이 fetch()이다.

fetch()를 호출하면 브라우저는 요청을 보내고 Promise 를 반환한다. 이 Promise는 내장 클래스 Resposne의 인스턴스와 함께 fulfilled 상태가 된다.

//이렇게도 쓰고
fetch(url,option)
.then((reponse) => {
	//응답처리
})
.catch(error) {
	// 네트워크 에러 처리
}

//이렇게도 쓸 수 있음
const response = await fetch(url, options);

options에는 HTTP 메서드나 헤더, body 등을 넣어줄 수 있다. 또한 XMLHttpRequest와 마찬가지로 보안상으로 문제가 될 수 있는 헤더의 값은 설정할 수 없다.

const user = {
	//user 정보
};
const url = 'https://test.com';

fetch(url, {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json;charset=utf-8'
	},
	body: JSON.stringify(user)
})

response.headers 프로퍼티의 get() 를 사용해 응답의 특정 헤더를 가져올 수 있고 response.headers 프로퍼티를 순회해 전체 헤더 값을 가져올 수 있다.

반환되는 Promise 객체는 네트워크 장애 같은 요인으로 요청이 완료 되지 못한 경우에만 거부되기 때문에 reponse 객체의 status 프로퍼티나 ok를 사용해 HTTP 상태 코드에 따라 응답을 처리 할 수 있다.

fetch((url)).then((res) => {
	if(res.ok) {//상태 코드가 200~209에 해당하는 값일때
		return res;
	}
})

응답 데이터를 원하는 형태로 가공할 수 있는 reponse 객체 제공 메서드

  • text()
  • json()
  • formData
  • blob()
  • arrayBuffer()

axios

axios는 node.js와 브라우저를 위한 Promise 기반 HTTP 클라이언트 라이브러리이다. 써드파티 라이브러리로써 추가적인 설치 및 임포트가 필요하지만 과정이 어렵지 않으며 폭 넓은 브라우저 호환성이 보장된다.

서드파티란 프로그래밍을 도와주는 플러그인이나 라이브러리 등을 만드는 회사를 말한다.

fetch와 비교했을 때 각각의 특징과 장단점은 다음과 같다.

AxiosFetch
써드파티 패키지Built-in으로 설치 필요 없음, 모던 브라우저에서 사용 가능
wide browser 지원오직 크롬 42+, firefox 39+, edge 14+, safari 10.1+ 지원. polyfill 이용해서 하위 호환성 지원 가능
XSRF Protection 보안 기능 제공없음
자동 JSON 데이터 변환 지원reponse 객체에 json() 호출하는 로직 추가하여 변환 가능
Request 취소 와 Request Timeout 설정 가능없음. AbortController 이용하여 구현 가능
HTTP Requests 의 Intercept 가능Intercept Requests 기본적으로 제공되지 않음. Global Fetch Method를 Overwrite 하여 나의 인터셉터 정의 가능
Download Progress 지원Upload Progress 지원안함. Response Object의 Body Property 통해 제공되는 ReadableStream 인스턴스 이용 가능

참고

https://yeonfamily.tistory.com/10

웹소켓

HTTP의 연결 방식은 요청과 그에 따른 응답이 있어야한다. 즉 요청이 있을 때만 서버와 연결이 되며 그렇지 않으면 연결이 유지 되지 않는다. 따라서 실시간으로 통신할 경우 HTTP 요청 방식은 한계가 명확하다. 클라이언트의 매우 잦은 요청은 네트워크 상 많은 부하를 발생 시킬 수 있다. 이를 보완하기 위해서 HTML5에는 웹소켓이 등장했다.

웹소켓은 서버와 사용자 간의 연결을 유지한 상태로 추가 요청 없이 양방향으로 데이터를 교환할 수 있는 프로토콜로 실시간으로 데이터 교환이 일어나야하는 서비스에서 유용하다.

const socket = new WebSocket("wss://example.com");
//ws, wss는 매개변수 url에서 사용하는 프로토콜로 http, https의 관계를 갖는다.

웹 소켓의 핸드쉐이크 과정은 HTTP를 이용한다.

핸드쉐이크는 네트워크 분야에서 통신이 시작되기 전 양측 간의 통신을 위해 정보를 ㄱ환하는 과정을 의미한다.

  • HTTP는 반드시 1.1 버전 이상이여야한다.
  • GET 메서드를 이용해 요청을 보낸다.

웹 소켓의 핸드 쉐이크 과정 순서는 다음과 같다

  • 브라우저는 서버에 웹 소켓 지원 여부를 물어본다.

    GET /chat HTTP/1. 1
    Host: example.com
    Upgrade: websocket
    Connetction: Upgrade
    ...

    Upgrade, Connection 헤더의 값을 설정하여 클라이언트 측에서 프로토콜을 웹 소켓 프로토콜로 변경하고 싶다고 나타낸다.

  • 서버에서 웹 소켓 지원 여부를 반환한다.

  • 서버, 브라우저 모두 웹 소켓을 지원한다면 연결이 성립된다.

XMLHttpRequest와 같이 상태를 나타내는 readyState가 존재한다.

  • CONNECTING : 연결이 수립되는 중
  • OPEN : 연결이 수립
  • CLOSE : 연결이 종료되는 중
  • CLOSED : 연결이 종료됨

웹소켓이 정상적으로 생성되면 4개의 이벤트를 사용할 수 있으며 on<eventName> 혹은 addEventListener()를 통해 등록할 수 있다.

  • open : 데이터를 주고 받을 준비가 되었을 때
  • close : 연결이 종료되었을 때
  • message : 서버로부터 메시지를 받았을 때, 이벤트 객체를 통해 메시지에 접근할 수 있음
  • error: 에러가 발생했을 때
const socket = new WebSocket("wss://example.com");
socket.addEventListener('message', (event) => {
	console.log('데이터 수신 :', event.data);
})

0개의 댓글