비동기 처리에 관련되서 코드 구현하는 부분을 공부했던 내용을 정리해서 적어보려고 한다.
공부하다 보니 해당 내용은 아니지만, 관련된 내용도 적게 되다 보니 그냥 각자 나눠서 올릴까 ? 생각도 했지만, 관련 내용을 한번에 모아서 보는 것이 좋을거 같다고 생각이 들어서 한번에 모아서 올려본다.
🌟잠깐의 알아야할 내용🌟
HTTP에는
1. Body
2. Header
3. Cookie
가 있고 이 것을 REST API 규칙에 맞게 요청한다.
Get
- 받아오기만 하면 될때 url로 요청만 하면 될때
( 서버에서 어떤 데이터를 가져와서 보여줌 )
Post
- 내 정보를 서버에 게시를 해놓고 그거에 맞는 정보를 받아올 때 ( 포스트잇 )
- 내가 원하는 정보, 컨텐츠
- 그 컨텐츠를 담는 공간이 바디이다.
( 서버로 데이터를 보냄 )
Put
- 원래있던 데이터를 새로운 데이터로 대체하고 싶을 때 사용. (수정할때 사용하게 된다. )
- ex) 게시글의 내용을 수정할 때 사용된다. ( 전체를 대체 )
( 데이터베이스 내부 내용 갱신 )
patch
- 데이터의 정보를 부분적으로 교체해야할 때 사용된다. ( 부분대체 )
Delete
- 삭제하고 싶을때 사용된다.
( 데이터베이스 내부 내용 삭제 )
이 메소드를 사용하기 위해서 같이 보내야할 정보들.
- 어떤 메소드를 사용할 것인지
- url 주소
- data ( 선택적 )
- params ( 선택적 )
req : 요청 오브젝트
res : 응답 오브젝트
비동기 처리란 ?
: 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 것.
비동기처리를 하다보면 한 가지 주의해야하는 점이 있다.
그것은 바로 콜백지옥( callback ) 이다.
❓왜 why❓
콜백 함수를 사용할 때,
파라라미터 ( 인자 ) 로 전달하는 방식이 반복되면서 코드들이 충격적인 들여쓰기가 되있는 것을 볼 수 있다.
이러한 이유는
1. 네트워크에서 데이터를 받아오는 상황
2. 파일을 읽어오는 상황
3. 이벤트 처리
4. 서버 통신 등 ..
비동기적 작업을 수행하게 되면 발생한다.
단점은 가독성이 매우 떨어지고 에러가 생기면 코드를 수정 하기가 어렵기 때문이다.
또, 디버깅 하기도 어렵다.
그래서 이러한 결과를 맞이하게되면 해결할 수 있는 방법이 있다.
비동기적인 기능인 코드를 콜백함수를 대신해 사용되는 object 이다.
Promise
async & await
AJAX
Generator
을 사용하는 것이다.
( 4번인 Generator
은 생략하고 1, 2, 3만 정리해보았다.
Generator
은 필요로할 때, 찾아볼 예정 )
Promise
첫 번째, Promise
이다.
❓
Promise
는 무엇인가 ❓
자바스크립트에서 제공하는 비동기를 간편하게 처리하도록 도와주는 object객체 이다.
정해진 기능을 수행하고 나서 수행이 성공했다면 성공의 메세지를,
기능을 수행하다 예상치 못한 에러가 생기면 에러를 표출할 수 있도록 도와준다.
즉 Promise란, 실행은 바로 하되, 결과값을 나중에 원할 때 쓸 수 있는 것.
( 실행은 바로 하지만, 결과값이 나올때는 reslove가 되었을 때나오기 때문에 나중이고, 결과 값을 사용할 때는 더 나중이다. )
실행은 바로 👉🏻 결과 값도 거의 바로 쓰고 싶은데 👉🏻 그 다음에 결과값이 나오면 👉🏻then
,await
,Promise.all
이런게 결과값을 기다린 후에 실행한다.
먼저, Promise
를 알기 위해 간단한 이론 설명을 해보자.
☝🏻첫번째.
State
상태
: 기능이 지금 어떤 상태인지 성공인지 실패인지 알려주는 상태
✌🏼두번째.
프로듀서 vs 클라이언트 ( 상황과 입장 )
프로듀서 : 정보를 제공해주는 프로듀서
클라이언트 : 정보를 소비하고 사용하는 소비자인 클라이언트
state
의 종류state
의 종류에는 3가지로 구분할 수 있다.
☝🏻첫번째,
Pending
: Promise
가 만들어져서 기능이 수행중일 때
( 기본 값은 undefinde
이다. )
✌🏼두번째,
Fulfilled
: 성공적으로 끝내고완료한 상태
🤟🏼세번째,
Rejected
: 파일을 찾을 수 없거나 네트워크의 문제가 있어서 에러를 호출한 상태
각, 파트에서 담당하는 역할을 정리해 적어보았다.
new Promise
를 이용해 promise
만들기resolve
는 성공의 코드, reject
는 실패의 코드reject
는 new Error()
라는 자바스크립트에서 제공 하는 Object
( 객체 )를 사용한다.then
, catch
로 성공, 실패 여부 판단 하기then()
: 성공의 여부를 나타내주는 기능
( 값을 바로 전달할 수 있고, 또 다른 비동기의 프로미스를 또, 새로운 프로미스를 만들어서 전달 할 수 도 있다. )
catch()
: 실패했을 때 어떤 에러인지 작성 할 수 있고, catch
뒤에 또 다른 then()
을 보여줄 수 있게 해준다.
( catch
를 사용하는 이유는 error
로 인해 뒤의 반환될 값들을 모두 먹통으로 만들어 버리기 때문이다. )
finally()
: 성공, 실패의 여부와 상관없이 어떤기능을 마지막으로 수행하고 싶을 때 사용한다.
❗️ 하나의 TIP ❗️
새로운 프로미스가 만들어 질때는, 엑스큐터( executor
)라는 함수가 자동적으로 실행이 된다.
( 그래서 사용자가 원할때
즉, 버튼을 클릭했을 때 어떤 모션을 행할 때 코드를 만들어야되는 것을 생각해야한다. )
Promise
사용 예시// 프로듀서
function getData() {
return new Promise( function ( resolve, reject ) {
$.get( ‘url 주소 /products/1’, function( response ) {
if ( response ) {
resolve( response );
}
reject(new Error (“Request issued failed”));
});
});
}
// 클라이언트
// $.get() 호출 결과에 따라 ‘response’ 또는 ‘Error’ 출력
getData().then( function ( data ) {
console.log(data); // response 값 출력
}).catch( function (err) {
console.log(err); // Error 출력
}).finally(() => {
console.log( “ 성공 실패 여부와 상관없이 어떤 마지막의 기능을 수행하기 위한 파이널리!!!! ” );
});
앞의 이론적인 설명을 잘 기억한다면, 코드를 보고 사용법에 대해 이해가 가능할 것이다.
프로듀서 부분을 보면,
서버에서 응답을 제대로 받아오면, ressolve()
메서드를 호출
서버에서 응답이 없다면, reject()
메서드를 호출
클라이언트 부분을 보면,
호출된 메서드에 따라 then()
이나 catch()
로 분기해 응답결과 또는 오류를 출력한다.
Promise chaining
(프로미스 체이닝 )❓Promise Chaining이란❓
간단하게 이야기 하자면,
여러개의Promise
를 연결해준다 라는 말이다.
그러면, 어떻게 연결하는지 코드를 한번 살펴보자.
setTimeout()
API를 사용한 예시setTimeout()
을 이용해 2초후에 resolve()
를 호출하는 예제 이다.function getData() {
return new Promise( function ( resolve, reject ) {
setTimeout( function () {
resolve(1);
}, 2000);
});
}
// then() 으로 여러개의 프로미스를 연결하는 방식
getData()
.then( function ( result ) {
console.log(result); // 1
return result + 10;
})
.then( function ( result ) {
console.log(result); // 11
return result + 10;
})
.then( function ( result ) {
console.log(result); // 21
})
코드를 한 줄 한 줄 설명해보자면,
resolve()
가 호출되면 Promise
가 대기 상태에서 이행 상태로 넘어가기 때문에 첫 번째 .then()
의 로직으로 넘어간다..then()
에서는 이행된 결과 값 1을 받아서 10을 더한다. 그 후, 두 번째 .then()
으로 넘겨준다..then()
에서도 마찬가지로 바로 이전 Promise
결과 값 11을 받아 20을 더하고 다음 .then()
으로 넘겨준다..then()
에서 최종 결과 값 31을 출력된다.❗️여기서 잠깐 하나의 TIP❗️
new Promise
는 자동으로 호출된다.
Promise
내부함수는 동기이고Promise
안에setTimeout
내부가 비동기 함수이다.
Promise Error Handling
( 프로미스 에러 핸들링 )먼저, 에러를 처리 하는 방법은 2가지가 있다.
일단 코드를 보자 !
function getData() {
return new Promise( function ( resolve, reject ) {
reject(‘failed’);
});
}
// 1. then()의 두 번째 인자로 에러를 처리하는 방법
getData().then( function () {
// … 응답받기 성공 코드
}, function(err) {
console.log(err);
});
// 2. catch()로 에러를 처리하는 방법
getData().then().catch( function (err) {
console.log(err);
});
과연 이 두가지 방법 중 선호하는 방법은 어떤 방법일까 ?
선호하는 방법은
catch()
로 에러를 처리하는 방법이라고 한다.
❓이유는❓
then()
으로 에러를 처리 했을 때 에러를 감지 못하는 오류가 있다.
예시를 보자.
then()
의 두 번째 인자로는 감지 못 하는 오류function getData() {
return new Promise( function ( resolve, reject ) {
resolve( ‘ 성공 ! ‘ );
});
}
getData().then(function(result) {
console.log(result);
throw new Error( “ Error in then() “ ); // Uncaught (in promise) Error: Error in then()
}, function(err) {
console.log( “then error : “, err );
});
getData()
함수의 프로미스에서 resolve()
메서드를 호출하여 정상적으로 로직을 처리했지만,
then()
의 첫 번째 콜백 함수 내부에서 오류가 나는 경우 오류를 제대로 잡아내지 못한다.
코드를 실행하면 이런 에러가 발생한다.
그러면 catch()
메서드를 이용해 에러를 처리한다면 어떻게 나타날지 한번 보자 !
catch()
메서드를 이용해 에러를 처리하는 방법function getData() {
return new Promise( function ( resolve, reject ) {
resolve( ‘ 성공 ! ‘ )
});
};
getData().then(function ( ressolve, reject) {
console.log(result); // 성공 !
throw new Error(“Error in then()”);
}).catch(function(err) {
console.log(‘then error : ‘, err); // then error : Error: Error in then()
});
발생한 에러를 console.log()
에 출력한 모습이다.
에러를 잘 처리하자.
QUEUE
(큐)Javascript
의 동작원리를 공부하면 당연시 알게 되는
이벤트 루프 ( event loop
) 관련된 이야기를 잠깐 하려고 한다.
비동기상황에 큐들을 구분해 보자면,
Macrotask Queue
( 매크로테스크 ) : 타이머 함수, 이벤트 리스너
사용자가 어떤 행동을 행할 시 또는 몇초뒤에 이벤트가 행하는 기능들
Microtask Queue
( 마이크로테스트 ) : Promise
, process.nextTick
, queueMicrotask
, MutationObserver
만 포함된다.
나머지는 Macrotask Queue
매크로테스크로 구분한다.
❗️TMI TIP❗️
이런일이 벌어질 일은 절대 없겠지만,,
비동기 코드 중.
setTimeout
가 만약 0초,Promise
중에는
Microtask Queue
( 마이크로테스트 )인Promise
가 먼저 호출된다.
async & await
자바스크립트의 비동기 처리 패턴 중 가장 최근에 나온 문법이다.
기존의 비동기 처리 방식인콜백함수
와Promise
의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있도록 도와준다.
기본 문법
async function 함수명() {
await 비동기_처리_메서드_명();
};
async
라는 예약어를 붙인다.HTTP
통신을 하는 비동기 처리 코드( Promise
) 앞에 await
을 붙인다.❗️주의❗️
비동기 처리 메서드가 꼭Promise
객체를 반환해야await
가 의도한 대로 동작한다.
(await
의 대상이 되는 비동기 처리 코드는axios
등Promise
를 반환하는API
호출 함수다. )
Promise Chaining
부분에 ❗️여기서 잠깐 하나의 TIP❗️에서 이야기 했지만,
Promise
내부에서await
전에 선언한, 호출한 것은 동기이다.Promise
내부함수는 동기- 서버에 연결하기 위한 API를 사용하는 부분이 비동기
async & await
사용 예제// 프로듀서
function getUserList() {
return new Promise(function(resolve, reject) {
const userList = ['user1', 'user2', 'user3'];
resolve(userList);
});
}
// 클라이언트
async function fetchData() {
const list = await getUserList();
console.log(list);
}
fetchData() // [ ‘user1’, ‘user2’, ‘user3’ ]
그럼 async & await
의 예외 처리는 어떻게 할까 ?
async & await
예외 처리async function fetchData() {
try {
const userInfo = await getUserList();
console.log(userInfo);
} catch (error) {
console.log(error);
}
}
fetchData();
async & await
에서 예외 처리 방법은 try & catch
이다.Promise
에서 에러 처리를 .catch()
를 사용했 듯이 async & await
에서는 catch {}
를 사용하면 된다.catch
로 잡아낼 수 있다.error
객체에 담기기 때문에 에러의 유형에 맞게 에러 코드를 처리하면 된다.🌟 Promise
와 async & await
을 잘 적용해서 사용하는 것이 제일 BEST 인 것 같다.
async & await
를 for loop
에서 사용하기배열의 요소를 돌면서
ajax
통신을 하는 등.. 비동기 작업할 때가 있다.
loop
을 돌 때에는for
,forEach
를 많이 쓰는데
for
,forEach
내부에async&await
비동기 처리를 하게 되는데, 이때 버그가 발생한다.
❗️문제의 코드❗️
const params = [1, 2, 3, 4];
const resArray = [];
params.forEach(async param => {
const res = await axios.get(`DATA_URL`);
resArray.push(res.data);
};
console.log(resArray); // []
이렇게 코드를 작성하게 되면
for
, forEach
에서는 모든 비동기 작업이 끝나는 것을 대기하지 않는다.
for of 사용 방법
for await of
const = params = [1, 2, 3, 4];
const resArray = [];
for await ( const param of params ) {
const res = await axis.get(`DATA_URL/?id=${param}`);
resArray.push(res.data);
};
console.log(resArray); // [x, x, x, x];
for of / for in 방법
for await of
와 비슷한 방식이다.for of
또는 for in
으로 비동기를 제어할 수 있다.for ( const param of params ) {
const res = await axios.get(`DATA_URL/?id=${param}`);
resArray.push(res.data);
};
for ( const index in params ) {
const res = await axios.get(`DATA_RUL/?id=${params[index]`);
resArray.push(res.data);
};
Promise
메서드이 부분 따로 업로드를 해야겠다.
지금은 간단한 사용법만 작성하고, 아 - 그냥 이런 메소드가 있구나 ? 하고 넘어가면 될거 같다.
Promise
의 병렬과 직렬에 대해 공부해야겠다.
업로드를 한다면, 여기에 링크를 달아 놓을 예정이다.
🌟all
, race
를 비교하고 이해하면 직렬과 병렬 차이를 알 수 있다.🌟
1. Promise.all([])
2. Promise.allSettled([])
3. Promise.race([])
이 메소드를 사용하는 이유는❓
Promise.all()
을 사용한다.사용 법을 보자.
// Promise.all()
const res = await Promise.all(
paramsList.map(async params => this.$api.getAll(params));
);
console.log(res); // [결과1, 결과2, 결과3]
모든 비동기가 끝나면 코드가 흐른다.
❗️ 한 번에 비동기가 실행되기 때문에 결과가 순서대로 들어가지 않는다 ❗️
🌟🌟🌟비동기의 결과가
param
의 순대로 들어간다면promise.all()
이 아닌
async & await
으로 하나 끝나면 결과 넣고, 두번 째 시작하는 방법으로 로직을 구현해야한다.
또는,promise.all()
로 순서없이 배열에 추가 후, 원하는 기준으로sort(정렬)
하는 방법도 있다.
AJAX
는 async & await
이 나오기 전에 사용되던 HTTP를 통신하는 도구 이다.
최근에도 많이 쓰이기도 한다.
여러 개발자들이 사용하는 도구들은
AJAX
axios
fetch
가 있다.
각 종류마다 장단점이 있다.
번외 느낌으로 Promise
를 AJAX
에 어떻게 적용시키는지 사용법을 적어본다.
id
를 통해 render
될 자료를 비동기로 받아와 id
에 해당 하는 정보 가져와서 console.log
에 출력하는 상황이다.const dataURL = `https://url`;
let dataId;
let dataInfo = [];
Promise.requset1 = () => {
return new Promise( function ( resolve, reject ) {
let xhr = new XMLHttpRequest();
xhr.open(“GET”, dataURL, true);
xhr.onload = function() {
if ( xhr.status === 200 ) {
ressolve(dataId);
} else {
reject( “request1 Error : “ + xhr.status );
};
};
xhr.send(null);
});
};
Promise.request1().then(data => console.log(data));
id
를 request2
함수의 인자로 전달해 해당 id
를 dataInfo
배열에 담는다.resolve
가 되지 않도록 해준다.Promise.request2 = info => {
return new Promise((resolve, reject) => {
let i = 0;
function userInfo() {
if ( i < 30 ) {
let xhr = new XMLHttpRequest();
xhr.open(“GET”, url, true);
xhr.onload = function() {
if ( xhr.status === 200 ) {
dataInfo.push(JSSON.parse(xhr.responseText));
i++;
userInfo();
} else {
reject(“request2 Error: “ + xhr.status);
}
};
} else {
resolve(dataInfo);
}
};
userInfo();
});
};
잘 담아서 확인했다면 promise pending
후 userInfo
가 console.log()
에 찍힌다.
Promise.request1().then(data => {
Promise.request2(data)
.then(function(userInfo) {
console.log(userInfo);
})
.catch(err => console.log(err));
});