데이터 fetch 싹싹김치 part. 1 : 동기와 비동기🥬

밍갱·2025년 2월 21일
0

1. 동기 vs 비동기☕️

01. 동기(Synchronous)란?

  • 직렬적 수행 : 요청과 응답이 순차적으로 이뤄지는 방식이다.
  • 시스템 효율의 저하 : 응답을 기다리는 동안 다음 작업은 대기해야 한다.
  • 일의 순서가 중요한 경우 동기 처리를 한다. (for 루프 등)

02. 비동기(Asynchronous)란?

  • 병렬적 수행 : 요청을 보내고 응답의 여부와 상관없이 다음 작업이 이뤄진다.
  • 효율적인 처리 : 먼저 요청한 응답을 기다리지 않고 바로 다음 작업을 진행한다.
  • 일의 순서가 중요하지 않은 경우 비동기 처리를 한다. (대부분의 JS 로직)

03. 비동기 처리 : 콜백 함수와 콜백 지옥

  • 비동기 처리와 콜백 함수(Callback Function)
    JavaScript에서 비동기 처리를 구현하는 기본적인 방법으로, 콜백 함수가 있다. 콜백 함수란, 함수의 인자로 전달되는 함수로 특정 작업이 완료된 후 호출된다. 즉, 함수의 비동기 작업이 완료된 후 콜백 함수가 실행되어 비동기 작업의 결과를 처리한다.
  • 콜백 지옥
    비동기 처리를 위해 콜백 함수를 여러개 중첩하여 사용하면, 오히려 코드가 복잡해지고 가독성이 떨어진다. 이를 콜백 지옥(Callback Hell)이라고 한다. 콜백 지옥은 유지보수가 어려워지고, 순차적 처리나 병렬 처리를 구현하기 복잡하다는 단점이 있다.
    이를 보완하고자 ES6의 Promise가 등장하였다.

2. Promise🤙

01. Promise란?

Promise는 '약속'이란 의미처럼, 비동기 작업의 진행 상태를 관리하는 "객체 "이다. Promise는 비동기 작업의 상태를 관리하고, 체이닝을 통한 비동기 작업 처리를 하기 때문에, 콜백 함수의 단점을 보완한다.

  • Promise() 생성자
    Promise를 지원하지 않는 함수를 감쌀 때 사용한다. 인자로 resolvereject를 받아, 성공 혹은 실패에 대한 로직을 수행한다.
const 이름 = new Promise((resolve, reject) => {콜백 함수 로직})

//예시
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("비동기로 처리해봅시다");
  }, 3000);
});

02. Promise의 상태

  • pending : 대기, 비동기 처리 로직이 아직 완료되지 않은 상태
  • fulfilled : 이행, 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • rejected : 실패, 비동기 처리가 실패하거나 오류가 발생한 상태

03. Promise의 메서드

Promise는 후속 처리 메소드를 체이닝(chaining)하여 여러 Promise를 연결한다. 이를 통해 콜백 지옥을 해결한다.

  • then() : Promise가 이행(fulfilled) 되었을 때 실행되는 로직. 이전 Promise의 결과를 받아 새로운 값을 반환하거나, 새로운 Promise를 반환할 수 있다.
  • catch() : Promise가 실패(rejected) 되었을 때 실행되는 로직. 이전 then()에서 발생한 오류를 잡아 처리할 수 있다.
  • finally() : Promise의 성공/실패 여부와 관계없이 무조건 실행되는 로직
fetch("https://jsonplaceholder.typicode.com/users/1")
  .then((response) => response.json()) // ✅응답을 JSON으로 변환
  .then((data) => console.log("사용자 데이터:", data)) // ✅JSON 데이터 출력
  .catch((error) => console.error("오류 발생:", error)) // ❌오류 처리
  .finally(() => console.log("요청 완료!")); // ❓성공/실패와 관계없이 실행

04. 비동기 작업의 병렬 처리 : Promise.all()

Promise.all()은 여러 개의 Promise들을 병렬적으로 실행하여 처리하는 메서드로, 다음 로직을 계속 실행하기 전에 서로 연관된 비동기 작업 여러 개가 모두 이행되어야할 때 사용한다. 즉, 내부 Promise들은 실행 순서와 상관없이 각각 진행되지만, Promise.all()은 모든 Promise가 완료될 때까지 기다린다.
인자로 여러 Promise를 담은 배열을 받으며, 인자로 들어온 Promise 중 하나라도 rejected가 되면 Promise.all()은 즉시 거부된다.

  • 사용 방법
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// [3, 42, "foo"]

05. async/await

  • async/await란?
    async/await는 JavaScript 비동기 처리 문법 중 가장 최근에 나온 문법으로, 기존 콜백 함수와 Promise의 단점을 보완하고, 개발자 친화적인 문법을 제공한다.

  • async
    async는 함수(function) 앞에 사용하는 키워드로, async 함수는 항상 Promise를 반환한다. 즉, 해당 함수의 return 값이 Promise가 아니어도 Promise로 감싸진 값이 반환된다.

  • await
    awaitasync 함수 안에서만 동작하며, Promise가 처리될 때까지 함수 실행을 기다리게 하는 키워드이다. await가 붙은 Promise가 처리되면 그 결과값과 함께 다음 로직이 실행된다.
    기존 Promise의 thencatch를 체이닝하여 사용하는 문법보다 await를 사용한 문법이 가독성이 좋다.

  • try...catch
    try...catchawait가 던진 에러를 잡는 문법이다. async/await를 사용하면 await가 대기를 처리해주기 때문에 then이 필요하지 않고, catch 대신 try..catch를 사용할 수 있다.

  • 사용 방법

async function 함수명() {
	await 비동기_처리_메서드_명();
};

//예시
async function fetchData () {
	const response = await fetch("https://jsonplaceholder.typicode.com/")
    const data = await response.json()
    
    return data
}
  • async/await의 한계
    async/await는 Promise 체이닝보다 가독성이 좋지만, Promise.all()처럼 병렬적으로 처리해야 하는 경우에는 await를 연속으로 사용하면 오히려 성능이 저하될 수 있다.
// 병렬 실행이 아님 (비효율적)
async function fetchUsers() {
  const user = await fetch("/user");
  const posts = await fetch("/posts");  
  return { user, posts };
}

// Promise.all()을 사용하여 병렬 실행 (더 효율적)
async function fetchUsersParallel() {
  const [user, posts] = await Promise.all([
    fetch("/user"),
    fetch("/posts")
  ]);
  return { user, posts };
}

동기/비동기 참고 사이트 1
동기/비동기 참고 사이트 2
Promise 참고 사이트 1
Promise 참고 사이트 2
async/await 참고 사이트 1

profile
미술 전공에서 프론트엔드 개발까지

0개의 댓글