'https://learn.codeit.kr/api/employees'

이 주소로 들어가보면 데이터들의 배열을 볼 수 있었다.

주소 뒤에 /1 이렇게 ID를 붙이면 특정 직원에 대한 정보를 가져올 수 있는데, 1-10번에 해당하는 직원 정보를 각자 가져와서 출력해보자.

URL 주소 뒤에 /1 을 붙이면 1번 직원의 정보만 볼 수 있다.

//for문을 사용하는 printEmployees 함수를 정의해보자.
async function printEmployees() {
  for (let i = 1; i < 11; i++) {
    const response = await fetch(`https://learn.codeit.kr/api/employees/{i}`);
    const data = await response.json();
    console.log(data);
  }
};

printEmployees();

템플릿 문자열 (${i}) 을 이용해서 리퀘스트를 내보내고 있다. 이걸 실행해보면 직원 정보가 하나씩 잘 출력된다.

1번 직원부터 10번 직원까지 하나씩 잘 출력됐다. 총 2.286초 소요

하지만 이 코드는 사실 비효율적인 코드이다. 

작성한 for문(좌)과 for문을 풀어서 쓴 형태(우)

이 코드는 수많은 await문을 순차적으로 실행한다. 첫번 째 await문을 기다리는 동안 함수 바깥으로 나갔다가 filfilled가 되면 다시 돌아와서 두번째 await을 기다리고 함수 바깥으로 나갔다가 filfilled가 되면 다시 돌아오고… …  …  …  

like 무한굴레

이런식으로 비동기 작업을 하나씩 순서대로 기다리기 때문에 오래 걸리는 것이다. 비동기 작업을 하나하나 기다리지 않고 한꺼번에 리퀘스트를 보내고 결과를 기다리는 효율적인 방법이 없을까? 그러려면 코드를 변경해주어야 한다.

//파라미터로 id를 받게 하고 
async function printEmployee(id) {
  const response = await fetch(`https://learn.codeit.kr/api/employees/{id}`); //id를 넣어준 다음
  const data = await response.json();
  console.log(data);
};

//for문을 함수 바깥으로 옮겨준다.
for (let i = 1; i < 11; i++) {
  printEmployee(i);
};
//그리고 함수 이름도 printEmployees에서 printEmployee로 바꿔준다.

언뜻보면 이전과 비슷한 코드처럼 보이지만 이번에는 async 함수를 10번 호출하고 있다. 이 방법이 훨씬 효율적이다.

결과가 거의 바로 나왔는데 위에서 소요됐던 2.286초 보다 빠른 0.491초 걸렸다.

시간이 훨씬 빨랐지만 한가지 주의해야 될 점은 순서는 뒤죽박죽이라는 점이다. 왜 이 방법이 더 빠른걸까? 

이번 for문을 풀어서 쓰면 이런 모습이다.

  1. 가장 먼저 printEmployee (1) 을 실행한다. 
  2. 함수 안에서 fetch 리퀘스트를 보내고 기다리는 동안 바깥으로 나온다.
  3. 그 아래에 있는 코드는 printEmployee (2)이다. 그래서 fetch printEmployee/2의 리퀘스트를 보내고 다시 밖으로 나온다.
  4. 이런식으로 printEmployee 3, 4, 5, 6, 7의 리퀘스트를 보낸다.
  5. 중요! 리퀘스트의 결과를 기다리지 않고 바로 다음 URL에 리퀘스트를 보낸다는 것이다. (10개의 리퀘스트를 거의 동시에 보내는 것)
  6. 리퀘스트를 거의 동시에 보냈기 때문에 리스폰스도 거의 동시에 돌아오고 결국 하나의 리퀘스트를 처리하는데 걸린 시간만큼만 기다리면 된다.
  7. 이 때 리퀘스트를 보낸 순서대로 리스폰스가 돌아온다는 보장이 없어서 출력 순서가 조금 뒤바뀌는 것이다.

비동기 작업을 처리하는 순서는 중요하지 않고 작업들을 최대한 빨리 끝내는게 목표라면 이 방법을 사용하는게 좋다.

리스폰스가 돌아온 다음에는 리스폰스를 파싱하고, 파싱 작업이 끝나는대로 데이터를 출력한다.

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

오류 처리하기 (try catch)  (1) 2024.12.23
async 함수의 리턴 값  (0) 2024.12.23
async 함수  (0) 2024.12.23
await 문법  (0) 2024.12.22
Promise  (0) 2024.12.22
//main.js
const response = await fetch('https://learn.codeit.kr/api/employees');
const data = await response.json();
console.log(data);

console.log('Task 2');

console.log('Task 3');

원래 비동기 프로그램의 목적은 비동기 작업의 결과를 기다리는 동안 다른 작업을 먼저 처리하는 것이다. fetch 결과를 기다리는 동안 아래에 있는 코드(작업)들을 먼저 처리해주면 좋을 것 같은데, 코드를 실행해보면 직원 데이터가 먼저 출력되고, Task 2, Task 3이 출력된다. 코드가 위에서부터 아래로 순서대로 실행되고 있는 것인데 Promise와 await으로 비동기 처리를 제대로 하려면 함수를 이용해야한다.

//asyncFunctions.js
export function printEmployees() {
  const response = await fetch('https://learn.codeit.kr/api/employees');
  const data = await response.json();
  console.log(data);
};

새로운 js 파일을 만들어 printEmployees라는 함수를 정의하고 main.js의 일부분을 긁어온다. 그런데 await에 빨간줄이 가있는걸 볼 수 있다.

await은 async 함수 내에서나 모듈(module)의 최상위 레벨에서만 사용할 수 있다고 한다. 일단 여기서 모듈이라는건 ES 모듈을 뜻하는데 package.json 파일에서 type을 모듈로 설정해 줬다. 

그래서 이 async-js 디렉토리 안에서는 파일들이 기본적으로 ES 모듈로 인식되기 때문에 함수 바깥에서도 await을 쓸 수 있었던 것이다. 만약 json 파일에서 type 모듈을 지우면 main.js 파일을 CommonJS 모듈로 인식하기 때문에 오류가 난다.
await으로 비동기 처리를 제대로 하려면 함수를 이용해야 하기 때문에 함수 바깥에서 await을 쓰는게 흔하진 않다. ES6 모듈 안에서만 이게 가능하다는 사실을 기억해두자.
다시 본론으로 돌아가서 async 함수 안에서만 사용할 수 있다는건 무슨 뜻일까? async 함수를 만들려면 function 앞에 async 키워드를 붙여주면 된다. 

//asyncFunctions.js
export async function printEmployees() {
  const response = await fetch('https://learn.codeit.kr/api/employees');
  const data = await response.json();
  console.log(data);
};

//참고, 화살표 함수 문법으로 async 함수를 정의하려면? async의 위치가 조금 다르니까 주의!

const printEmployeesArrow = async () => {
  const response = await fetch('https://learn.codeit.kr/api/employees');
  const data = await response.json();
  console.log(data);
}

그러면 아까 오류를 보이던 await의 빨간 줄이 더이상 보이지 않는다. 그럼 main.js 파일에서 printEmployees 함수를 가져와서 호출해 보자.

//main.js
import { printEmployees } from './asyncFunctions.js'
printEmployees();

console.log('Task 2');

console.log('Task 3');

실행해보면 이번엔 아까와 다르게 Task 2와 Task 3이 출력되고 그 아래에 직원 데이터가 출력됐다. 
코드의 실행 순서를 살펴보면

  1. 가장 먼저 printEmployees 함수를 import 하고 (main.js 1번째 줄)
  2. printEmployees 함수를 실행한다. (main.js 3번째 줄)
  3. 그러면 함수 안으로 가서 fetch를 실행 (asyncFunctions.js 2번째 줄)
  4. fetch는 prmise를 리턴하는데 앞에 await 문이 있기 때문에 promise가 fulfilled 상태가 될 때까지 기다린다. (asyncFunctions.js 2번째 줄)
  5. 여기서 중요한 사실!  async 안에서 await 함수를 쓰면 promise가 fulfilled가 될 때까지 기다리는 동안은 다시 함수 바깥으로 나와서 나머지 코드를 모두 실행한다.
  6. 나머지 코드 = main.js의 5, 7번째 줄  (그래서 printEmployee 함수 아래에 있는 코드들이 먼저 실행된 것)
  7. 그러다 promise 상태가 fulfilled 가 되어 성공 결과를 갖게 되면
  8. 다시 함수의 body로 돌아와 코드를 이어서 실행한다. (asyncFunctions.js 2-3번째 줄)
  9. 그런데 여기에 또 promise를 await 하는 부분이 있다. (asyncFunctions.js 3번째 줄)
  10. 그럼 다시 함수 바깥으로 나가서 코드를 실행한다. (main.js로 가지만 남은 코드는 없다.)
  11. 이번엔 실행할 코드가 더이상 없기 때문에 그냥 기다리다가 promise가 fulfilled 상태가 되면 다시 돌아와서 코드를 마저 실행한다. (asyncFunctions.js 3-4번째 줄)
  12. 콘솔창에 직원 데이터 출력 (asyncFunctions.js 4번째 줄)
그래서 이런 결과창을 갖게 되는 것이다.

 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

async 함수의 리턴 값  (0) 2024.12.23
async 함수 사용시 주의사항  (0) 2024.12.23
await 문법  (0) 2024.12.22
Promise  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22

fetch 함수는 Promise 객체를 리턴한다. 비동기 작업이 완료되면 promise가 결과값을 알려주는데 그 결과값을 받아오려면 await 문법을 쓰면 된다.

const response = await fetch('https://learn.codeit.kr/api/employees');

console.log(response);

이렇게 promise를 리턴하는 부분 앞에 await 키워드를 쓰면 되는데, 실행해보면 뭔가 복잡해보이는 것들이 출력된다. 이번에는 promise가 바로 리스폰스에 할당되는 것이 아니라 fetch 작업이 완료될 때까지 기다렸다가 fetch의 결과값이 리스폰스에 할당된 것이다.

원래 리스폰스는 복잡하게 생겨서 직원데이터를 얻기 위해서는 리스폰스(서버가 보내주는 응답)를 파싱(어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때 불러올 수 있게 하는것) 해야 한다. 그러기 위해선 리스폰스의 json 메소드를 쓰면 된다.

const response = await fetch('https://learn.codeit.kr/api/employees');
const data = response.json();
console.log(response);

json 메소드로 리스폰스를 파싱해서 데이터 변수에 저장해줬다.

실행 해본 결과

실행해보면 또 promise 라고 나오는데 json 메소드도 실행하는데 오랜 시간이 걸릴 수 있기 때문에 promise를 리턴하는 것이다. 그래서 여기 앞에도 await을 붙이면 직원 데이터가 잘 출력된다.

const response = await fetch('https://learn.codeit.kr/api/employees');
const data = await response.json();
console.log(response);

결과

참고로 await 부분을 하나의 표현식으로 생각해서 

const response = await fetch('https://learn.codeit.kr/api/employees');
console.log(await response.json());

이렇게 작성하는것도 가능하다. 일단 console.log( 이 부분 )을 실행하고 이거(await response.json())의 결과값을 출력하는 것이다. (하지만 이전 버전이 더 이해하기는 쉽다.)


promise와 await 문법의 동작 원리

fetch 함수는 promise를 리턴하고, promise는 3가지 중 하나의 상태를 갖는다.

  • Pending: 비동기 작업의 결과를 기다릴 때
  • Fulfilled: 비동기 작업이 성공적으로 완료됐을 때. 비동기 작업의 성공 결과를 결괏값으로 갖게 됨.
  • Rejected: 비동기 작업이 중간에 실패했을 때. 비동기 작업에서 발생한 오류를 결괏값으로 갖게 됨.

처음에는 Promise가 pending, 즉 미결정 된 상태이다. 그래서 이전처럼 promise를 바로 출력했을 땐 promise {<pending>}이라는 결과가 출력됐었다.


const result = await Promise;

// 예시
const response = await fetch('https://learn.codeit.kr/api/employees');

하지만 Promise 객체 앞에 await 키워드를 쓰면 Promise의 상태가 fulfilled 또는 rejected가 될 때까지 기다린다. (Promise 객체의 값을 가져오려면 항상 앞에 await을 붙여야 한다.)

  • Fulfilled(비동기 작업 성공적으로 완료)가 되면 Promise 객체의 결괏값을 리턴한다.
  • Rejected(비동기 작업 실패)가 되면 Promise 객체의 결괏값(오류)을 throw한다.

promise가 Fulfilled되는 경우로 생각해보자.

response 변수에 할당

비동기 작업이 성공해서 Fulfilled 상태가 되면, 비동기 작업의 성공 결과. fetch의 경우 리스폰스를 promise의 결과값으로 갖게 된다. 그리고 await은 promise의 결과값을 꺼내서 리턴해준다. 그래서 리스폰스가 리스폰스 변수에 할당되는 것이다.

response.json 역시 promise를 리턴하는데, 마찬가지로 처음에는 promise가 pending 상태지만, 앞에 await이 있기 때문에 파싱 작업이 끝날 때까지 기다린다. 파싱 작업이 끝나면 promise는 Fulfilled 상태가 되고, 성공 결과. 즉, 파싱된 데이터를 결과값으로 갖게 된다. 그리고 이 결과 값이 데이터에 할당되는 것이다.

data 변수에 할당

그래서 데이터를 출력하면 직원 데이터가 잘 출력된다.

출력된 직원 데이터

‼️ 결론 : Promise를 리턴하는 표현식이 있다면 그 앞에 await 키워드를 써서 결과값을 받아 올 수 있다. await을 쓰면 promise가 fulfilled 상태가 될 때까지 기다렸다가 결과값을 돌려주기 때문이다.

promise를 리턴하는 표현식 앞에 작성된 await

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

async 함수 사용시 주의사항  (0) 2024.12.23
async 함수  (0) 2024.12.23
Promise  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22

콜백의 한계점이라는 직전 포스팅에서 사용했던 코드는 

// 직원 데이터를 가져온 후 리스폰스를 파싱하고 데이터를 프로세싱 하는 예시
getEmployees((response) => {
  json(response, (data) => {
    groupEmployees(data, (result) => {
      console.log(result);
    });
  });
});

위 코드였다. getEmployees라는 함수를 이용해 웹 리퀘스트를 보내는 것을 흉내 냈었는데, 실제로 웹 리퀘스트를 보낼 때는 fetch() 라는 함수를 많이 사용한다. (fetch 함수는 promise 객체를 리턴)

const response = fetch('https://learn.codeit.kr/api/employees');

console.log(response);

위 URL은 직원 10명의 데이터가 있는 창이다. fetch는 코드로 리퀘스트(웹 브라우저가 서버에 요청)를 보내서 이 데이터를 받아오는 것이다.

URL로 접속해보면 나오는 화면

실행해보면 이렇게 나오는데

드래그 되어 있는 부분은 주의 메세지여서 무시해도 된다.

출력결과는 Promise { <pending> } 이 부분이다. fetch 함수가 Promise 라는걸 리턴해서 그 값이 리스폰스(서버가 보내주는 응답)에 저장된 것이다. 그렇다면 Promise는 무엇일까?

Promise는 비동기 작업이 완료되면 값을 알려 주는 객체로 작업이 완료되면 값을 알려줄 것을 "약속"하기 때문에 promise이다.

fetch는 오래 걸리는 작업이기 때문에 결과 값을 바로 돌려줄 수 없으니까 일단 promise를 돌려주고 나중에 작업이 완료되면 결과 값을 promise에 채워 넣어 주겠다는 개념이다. 이렇게 promise를 바로 리턴하면 좀 더 직관적인 코드를 작성할 수 있다.

리퀘스트(웹 브라우저가 서버에 요청)를 보내고 리스폰스(서버가 보내주는 응답)를 파싱(어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때 불러올 수 있게 하는것)해서 결과를 출력하는 코드를 전에는 왼쪽(콜백기반) 처럼 했지만 promise 기반 fetch 코드를 완성하면 오른쪽의 모습이다.

promise 기반 코드는 평소에 쓰는 코드와 비슷한 모습인데, 코드가 한줄씩 있고 이전 줄에서 선언한 변수를 다음줄에서 사용하고 있다. 비동기 작업을 처리할 때도 익숙한 형태로 코드를 작성할 수 있다는게 promise 문법의 큰 장점이다. (자바스크립트에서 많은 비동기 코드가 prmise를 활용하는 이유이기도 함)

Promise는 세 가지 중 하나의 상태를 가질 수 있는데

  • Pending: 비동기 작업이 끝나기를 기다릴 때
  • Fulfilled: 비동기 작업이 성공적으로 끝났을 때. 비동기 작업의 성공 결과를 결괏값으로 갖게 됨.
  • Rejected: 비동기 작업이 실패했을 때. 비동기 작업에서 발생한 오류를 결괏값으로 갖게 됨.

그리고 Promise 객체를 통해 비동기 작업의 결과를 가져오거나 오류를 적절히 처리할 수 있다. Promise 객체를 다루는 방법은 두 가지가 있는데

1. .then() 메소드 + 콜백

2. async 와 await 문법

이다. 처음 배울 때는 async 와 await이 조금 더 직관적이고 이해하기 쉽다. 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

async 함수  (0) 2024.12.23
await 문법  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22

콜백을 이용해서 비동기 프로그램을 짤 수 있다는 것은 좋지만 한계점이 있다.

여러 비동기 작업을 연속적으로 처리하는 것인데 예를 들어 아래와 같은 상황이다.

// 직원 데이터를 가져온 후 리스폰스를 파싱하고 데이터를 프로세싱 하는 예시
getEmployees((response) => {
  json(response, (data) => {
    groupEmployees(data, (result) => {
      console.log(result);
    });
  });
});

코드가 조금 헷갈리는데 콜백안에 콜백이 들어가서 그렇다. 코드를 해석해보자면 

1. getEmployees 호출:

  • 이 함수는 직원 데이터를 가져오는 비동기 함수이다.
  • 이 함수는 리스폰스를 받기 위해 콜백 함수를 인자로 받는다. 이 콜백 함수는 서버로부터 응답을 받은 후 실행된다.
  • 응답이 성공적으로 오면, 해당 응답을 response로 전달받는다.

2. json(response) 호출:

  • json 함수는 response를 JSON 형식으로 파싱하는 함수이다.
  • 이 함수도 콜백을 받으며, 파싱된 데이터를 data로 전달한다.
  • 즉, response가 JSON으로 변환되어 data에 저장된다.

3. groupEmployees(data) 호출:

  • data는 이제 직원 데이터의 배열이다.
  • groupEmployees 함수는 이 배열을 받아서 그룹화하는 기능을 수행한다. 이 함수도 콜백을 받아 결과를 result로 전달한다.

4. console.log(result):

  • 최종적으로 그룹화된 결과가 result에 저장되고, 이를 콘솔에 출력된다.
더보기
  • request : 웹브라우저가 서버에 요청
  • response : 서버가 보내주는 응답
  • parshing : 어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때 불러올 수 있게 하는것
  • parser : 파싱을 수행하는 프로그램
  • json(JavaScript Object Notation) : Javascript 객체 문법으로 데이터를 쉽게 교환 하고 저장하기 위한 텍스트 기반의 데이터 교환 표준, 쉽게 말해 일반적으로 서버에서 클라이언트로 데이터를 보낼 때 사용하는 양식이다.
  • 디버깅(Debugging) : 프로그래밍 과정에서 발생하는 오류나 비정상적인 연산, 즉 버그를 찾아 수정하는 프로세스
  • 테스팅 : 소프트웨어의 품질을 평가하고, 개발 과정에서 발생할 수 있는 오류를 사전에 발견하고 수정하는 과정

콜백 기반 코드는 모든 작업을 함수 안에서 처리하기 때문에 작업을 이어서 하려면 콜백 안에 콜백을 등록해야 한다. 그렇기 때문에 콜백을 중첩해서 사용해야 하고, 이런 현상을 콜백 헬(Callback Hell, 콜백 지옥)이라고 부른다. 코드를 이해하기도 힘들고, 디버깅이나 테스팅 같은 걸 하기도 어렵기 때문이다. (하지만 콜백을 이용해서 비동기 프로그램을 구현하다보면 어쩔 수 없이 발생하는 문제였다.) 이 불편함을 해소하기 위해 Promise라는 것이 2015년에 등장했다.

참고로 모든 상황에 Promise 기반 코드가 더 좋고, 모든 비동기 작업은 Promise로 처리하는 것은 아니다. Promise는 비동기 코드의 결괏값을 활용해야 할 때 특히 유용하고 이런 경우가 아니라면 여전히 콜백을 사용한다.

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

await 문법  (0) 2024.12.22
Promise  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22

비동기 함수 복습

setTimeout() 함수

setTimeout(callback, delay);
  1. setTimeout() 이전에 있는 코드 실행
  2. setTimeout() 함수 실행 : delay 만큼 기다리는 타이머를 시작
  3. setTimeout() 이후에 있는 코드 실행
  4. delay가 지나면 callback 실행

asyncFunction() 함수 (사실 함수는 아니고 비동기 함수(async function)를 생성하는 객체이다.)

async function 선언은 AsyncFunction 객체를 반환하는 하나의 비동기 함수를 정의한다. 비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise 를 사용하여 결과를 반환한다. 그니까 그냥 비동기 함수라고 쳐본다면,

asyncFunction(callback, ...);

asyncFunction()을 호출하고 callback을 전달하는 형태로, 동작은 setTimeout과 비슷하다.

  1. asyncFunction() 이전에 있는 코드 실행
  2. asyncFunction() 함수 실행 : 특정 조건이 만족되면(ex. 3초가 지나 어떤 '이벤트'가 발생했다.) callback이 실행되도록 한다.
  3. asyncFunction() 이후에 있는 코드 실행
  4. 특정 조건이 만족되는 시점에 callback 실행

이 흐름만 이해하고 있다면 어떤 비동기 함수든 쉽게 사용할 수 있다. 


자주 사용하는 비동기 함수 TOP 4

  1. setInterval() 함수
  2. DOM의 addEventListener() 함수
  3. React의 useEffect() 함수
  4. Express의 get() 함수

setInterval() 함수

setTimeout()과 비슷한 setInterval() 함수로, setInterval() 함수는 시간 간격을 두고 콜백을 반복적으로 실행한다.

console.log('Start');

// setInterval(callback, interval): interval 단위는 밀리초.
setInterval(() => console.log('2초가 지났습니다'), 2000);

console.log('End');
Start
End
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다
...

(callback은 interval이 지난 후 처음으로 실행된다.)

만약 콜백이 호출되는 것을 멈추고 싶다면 setInterval() 함수의 리턴값을 저장해 놨다가 clearInterval()을 실행하면 된다.

console.log('Start');

const intervalID = setInterval(() => console.log('2초가 지났습니다'), 2000);

// 7초 후에 setInterval() 해제
setTimeout(() => clearInterval(intervalID), 7000);

console.log('End');
Start
End
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다

2초가 지났습니다를 2초마다 출력하지만 7초 후에 clearInterval() 함수가 setInterval() 함수를 해제하기 때문에 2초 지났습니다는 세 번만 출력된다.


DOM의 addEventListener() 함수

addEventListener()는 웹 페이지 요소에 상호 작용이 있을 경우 실행할 함수를 등록한다.

const btn = document.querySelector('.my-btn');

btn.addEventListener('click', () => console.log('button clicked!'));

// ...

자바스크립트로는 웹 페이지의 요소(예: 버튼, 이미지, 문단 등)를 코드로 가져올 수 있는데, 첫 번째 줄에서는 my-btn 클래스를 가진 버튼 요소를 찾아서 btn이라는 변수에 저장했다.

그리고 두 번째 줄은, btn에 클릭('click') 이벤트가 발생할 시 () => console.log('button clicked!') 콜백이 실행되도록 해 줬다. btn.addEventListener() 아래에 있는 코드를 실행하다가 사용자가 btn 요소를 클릭하면 콜백이 실행돼서 'button clicked!'라고 콘솔에 출력되는 동작 방식이다.


React의 useEffect() 함수

function PostList() {
  // ...

  useEffect(() => console.log('render finished!'), []);

  return (
    <div className="post-list">
      <div className="post-item">...</div>
      <div className="post-item">...</div>
      <div className="post-item">...</div>
      ...
    </div>
  );
}

React는 웹사이트 화면을 만드는 데 사용하는 라이브러리이다. React에서는 웹사이트 화면을 컴포넌트(Component) 단위로 분리해서 만든다.

위에서 정의한 PostList()는 어떤 웹사이트의 포스팅 목록에 해당하는 컴포넌트인데, 안에 useEffect()라는 비동기 함수가 있다. 이 함수는 컴포넌트가 모두 화면에 그려지는 시점에 콜백을 실행해 준다.

그러니까 React는 PostList() 함수의 내용을 쭉 실행하고 리턴되는 HTML 요소들을 그리기 시작하는데(화면에 그리는 작업은 시간이 조금 걸린다.) 작업이 완료되고 나면 useEffect()에 등록된 콜백이 실행되고 render finished!라는 메시지가 출력되는 것이다.

지금은 단순히 콜백에서 메시지를 출력하지만 실제로는 웹 리퀘스트를 보내 데이터를 받아 오는 등 다양한 작업을 한다. 콜백을 바로 실행하지 않고 일단 화면을 그리기 때문에 웹 페이지가 더 빨리 로딩되는 것처럼 보이게 할 수 있다.


Express의 get() 함수

app.get('/hello', (req, res) => {
  res.send('Success!');
});

// ...

Express는 서버를 만들 때 사용하는데 서버란 어떤 리퀘스트가 들어오면 리스폰스를 돌려주는 프로그램을 의미한다. 서버는 DOM의 버튼 예시와 비슷하게 리퀘스트가 언제 들어올지를 모르기 때문에 리퀘스트에 대한 처리는 비동기 형태로 한다.

app.get() 함수는 /hello 주소로 GET 리퀘스트가 들어오면 두 번째 아규먼트인 콜백을 실행하는데 콜백은 ‘Success’라는 내용을 담고 있는 리스폰스를 보내준다.

그리고 여기서 재미있는 점은 콜백에 파라미터가 있다는 점이다. 나중에 실행할 콜백에 파라미터가 있는 경우도 많지만 이번 예시의 경우 들어온 리퀘스트 객체(req)와 돌려줄 리스폰스 객체(res)를 파라미터로 받고 있다. 프로그래머는 이걸 이용해서 적절한 리스폰스를 보내면 된다.


 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

Promise  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22
콜백(callback)  (0) 2024.12.21

1. 비동기 함수는 이후에 있는 코드를 모두 실행하고 콜백을 실행

예를 들어 setTimeout() 함수의 두번째 파라미터로 0을 넘겨준다면? 3000을 적어줬을 때 뜻이 3000밀리초, 즉 3초였기 때문에 0은 바로 실행하라는 뜻이다. 하지만 아래 코드를 실행해보면 결과는 예상한 것과 다르게 나온다.

console.log('1');

setTimeout(() => console.log('2'), 0);

console.log('3');
console.log('4');
console.log('5');
console.log('6');
console.log('7');
console.log('8');
console.log('9');
console.log('10');
1
3
4
5
6
7
8
9
10
2

setTimeout(() => console.log('2'), 0) 아래에 있는 코드들이 먼저 실행되고 콜백이 실행됐다. 비동기 함수의 콜백은 아무리 빨리 실행돼도 비동기 함수 이후에 있는 코드가 모두 실행된 후에 실행된다. 반대로 생각해보면 비동기 함수 이후에 있는 코드가 굉장히 오래 걸려도 그걸 모두 실행하고 콜백을 실행한다는 의미이다.

예시를 바꿔서 실행해도 console.log('2')는 가장 마지막에 실행된다.

console.log('1');

setTimeout(() => console.log('2'), 1000);

// 1초 이상 걸리는 작업들

하지만 대부분의 경우에는 비동기 함수 이후에 코드가 아무리 많다고 해도 비동기 작업이 훨씬 오래걸린다. 콜백의 실행 타이밍이 몇 밀리초 단위까지 중요한 경우는 별로 없기 때문에 문제가 되는 경우는 드물다.

2. 실행할 콜백이 여러 개일 경우, 동기적으로 실행

console.log('1');

setTimeout(() => console.log('2'), 1001);

setTimeout(() => console.log('3'), 1000);

console.log('4');

위 코드의 실행 순서를 잠깐 생각해보자.

우선 1, 4가 출력되는건 예상할 수 있다. 그 이후 콜백이 어떻게 되는지가 관건인데 시간을 잘보면 두번째 setTimeout() 의 콜백이 실행되는 동안 첫번째 setTimeout() 의 타이머가 끝난다. 이런 경우엔 콜백은 동기적(순차적)으로 실행된다.

중요한 점은 비동기 작업이 비교적 오래 걸리는 작업들이라는 것인데, 예를 들면 특정 시간을 기다리거나 사용자의 상호작용을 기다리는 작업, 웹 리퀘스트의 결과를 기다리는 작업들처럼. 이런 비동기 작업들은 병렬적으로 처리되기 때문에 충분히 효율적인 코드를 작성할 수 있다.

 

 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22
콜백(callback)  (0) 2024.12.21
비동기 프로그램  (0) 2024.12.21

자바스크립트에 setTimeout() 함수특정 시간이 지난 후에 콜백을 실행하는 함수이다. 기다리는 동안에는 함수 아래에 있는 코드를 실행하고, 첫번째 아규먼트로는 어떤 콜백이든 전달 할 수 있다. 

// ...

setTimeout(callback, delay);

// ...


//함수의 동작은 아래와 같다.
//1. setTimeout() 이전에 있는 코드 실행
//2. setTimeout() 함수 실행: delay 만큼 기다리는 타이머를 시작
//3. setTimeout() 이후에 있는 코드 실행
//4. delay가 지나면 callback 실행
console.log('1');

setTimeout(() => console.log('2'), 3000);
//첫번째 아규먼트로 콜백() => console.log('2')을 받고, 3000밀리초 = 3초가 지나면 이 콜백을 실행해준다.

console.log('3');
1
3
2

위 코드가 비동기 프로그램의 예시이다. 코드가 쓰여져 있는 순서대로 실행되지 않기 때문에.

비동기 실행 순서 설명

  1. 일단 console.log('1')이 실행된다.
  2. 이후에는 setTimeout 함수가 실행된다. (중요! 이 시점에는 3초 세는 타이머를 시작하기만 하고 바로 다음줄로 넘어간다. 콜백까지 실행 한 다음에 다음줄로 넘어가는 것X)
  3. 그래서 일단 console.log('3')이 실행되고
  4. 3초가 다되면 setTimeout에 전달된 콜백(console.log('2'))이 실행된다.

코드를 순서대로 실행하는 동기함수 였다고 가정하고 설명

  1. console.log('1')이 가장 먼저 실행
  2. 이후에는 setTimeout 함수가 실행
  3. 타이머를 시작한 후, 3초를 기다린 다음 콜백(console.log('2')) 까지 실행한다.
  4. 그러고 난 다음 console.log('3')이 실행된다.

두 프로그램을 비교해보면 비동기 프로그램이 동기 프로그램보다 조금 더 빨리 끝나는것을 확인 할 수 있다. 예시는 차이가 미세해도 setTimeout 이후의 작업이 더 오래걸리는 작업이었거나 더 많은 작업이 있었다면 비동기 프로그램이 훨씬 더 효율적이었을거다.

비동기 프로그램은 오래걸리는 작업이 있을 때 기다리는 동안 다른 작업을 먼저 처리함으로써 시간을 절약해준다.

setTimeout 함수를 비동기 함수(Asynchronous Function)하고 부르는데, 비동기 함수란 함수의 내용을 끝까지 쭉 실행하지 않고 중간에 다른 작업을 처리하다가 다시 돌아와서 마무리 하는 함수를 말한다. 콜백을 이용하면 나중에 해야하는 작업을 함수 형태(위 코드를 예로 들면 () =>  console.log('2') 같은)로 전달해 줄 수 있기 때문에 비동기 함수를 구현하는데 특히 유용하게 쓰인다.

콜백이라는 이름에 대해 생각해보면 call back : '나중에 다시 부르다' 라는 뜻이다. 바깥 함수 안에서 콜백이 언젠가 실행되기 때문에 이런 이름을 가지고 있는 것이다.

  • 자바스크립트나 자바스크립트 라이브러리는 다양한 비동기 함수를 제공해준다.
  • 제공되는 비동기 함수에 콜백을 넘겨주기만 하면 비동기 프로그램을 구현할 수 있다.

 

 
 
 
 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백(callback)  (0) 2024.12.21
비동기 프로그램  (0) 2024.12.21

function 키워드로 선언한 함수를 콜백으로 사용(콜백 파라미터 없음)

function sayHello() {
  console.log('Hello World!');
};

function printMessage(func) {
  console.log('Printing message...');
  func();
};

printMessage(sayHello);

여기서 [ printMessage(sayHello); ]  sayHello 뒤에 괄호를 쓰지 않는 것이 중요하다. sayHello를 호출하는게 아니라 그냥 아규먼트로 전달하는 것이기 때문이다. sayHello 처럼 다른 함수의 아규먼트로 전달되는 함수를 콜백이라고 부른다.

Printing message...
Hello World!

Arrow Function을 콜백으로 사용(콜백 파라미터 없음)

//function sayHello() {
//  console.log('Hello World!');
//};

function printMessage(func) {
  console.log('Printing message...');
  func();
};

printMessage(() => console.log('Hello World!'));

위 코드 처럼 간단한 함수는 화살표 함수로 변환할수도 있다. sayHello 함수를 화살표 함수로 바꿔보면, printMessage(() => console.log('Hello World!')); 이렇게 작성할 수 있다.

Printing message...
Hello World!

function 키워드로 선언한 함수를 콜백으로 사용(콜백 파라미터 있음)

function sayHello(name) {
  console.log(`Hello ${name}!`);
};

function printMessage(func, name) {
  console.log('Printing message...');
  func(name);
};

printMessage(sayHello, 'Bob');

sayHello 함수는 name 파라미터를 받아서 name에게 인사를 하도록 코드를 변경했다. 그리고 printMessage 함수도 name 파라미터를 받는데 안에서 func 콜백을 호출할 때 name을 넘겨준다. 그렇게 되면 func 파라미터는 printMessage 에 첫 아규먼트로 적은 sayHello, name 파라미터는 'Bob'을 받아 실행을 한다. 이렇게 콜백은 파라미터를 받을 수도 있다.

파라미터를 받는 경우 바깥 함수 안에서 파라미터를 채워서 콜백을 실행해 준다.

Printing message...
Hello Bob!

Arrow Function을 콜백으로 사용(콜백 파라미터 있음)

function printMessageWithParams(func, name) {
  console.log('Printing message...');
  func(name);
}

printMessageWithParams((name) => console.log(`Hello ${name}!`), 'Bob');

바로 직전에 사용 했던 코드를 화살표 함수로 바꿔서 사용하면 이렇게 된다.

간단한 콜백에는 화살표 함수를 많이 사용한다.

Printing message...
Hello Bob!

 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22
비동기 프로그램  (0) 2024.12.21

코드 중에는 실행하는데 비교적 오래 걸리는 것들이 있다.

예를 들면 코드로 웹 리퀘스트(web request)를 보낼 수 있는데, 이 작업이 완료 되려면 인터넷을 통해 리퀘스트(Requset)가 서버까지 전달되어야 하고, 서버가 리퀘스트를 처리하고, 리스폰스가 다시 컴퓨터까지 전달되어야 한다.

response = sendRequest('https://www.worldwideweb.com')

요즘엔 물론 컴퓨터 성능도 좋아지고, 인터넷 속도도 점점 빨라지고 있지만 단순한 연산을 하는것에 비교하면 몇천 배 또는 그 이상의 시간이 걸린다.

그런데 코드로 리퀘스트를 보내고, 리스폰스를 기다리는 동안에는 컴퓨터가 따로 하는일이 없다. 리스폰스를 기다리기만 한다.

기다리는동안 수천개의 연산을 할 수 있을텐데 그냥 기다리고만 있는것은 별로 효율적이지 못하다. 효율적인 프로그램은 오랜 시간동안 기다려야 할 때 다른 일을 먼저 하고 있다가 기다리던 작업이 완료 되면 다시 그 일로 돌아갈것이다.

이게 바로 비동기 프로그래밍의 핵심 아이디어이다.

비동기 프로그래밍은 주어진 코드를 꼭 순서대로 실행하는 것이 아니라 오래 기다려야 하는 작업이 있으면 다음 작업을 먼저 처리하고, 나중에 처리하던 작업으로 다시 돌아와서 마무리하는 방식이다.

이걸 잘 활용하면 여러 작업을 동시에 효율적으로 처리할 수 있다.

언뜻 생각했을 때는 비동기 프로그램을 구현한다는게 막연하게 어려울것 같은데 자바스크립트는 비동기 프로그램을 짜기 위한 다양한 문법과 툴을 제공하기 때문에 이것을 어떻게 사용하는지 알면 비동기 프로그램을 쉽게 짤 수있다.

 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22
콜백(callback)  (0) 2024.12.21

+ Recent posts