비동기 동기 처리에 대해 알아보려 합니다.
프론트엔드의 면접 단골질문이며, 네트워크 통신과 더불어 기본 지식이라고 생각했습니다.
백엔드와 네트워크 통신을 할 때 await/async
를 적어 넣으면서도 자바스크립트의 비동기처리 동기처리에 대해 설명하시오 !
하면 설명하기 어려운 경우가 많았어요.
아니 그냥 어려웠습니다! 그래서 이번에 이것에 대한 개념을 잡아보고 가겠습니다.
자바스크립트는 일단 싱글스레드 언어로 한번에 하나밖에 처리하지 못하는 것을 의미합니다.
이것이 바로 동기 처리 Sync
입니다. Call Stack
을 담는 공간이 하나라고도 표현합니다.
한번에 한줄의 코드씩 위에서 아래로 순차적으로 실행하는 것이 자바스크립트의 기본 동작 방식입니다.
console.log(1);
console.log(2);
console.log(3);
// 1 -> 2 -> 3
그러나 오래 걸리는 작업이 있으면 제껴두고 다른거 부터 처리하는 방식을 우리는 항상 사용해 왔습니다.
서버로 통신하거나 setTimeout
함수가 대표적인 예시인데 이것은 비동기 처리Async
라고 합니다.
하지만 이건 자바스크립트가 그걸 처리해 주는게 아니라 자바스크립트를 실행하는 브라우저 덕분에 이런 실행이 가능한것 입니다 !
setTimeout(()=>{console.log('오래걸림'),1000);
btn.addEventListner('click',()=>{})
axios.get('url')
대표적인 오래 걸리는 작업들입니다.
이런 작업들은 비동기 처리를 해줘서 WebAPI
로 보내주고 거기서 따로 처리해줍니다.
이게 브라우저의 역할이에요.
대표적으로 많이 사용하는 크롬 브라우저의 엔진의 경우 멀티 스레드로 구현이 되어있다 합니다.
우리는 콜백함수를 이용하여 코드를 우리가 원하는대로 실행시킬수 있습니다.
console.log(1);
setTimeout(()=>{},1000)
console.log(2);
위 코드를 실행하면 1을 출력하고 1초 쉬고 2를 출력할까요?
정답은 아닙니다
console.log(1);
setTimeout(()=>{console.log(2)},1000)
이렇게 setTimeout
합수에 콜백함수를 넣는 공간에 원하는 코드를 넣어줄때 비로소 1을 출력 1초 쉼 2를 출력 이라는 결과를 만들수 있는거죠?
const 함수1 = (콜백) => {
console.log(1)
콜백()
}
const 함수2 = () => {
console.log(2)
}
직접 콜백함수를 디자인 할수도 있죠..
콜백함수랑 비동기랑 무슨 상관이지 ?
라고 하면 콜백함수는 비동기와 관련이 없습니다 ! 단지 자바스크립트에서 사용할수 있는 하나의 패턴입니다.
콜백함수에만 의존하다 보면 우리는 이런 실수를 할수 있어요.
첫째로는 함수에 매번 파라미터를 뚫어줘야 한다는 것이고,
그렇다고 콜백함수에 직접 함수 선언을 해도 되는데 코드 가독성이 현저히 떨어집니다.
그리고 무한 콜백 지옥에 빠질수 있습니다.
첫째함수(function(){
둘째함수(function(){
셋째함수(function(){
//어쩌구..
});
});
}):
그래서 순차적 코드를 사용하기 위해 Promise
라는 ES6
신문법이 존재합니다.
콜백함수처럼 하나의 디자인 패턴입니다
const Promise = new Promise((성공, 실패)=>{
// 실행할 함수
// ex : axios요청같은 실패 가능성이 있는 코드
});
Promise.then(()=>{
//성공 시 실행할 함수
}).catch(()=>{
//실패 시 실행할 함수
}).finally(()=>{
//성공 실패 관계 없이 실행할 함수
});
그래서 이걸 어따쓰냐?
실패 가능성이 있는 코드를 주로 작성합니다 ! 위의 예시처럼 axios
요청이 있겠죠.
콜백 함수에 비해 간결하고 콜백지옥에 빠질일도 없습니다.
그리고 성공 실패 판정까지 내려주는 프로미스 패턴 칭찬해요!
그러면 비동기 처리를 Promise가 해주는건가?
이것도 아닙니다. 콜백 함수처럼 그저 함수 디자인 패턴일 뿐이에요..
우리가 흔히 알고있는 fetch
함수가 Promise 패턴으로 제작이 되어있다는 것을 써보신 분이라면 알수 있을겁니다.
Promise
를 사용할때 then
과 catch
를 이용해서 다음에 동작할 코드를 작성해주는데
ES8
신문법으로 async/await
패턴이 또 등장합니다!
이 둘은 대부분의 상황에서 세트로 움직입니다.
const getTodo = async () => {
const Promise = new Promise((성공, 실패)=>{
// 실행할 함수
});
try{
const result = await Promise
console.log(result)
//성공
}catch{
//실패
}
}
getTodo.then(()=>{
//실패 코드 작성이 불가능
})
async 키워드를 입력하여 async함수임을 선언해줍니다.
그 안에서 await 키워드를 사용해 Promise의 결과가 반환될때 까지 기다립니다.
위 패턴은 제가 자주 사용하는 패턴인데 axios 혹은 fetch는 Promise 기반으로 만들어져 있습니다.
즉 대체가 가능하단겁니다.
const submitCode = async () => {
try {
const result = await defaultAxios.post('ide/run', {
uuid: uuid,
questionId: questionId,
requestCode: code,
language: language,
});
console.log(result);
} catch (error) {
console.log(error);
}
};
제가 얼마전에 짠 코드인데 저는 axios가 Promise 기반이고 그것은 단지 디자인 패턴이라는 것도 모르고 코드를 짯습니다..
하지만 항상 사용하던 것이기 때문에 다시 천천히 정리해보니 이해가 금방 되고 저의 코드에 대해 설명할수 있게 되었습니다.
비동기에 대해서는 위에서 설명했듯이 타이머 , 네트워크 요청 , 파일 입출력 , 이벤트 처리 오래 걸리는 작업을 우리는 WebAPI라고 합니다.
이들은 시간을 많이 사용하거나 실패 가능성이 있기 때문에 브라우저가 멈추거나 대기시간동안 다른 동작을 전혀 하지 못하게 될 수도 있죠
그 이유는 자바스크립트는 한번에 한가지일밖에 못하는 싱글스레드 언어이기 때문입니다.
그런 이유로 생기는 성능 저하를 방지하기 위하여 WebAPI 작업은 브라우저에서 자동으로 처리해 줍니다.
브라우저는 멀티 스레드로 구현되어 있어 여러가지 일을 한번에 처리할수 있습니다.
그렇게 자바스크립트에서 처리할수 있는 것은 자바스크립트 엔진에서 싱글스레드로 처리하고
WebAPI에 대해서는 브라우저에서 처리가 되면서 비동기 작업이 이루어지게 되고 멀티스레드로 작동할수 있게 되는겁니다.
그러나.. 완벽한 멀티스레딩이 아닙니다.
네트워크 통신을 예로 들어 url로 요청을 보내는 코드에 대헤서는 WebAPI에서 처리해주지만
그 후에 데이터를 가져와서 처리해주는 콜백함수는 다시 자바스크립트엔진에서 처리하기 때문입니다.
여기까지 보면 멀티스레딩이 정답이라 느낄수 있겠지만 너무 많은일을 한번에 처리하려고 하면 동시성 문제로 성능 저하가 이루어질수 있습니다.
그래서 자바스크립트는 이런 방식을 택한것 이구요.
요청보내기 WebAPI
-> 요청을 보내서 가져온 데이터 가공 JS
-> 가공한 데이터를 유저에게 보여주기 JS
대략 이런 흐름으로 볼수 있겠습니다.
여기서 우리는 생각을 해야합니다 ..
그렇다면 요청을 보냈어 -> 결과를 가져왔어 가공해야지 -> 그 결과로 무언가 유저에게 보여주고 싶어 !
라는 흐름에서 1번이 완료되지 않고 2번과 3번이 멀티스레딩으로 동시에 처리된다면 문제가 되겠죠 !?
데이터를 가져오는 중인데 없는 데이터를 가공해서 유저에게 보여주려 하고 있으니까 말이죠 !
여기서 저희는 디자인 패턴을 이용하여 기다렸다가 데이터에 대한 가공을 할수 있게 되는겁니다.
그래서 콜백함수
or then
or async/await
가 필요한 것이지요 !
요청을 기다렸다가 성공했을때 보여줄 화면, 실패했을때 보여줄 화면을 우리는 이러한 디자인 패턴을 이용하여 쉽게 구현할수 있습니다.
디자인 패턴에는 정답은 없습니다.
간단하다면 콜백으로 처리할수 있겠고 then()
, async/await
모두 비슷한 행위를 하기 때문이죠..
브라우저에 대해 정말 깊은 내용을 많이 보았지만 동기 비동기 디자인패턴 만 생각해도 나의 코드에 대한 근거가 어느정도 생기는것 같네요.
흐름이 비동기 동기로 시작해서 갑자기 디자인패턴이 나왔기 때문에 뭔가 뜬금없다고 느껴지시기도 했겠지만
위에서 둘의 관계에 대해 설명했듯이 둘은 많은 연관이 있었습니다.
저의 흐름이 이해가 잘됐으면 좋겠네요.
저도 모르는게 많기 때문에 잘못된 정보가 있으면 글과 머릿속에서 수정하겠습니다.
쓰다보니 지쳐서 저는 2000..