개요
server 동작을 구현하기 위해 필요한 원리와 REST API에 대해 이해한다.
Checklist
비동기 프로그래밍의 개념에는 컴퓨터 공학에서의 Blocking 개념을 먼저 알고 있어야 한다.
Blocking
프로세스가 다른 프로세스의 작업 및 시스템을 호출하여 해당 작업이 완료될 때까지 진행중인 작업을 보류하는 과정(작업종료가 아닌, 작업의 우선순위를 후순위로 처리한다).
네트워크에서도 유사한 의미로 사용되며, 다른 작업이 완료될 때까지 지금의 작업 진행을 보류한다.
non-Blocking
다른 작업의 수행 완료를 기다리지 않고, 기존 작업도 같이 병렬적으로 수행한다.
네트워크나 node.js에서 중요한 모델이며, 특히 node.js에서 I/O입출력 이슈를 해결하기 위한 기술이기도 하다.
javaScript 언어의 대표적인 특징이기도 하며, 기본적으로 non-blocking을 지원하기때문에 웹브라우저 동작이 병렬적으로 처리될 수 있다.
동기(Synchronize)처리
요청대상과 처리대상을 일치시킨다.
즉 처리요청을 한 대상에 대한 처리(작업)이 완료될 때까지 다른 작업은 수행하지 않는다.
비동기(Asynchronize)처리
동기처리와 반대 개념으로, 요청대상과 처리대상을 일치시키지 않는다.
요청대상과 처리대상이 모두 독립적인 상태/시점에서 작업하는 개념으로, 처리시점은 내부적인 event loop에 의해 결정된다.
함수의 인자를 전달받을 때 변수가 아닌, 함수를 전달받는 경우를 말한다.
function Reference(id, log){
const user = {
id : id,
name : "User No_" + id
}
//log 함수를 인자로 받아 활용하는 logic
log(user)
}
//log인자에 전달되는 함수를 callback, 이 callback함수를 할당받는다.
Reference(1, (user) => {
console.log("user info : ", user);
})
이처럼 return값을 반환받아 다시 변수로 할당하는 번거로운 작업을 하지 않아도 되고, 예상치 못한 비동기 처리 이슈를 방지할 수 있다.
즉 함수로 인자를 바로 전달하여, return없이 즉시적인 반환이 가능하다.
단 처리순서를 보장하기 위해 무수히 많은 callback을 사용한다면 코드가 지나치게 길어져 가독성이 떨어질 수 밖에 없다.
이처럼 callback을 과하게 사용하는 경우를 callback 지옥이라 하며, 이러한 단점을 보완하기 위해 promise / async-await과 같은 기능을 활용할 수 있다.
Javscript는 위에서 기술한 비동기 처리방식을 활용하므로 logic을 순차적으로 처리하지 않고, event loop 등 내부적인 모듈을 기반으로 작업을 진행한다.
따라서 logic 내부적으로 반드시 순서를 지켜야 하거나 일전의 처리를 기다려야 하는 경우, 처리 순서를 강제하는 여러 기능들을 사용할 수 있다.
Promise
Promise는 말 그대로 약속한다는 개념의 객체이다.
사용자가 비동기 처리를 반드시 해야하는 상황이 올때 logic의 순차적인 실행을 보장(=약속)받기 위해 사용하는 가장 대표적인 비동기처리 함수이다.
비동기 처리 결과에 따라 대기 / 이행 / 실패 상태로 나뉘고,
Promise 객체는 이에 따라 비동기처리 결과반환(resolve) / 처리실패(reject) 의 결과를 반환한다.
resolve를 받아 data를 받아오거나, reject를 받아 예외(error)처리를 통해 logic을 재구성할 수 있게 된다.
※ Promise 내부적인 logic을 오류없이 완료하면 resolve, 오류발생 시 reject를 반환한다.
"use strict";
//promiseall
//내부 promise들이 모두 완료될때까지 대기
const promise1 = new Promise((resolve, reject) => resolve("즉시 호출"));
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("3초뒤 호출");
}, 3000);
});
Promise.all([promise1, promise2]).then((values) => console.log(values));
async - await
async - await 구문에 promise 객체를 활용하여 비동기처리를 구현할 수 있고, promise 객체 자체가 비동기처리에 적합한 logic이기 때문에 많이 활용한다.
await 구문을 사용하기 위해선 반드시 async 함수 안에서 사용해야 한다.
(즉, promise 객체를 활용하는 문법적인 도구)
async () => await timeInterval({
const value = true
const res = new Promise((resolve, reject) => {
if(value){//Promise 객체호출후, logic 성공시 resolve를 통해 true
resolve({
console.log("true")
})}else{//Promise 객체호출후, logic 실패시 reject를 통해 false
reject({
console.log("false")
})
}
}), 1000)
console.log(value)
try{
const value = await res
console.log(value)
}catch(err){
console.error(err)
}
※ async 함수 내 await 구문을 사용하여 console.log는 상위 logic인 timerInterval의 logic이 완전히 종료될 때까지 대기한다.
※ 비동기처리에서 event loop 발생시점 및 logic 흐름을 위와 같이 async - await 구문을 통해 control할 수 있다.
http 프로토콜에서 req 인자에 외부 resource, data를 전달할 수 있다.
서버와 상호작용을 할 수 있는 객체이다.
쉽게 말하면 데이터를 받아올 수 있는 객체이며, 해당 사이트 혹은 get/post method를 통해 전달받는 resource들을 XMLHttpRequest를 통해 얻을 수 있다.
HTTP와 함께 FTP같은 다른 프로토콜에서도 동작이 가능하며, 별도의 상태코드가 존재한다.
※ 0 : uninitialized, request 요청 전
※ 1 : loading, client와 server 연결됨(event triggered)
※ 2 : loaded, server가 request를 받음
※ 3 : interactive, request를 처리함
※ 4 : complete, response 대기
axios나 REST API처럼 데이터 확보 및 생성 등을 위해 사용하는 도구 중 하나이다.
특히 axios의 경우 서버와의 상호작용이 성공적으로 완료되면 res인자를 반환하는데, fetch 역시 동일한 logic이다(data상호작용이 완료되었을때 res 반환).
우리가 흔히 알고있는 web application이나 microservice architecture에서 각 component들을 연결하는 가장 일반적인 method 및 API를 일컫는 용어이다.
보통은 http 프로토콜에서 활용하는 pattern이나 client와 server의 요청 및 응답 API과 같은 의미로 이해하면 편하다.
엄격하게 말하면 HTTP API에서 여러 제약조건을 만족하였을때 REST API라 할 수 있는데, 실무에서는 같은 의미로 취급하는 경우가 많다.
REST API는 몇가지 조건을 만족해야 한다.
※ 균일한 인터페이스(멱등성) : 요청에 상관없이 동일한 resource에 대한 모든 API 요청은 동일하게 보여져야 하며, client가 필요로 하는 모든 정보를 포함해야 한다.
※ client/server decoupling : client와 server는 서로 완전히 독립적이어야 하고, resource의 URI를 제외하고 어떠한 방식으로도 서로 상호작용은 할 수 없다.
※ Stateless : server는 client 요청과 관련한 데이터를 저장할 수 없으며, 이에 따라 client request에서 처리가 필요한 모든 정보가 담겨져 있어야 한다.
※ caching : resource는 client/server에서 caching할 수 있어야 하며, server 응답에는 caching 허용 여부에 대한 정보가 포함되어 있어야 한다.
→ resource caching이 가능하다면 client/server의 구성 요소 확장성이 증가한다.
※ 계층 구조 아키텍쳐 : REST API에서의 request, response는 다른 계층에서 전달된다.
→ 통신 루프 상에서 서로 다른 중개자가 있을 수 있음을 유념해야 하고, client/server는 이러한 통신과정을 알 수 없도록 설계해야 한다.
※ on demand : 특정 경우에는 응답에 실행코드를 포함하여, 정적 리소스에 대한 정적 응답과 더불어 동적 실행이 가능하다.
위에서 기술하였듯이 실무에서는 REST API와 HTTP protocol을 사실상 동일한 개념으로 보지만, 사전적으로는 RESTful API가 훨씬 제한적이고 범위가 좁다.
use strict 모드에서만 설계할 수 있을 만큼 method가 제한적이고, 모든 조건을 만족하는 API 설계가 쉽지 않다.
Cross-Origin Resource Sharing(교차 출처 resource 공유)는 request(요청)을 발생한 자원의 출처가 다를 경우, application이 모든 자원에 접근할 수 있도록 권한을 부여하는 체제이다.
해당 브라우저는 server 동작을 일으키는(POST 등) HTTP method의 header에 CORS를 명기한 사전 전달(preflight)을 진행하며, server 허가가 이루어지면 CORS가 가능해진다.
참조개념
javascript 자체가 비동기방식으로 logic을 처리한다.
node.js는 이러한 특징을 네트워킹 처리를 하는데 철저히 활용하며, 우리가 흔히 알고있는 순차적인 알고리즘이나 직렬적인 처리 개념과 상이하다는 것을 유념해야 한다.
코드자체는 사용자가 기재한 순서대로 처리하되, 일전의 처리를 요청한 과정이 완료될 때까지 기다리지 않고 바로 다음 처리를 진행한다.
이는 Javascript 엔진인 event loop의 존재로 가능하다(사용자의 요청에 따른 반응으로 로직이 실행).
javascript 기반의 언어들은 비동기 처리로 인한 이슈발생을 미리 예상하여 await - async 함수 등으로 순차처리를 해줘야 한다.
WebDevCurriculum - QUEST09
https://github.com/leejiwoo2021/WebDevCurriculum/tree/master/Quest09
XMLHttpRequest
https://popcorn16.tistory.com/128