본문 바로가기

[자바스크립트] 비동기 작업의 시간을 줄여보기

 

 

 

들어가며

본격적으로 개발을 하면서, 다양한 API를 개발하곤 합니다. 한 번은, 검색 API를 개발하면서, 응답속도가 3초 이상이 걸리는 로직이 있었습니다. 간단한 로직이었음에도 왜 3초씩이나 걸리는지 제대로 알지 못했습니다. 지금 생각해보면, 비동기에 대한 이해가 없어서 생긴 문제였습니다. 지금부터라도 비동기에 대해 더 자세히 공부해야겠다고 생각했습니다. 아래 글은 이 글을 바탕으로 작성됐습니다.

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

await의 숨은 병목

async, await을 활용하면 비동기 처리를 동기적으로 처리하게끔 코드를 작성할 수 있습니다. 하지만, 이 과정에서 await을 마구잡이로 사용하게 된다면, 코드를 처리하는 시간이 오래 걸릴 수 있습니다. 

 

 

const book = await read(param); // 1000ms
const shop = await readSomething(param); // 500ms
const {key} = await readThat(param); // 300ms
const result = await checkValidKey(key); // 900ms

if(result) {
	console.log('end')
}

 

 

예를 들어 await을 많이 활용한 코드를 모두 처리하려면, 2,700ms를 기다려야 합니다. 이 시간을 줄일 수는 없을까요? 이에 대해 알아보겠습니다.

 

 

const [book, show, {key}] = await Promist.all([
	read(param), // 1000ms
    readSomething(param), // 500ms
    readThat(param) // 300ms
]);

const result = await checkValidKey(key); // 900ms

if(result) {
	console.log('end')
}

 

시간을 단축하기 위해서 Promise.all을 사용했습니다. 이 로직을 통해 이제는, 1,900ms만 기다리면 됩니다. 800ms나 줄였습니다. 하지만 아직 개선의 여지가 있어 보입니다.

 

const [book, show, result] = await Promist.all([
	read(param), // 1000ms
    readSomething(param), // 500ms
    checkValidKey((await readThat(param)).key) // 1200ms
]);

if(result) {
	console.log('end')
}

 

자, 이제 1200ms면 모든 작업을 끝내고, end를 볼 수 있습니다. API의 응답 시간 개선 없이 1700ms를 단축했습니다. 거의 1분이라는 시간을 단축시킬 수 있었는데, 같은 로직의 코드이지만 처리하는데 왜 이렇게 시간이 오래 걸렸던 것일까요?

 

async, await을 사용하는 목적은 코드를 동기식으로 작성하기 위함입니다. 1개만 있을 때에는 크게 문제가 되지 않지만, 두 개 이상의 await을 사용할 시에는 의존관계를 면밀히 살펴봐야 할 필요가 있습니다. 예를 들어, read와 readSomething은 '독립적인 관계'를 가진 함수입니다. read를 먼저 호출하거나, readSomething을 먼저 호출하나 서로에게 영향을 주지 않습니다. 반대로 readThat과 checkValidKey는 '의존적인 관계'를 가진 함수입니다. 반드시, readThat이 선행되어야, checkValidKey를 호출할 수 있습니다. 또, read와 readSomething은 readThat, checkValidKey 2개의 함수와 '독립적인 관계'를 가진 함수입니다. 세 가지 작업을 동시에 비동기적으로 진행하여도 무방합니다. '의존적인 관계'에서는 await을 사용하여 동기식으로 작성하여도 무방합니다. 왜냐하면 동기적으로 동작해야만 하는 코드이기 때문입니다. 하지만 '독립적인 관계'에서는 위의 예시처럼 Promise.all을 사용하거나, 아래와 같이 사용할 수도 있습니다.

 

 

 

const run1 = read(param); // 1000ms
const run2 = readSomething(param); // 500ms
const run3 = readThat(param); // 300ms

const book = await run1; // 1000ms wait
const shop = await run2; // 0ms wait
const {key} = await run3; // 0ms wait

const result = await checkValidKey(key); // 900ms

if(result) {
	console.log('end') // Total 1,900ms
}

 

위의 예시에서, run1, run2, run3은 동시에 시작합니다. run2 및 run3은 run1보다 빨리 끝났지만, run1이 아직 끝나지 않아 run1이 끝날 때까지 기다리게 됩니다. 

 

const run1 = read(param); // 1000ms
const run2 = readSomething(param); // 500ms
const run3 = readThat(param); // 300ms

const {key} = await run3; // 300ms wait
const run4 = checkValidKey(key);

const book = await run1; // 700ms wait
const shop = await run2; // 0ms wait

const result = await checkValidKey(key); // 200ms

if(result) {
	console.log('end') // Total 1,200ms
}

 

'후속 작업'이 존재하면, 그것을 우선적으로 await 하여, 가능한 한 빨리 후속 작업을 호출할 수 있도록 하면 됩니다. 일반적인 경우에, await을 사용하는 것이 더 깔끔한 코드를 작성할 수 있도록 해줍니다. 또한 대부분의 경우에 API 호출에 오래 걸리지 않습니다. 그렇기 때문에, 적극적으로 await을 사용하는 것을 추천드립니다. 하지만, API가 느리거나, 최적화를 원하는 경우(라고 쓰지만, 100ms라도 로딩 시간을 줄일 수 있으면 줄이는 것은 매우 좋습니다)에는, 대부분의 경우에 Promise.all 같은 기법을 사용해야 합니다.

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

마치며

비동기를 제대로 이해하지 못해서, await를 남발하는 경우가 있었는데, 이제 조금은 로직을 효율적으로 구성할 수 있을 것 같습니다. 효율성을 꾸준하게 생각할 수 있는 개발자가 되고 싶습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

출처

 

await의 함정, 숨은 병목을 찾자

자바스크립트에서 async await을 사용하면 좀 더 깔끔한 코드를 작성할 수 있습니다. 하지만, 적절하게 사용하지 않으면 적지 않은 시간을 사용해야할 수도 있습니다. 특히, 여러개의 await을 사용할

jaeheon.kr