프론트엔드에서의 비동기

jiny·2024년 4월 2일
2

JavaScript

목록 보기
3/3
post-thumbnail

Intro

이번 미션의 주요 목표는 웹 프론트엔드에서의 비동기에 대해 이해하고, API 통신을 처리할 때 고려해야 하는 다양한 문제를 직접 경험해보면서 해결 방법을 고민해보는 것입니다.

이번 영화 리뷰 미션의 핵심 주제는 비동기 였다.

그래서 그런지 항상 async/await만 사용해오던 나는 비동기의 필요성에 대해 알고싶어졌다.

그렇기 때문에 먼저 사전적 정의, 프로그래밍 관점에서의 동기와 비동기에 대해 먼저 알아보며 의미에 대해 살펴보려고 한다.

그리고 프론트엔드에서 비동기를 활용하는 대표적인 예시인 AJAX에 대해 가볍게 살펴볼 것이다.

마지막으로 미션 내에서 AJAX가 활용된 예시를 살펴보려고 한다.

❌ 이번 글에서는 callback, promise, async/await에 대해선 다루지 않을 예정입니다!

동기와 비동기의 사전적 정의

정의를 알아보기 전에 예시를 하나 들어보려고 한다.

카페에 와서 음료를 주문했다고 가정해보자.

손님이 10명 정도 대기 중 이라고 할 때 내가 주문한 음료를 받으려면 모두 음료를 받을 때까지 대기 해야 한다.

다시 말해, 모든 손님이 순차적으로 1명씩 음료를 받아야 음료를 받을 수 있다.

그렇다면 이런 상황을 떠올려볼 수 있을거 같다.

유명한 대형 카페에 가서 약 100명의 사람들이 한명씩 음료를 받아야 한다면?

모든 사람들이 한명씩 음료를 받아야 한다면 음료를 받기 까지 상당한 시간이 소요 될 것이다.

지금까지의 설명이 동기 라고 말할 수 있다.

기다리는 시간이 길어지면 길어질수록 느끼는 불편함이 커질거 같다.

그러면 이제 다른 상황을 들어보려고 한다.

위 사진처럼 손님이 주문 하면 진동벨을 받는다고 가정해보자.

더 이상 손님은 음료가 나올 때까지 앞에서 기다리지 않아도 된다.

또한, 벨이 울릴 때 음료를 받아오면 기다리는 시간을 줄일 수 있을거 같다.

마지막으로, 기다리는 시간이 길어질 경우 그 시간에 다른 작업을 수행할 수 있을거 같다.

지금까지의 설명이 비동기 라고 할 수 있다.

동기와 비동기는 다음과 같은 사전적 정의를 가진다.

동기

동시에 존재하는

비동기

동시에 존재하지 않는

동기(Synchronous)의 경우 일의 작업들이 동시에 존재하는 것을 의미하며, 모든 작업들이 순차적으로 처리 된다.

반면, 비동기(Asynchronous)는 일의 작업들이 동시에 존재하지 않기 때문에, 모든 작업들이 비순차적으로 처리 된다.

이제 사전적 의미를 어느정도 파악했으니 프로그래밍 관점에서 더 자세히 파악해보려고 한다.

프로그래밍 관점에서의 동기와 비동기

동기

현재 작업의 요청과 응답이 동시에 발생하는 것

프로그래밍 관점에서 동기를 사용하는 것의 대표적인 예시는 코드 실행이라고 할 수 있을거 같다.

프로그램이 동기적으로 실행된다.

동기적으로 실행한다.는 의미는 현재 작업이 응답이 발생(현재 코드 실행)함과 동시에 다음 작업을 요청(다음 코드 실행)한다는 것이다.

다음 그림을 한번 살펴보자.

그림 내 존재하는 파란색 막대기와 주황색 막대기를 각각 함수라고 생각해볼 수 있을 것 같다.

그리고 파란색 막대기 끝은 함수의 실행 종료를, 주황색 막대기 끝은 함수의 실행 시작을 의미한다.

즉, 프로그래밍 관점에서 동기는 특정 함수의 실행이 끝나면 다른 함수가 바로 실행 됨을 의미한다.

예시 코드를 통해 확인해보자.

function start() {
	console.log('start')
}

function finish() {
	console.log('finish')
}

start();
finish();

// start
// finish

함수 실행 순서는 다음과 같이 전개 된다.

  1. start
  2. finish

각 작업은 출력 순서에서 확인할 수 있듯, 한 함수의 작업이 종료되면 다음 함수의 작업이 실행된다.

다시 말해, 일이 순차적으로 진행되는 것을 확인할 수 있다.

동기적인 실행의 문제점

그렇다면 비동기라는 개념을 왜 적용하게 되었을까?

다음 예시 코드를 한번 확인해보자.

function sleep(callback, delay) {
  const delayUntil = Date.now() + delay;

  while (Date.now() < delayUntil);
  
  callback();
}

function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
}

sleep(foo, 3 * 1000);

bar();

// foo(3초 후)
// bar

bar 함수는 sleep 함수의 실행이 끝나고 호출되기 때문에 3초 이상 블로킹 된다.

즉, 다음과 같이 sleep 함수로 인해, 다음에 실행 되어야 할 foo가 3초 이상 지연된다는 것이다.

일이 지연될수록 전체적인 일처리 시간이 증가한다.

이를 정리하면 위와 같은 문장으로 정리해볼 수 있다.

0.1초라도 줄이기 위해 코드를 최적화하는 개발자 입장에선 꽤 치명적으로 보인다.

그렇다면 비동기 관점에서 이러한 문제를 어떻게 해결해볼 수 있을까?

비동기

function foo() {
  console.log('foo');
}

function bar() {
  console.log('bar');
}

setTimeout(bar, 3 * 1000);

foo();

비동기적으로 코드를 짜본다면 다음과 같이 만들 수 있다.

실행 순서는 다음과 같이 전개 된다.

  1. setTimeout 인자에 있는 bar는 3초 뒤 실행되도록 예약된다.
  2. 지연되는 동안 foo는 실행 된다.
  3. 3초가 지나면 bar가 실행 된다.

bar가 3초 뒤에 실행되더라도 먼저 foo가 실행되는 것을 알 수 있다.

이를 정리하면 다음과 같이 말해볼 수 있다.

한 작업이 처리되는 동안 다른 작업이 처리 될 수 있다.

즉, 현재 작업이 비동기적으로 처리되는 것을 알 수 있다.

동기적인 상황에선 3초를 기다리는 동안 blocking 되었지만, 비동기를 도입한 덕분에 foo가 더 이상 지연되지 않고 실행될 수 있다.

이제 프론트엔드에서는 비동기를 어떻게 사용하는지에 대해 알아보자.

프론트엔드에서의 비동기

웹 프론트엔드에서 비동기를 사용하는 대표적인 예시는 AJAX이다.

AJAX(Asynchronous JAvascript XML)

비동기 방식으로 데이터를 주고 받기 위한 자바스크립트 기술

그렇다면 프론트엔드 내에서 왜 AJAX를 도입하게 되었을까?

이전 웹 페이지의 동작 방식 때문이었다.

현재와 달리 옛날 웹은 다르게 동작했었다.

  1. HTML 태그로 시작해 HTML 태그로 끝나는 완전한 HTML을 서버로부터 전송 받는다.
  2. HTML을 받아 웹 페이지 전체를 처음부터 다시 렌더링

이런 동작 방식의 경우, 화면이 전환(페이지 이동)될 때 마다 서버로부터 새로운 HTML을 전송 받아 웹 페이지 전체를 처음부터 다시 렌더링하게 된다.

이는 다음과 같은 문제점들을 야기한다.

  1. 불필요한 데이터 통신이 발생 한다.

  2. 화면 전환 시 순간적으로 깜빡이는 현상(fouc)이 발생 한다.

  3. 서버로부터 응답이 있을 때 까지 다음 처리는 블로킹 된다.

이러한 문제점들을 해결하기 위해 AJAX를 사용해 볼 수 있다.

AJAX의 특징은 아래와 같이 정리해볼 수 있다.

  1. HTML, XML, JSON 등 다양한 데이터를 주고 받을 수 있다.

  2. AJAX를 위해 XMLHttpRequest 객체를 사용한다.

AJAX 방식으로 비동기 통신하는 예제 코드를 확인해보자.

function getData() {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    } else {
      console.error('Error: ' + xhr.status);
    }
  }
}

const xhr = new XMLHttpRequest();

xhr.open('GET', 'https://api.example.com/data', true);

xhr.onreadystatechange = getData

xhr.send();

현재 코드 흐름은 다음과 같이 전개 된다.

  1. XMLHttpRequest 객체를 만든다.
  2. xhr.send를 통해 HTTP Request를 보낸다.
  3. 서버가 응답이 오면 등록해 둔 callback(getData)을 통해 데이터를 받는다.

즉, 순차적으로 실행되는 것이 아닌 send를 통해 HTTP Request를 보냈을 때 연속적으로 Response 받는 것이 아닌, 특정 시점에서 Response를 전달 받게 된다.

그 특정 시점을 callback 함수를 통해 제어함으로써, 원하는 타이밍에 데이터를 전달받게 되고, 그 타이밍이 다가오는 동안 다른 작업을 처리할 수 있게 된다.

이 과정 내에서는 비동기 특성에 의해 서버와 HTTP 통신을 할 때 다른 작업들이 blocking 되지 않게 된다.

즉, send를 통해 request를 보낸 후 데이터를 전달 받을 때 까지 UI Rendering, Fallback 처리 등 다른 작업들을 처리하는 것이 가능해진다.

또한, 데이터를 이제 HTML이 아닌 JSON 형식으로 전달 받기 때문에, 부분적인 렌더링이 가능해졌다.

AJAX의 이점을 영화 리뷰 미션 내에서 한번 확인해보자.

화면을 확인해보면, 영화 목록을 받아오기 위해 데이터 패칭을 하는 동안에도 검색 버튼을 통해 다른 제목의 영화를 요청하는 것을 확인할 수 있다.

동기적인 방식으로 처리했다면 이전 까지의 인기순 영화 목록을 다 불러온 후 검색 결과 영화 목록을 가져올 것이다.

하지만, AJAX를 통해 비동기적인 방식으로 처리함으로써, 인기순 영화 목록을 다 불러오기 전 검색 결과 영화 목록 가져오는 작업을 처리할 수 있게 된다.

레퍼런스

0개의 댓글