본문 바로가기

React

React: 비동기 프로그래밍

01. 동기 vs 비동기

동기와 비동기 개념

순차적이냐 비순차적이냐의 차이

(1) 동기 처리 (Synchronouse Processing)

  • 요청과 그에 따른 응답이 순차적으로 일어나는 방식
  • 일의 순서중요한 경우 동기 처리
  • 처리 순서가 보장되지만, 응답을 기다리는 동안 다른 작업을 수행할 수 없기 때문에 비효율적일 수 있음

(2) 비동기 처리 (Asynchronouse Processing)

  • 요청과 그에 따른 응답이 비순차적으로 일어나는 방식
  • 요청을 보내고 응답을 기다리지 않고, 다음 작업을 계속 진행합니다.
  • 일의 순서가 중요하지 않은 경우, 효율적인 일 처리를 위해 비동기 처리!

예시 코드

function sendTextMessage(message, callback) {
  // 메시지를 보내는 비동기 작업을 가정해요.
  setTimeout(() => {
    console.log("문자 메시지 전송 완료: " + message);
    callback(); // 응답이 도착하면 콜백 함수 호출
  }, 2000); // 2초 후에 메시지 전송이 완료된다고 가정
}

console.log("메시지 보내기 시작");
sendTextMessage("안녕, 잘 지내?", function () {
  console.log("메시지 전송 후 후속 작업 수행");
});
console.log("다른 작업 수행");

// 출력 결과:
// 메시지 보내기 시작
// 다른 작업 수행
// 문자 메시지 전송 완료: 안녕, 잘 지내?
// 메시지 전송 후 후속 작업 수행

02. Promise

Promise 개념

  • Promise는 자바스크립트에서 비동기 작업의 완료 또는 실패를 처리하기 위해 사용되는 개념
  • 비동기 작업이 성공하거나 실패했을 때 각각의 결과를 처리하는 데 도움
  • Promise는 비동기 작업의 결과를 다루기 쉽게 하기 위해 만들어졌으며, 비동기 작업이 끝난 이후에 실행될 콜백을 등록할 수 있는 메서드를 제공
  • Promise 객체를 생성하기 위해 Promise 생성자(new Promise)를 사용가능

Promise 등장배경

자바스크립트는 비동기 작업을 처리할 때 콜백 함수를 많이 사용

하지만 콜백 함수를 중첩해서 사용하다 보면 코드가 복잡해지고 가독성이 떨어지는 문제가 발생 => 콜백 지옥

Promise가 없다면

  • 콜백 함수의 중첩이 깊어져서 코드를 이해하고 유지보수하기 어려워짐
  • 에러 처리가 분산되어 관리가 어렵고, 코드의 흐름을 추적하기 힘들어짐
  • 여러 개의 비동기 작업을 순차적으로 처리하거나 병렬로 처리하는 경우 코드가 복잡해짐

Promise의 상태

  • 대기(Pending): 초기 상태, 이행되거나 거부되지 않은 상태
    resolvereject로 인해 다른 상태로 변경되기 전까지의 상태
  • 이행(Fulfilled): 비동기 작업이 성공적으로 완료된 상태
    resolve로 인해 pending 상태에서 fulfilled 상태로 변경
  • 거부(Rejected): 비동기 작업이 실패한 상태
    reject로 인해 pending 상태에서 rejected 상태로 변경

Promise 객체는 then, catch, finally 메서드를 통해 이행되거나 거부된 이후의 동작을 정의가능

예시코드 / 활용방법 (React)

직접 Promise 객체 만들기 (with 생성자)

const myPromise = new Promise((resolve, reject) => {
  // 비동기 작업을 수행합니다.
  if (/* 작업이 성공적으로 완료되면 */) {
    resolve("성공 메시지");
  } else {
    reject("실패 메시지");
  }
});

=>

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // 작업의 성공 여부를 나타내는 변수
    // 여기를 true <-> false 변경해가며 테스트해보세요!
    const success = true;

    if (success) {
      // resolve 함수는 Promise 객체의 상태를 이행(fulfilled) 상태로 변경해요.
      // resolve 함수 자체는 반환값이 없어요.
      // 다만, resolve 함수가 호출되면 그 값이 then 메서드의 callback 함수로 전달돼요.
      resolve("작업이 성공적으로 완료되었습니다!");
    } else {
      // 마찬가지 원리로, reject 함수가 호출되면 그 값이 catch 메서드의 callback 함수로 전달돼요.
      reject("작업이 실패했습니다.");
    }
  }, 2000); // 2초 후에 작업이 완료
});

myPromise
  .then((result) => {
    console.log(result); // "작업이 성공적으로 완료되었습니다!" 출력
  })
  .catch((error) => {
    console.log(error); // "작업이 실패했습니다." 출력
  });

 

in React

import React, { useEffect, useState } from 'react'

const App = () => {
  const [message, setMessage] = useState("타이머시작");

  useEffect(() => {
    const delay = (ms) => {
      const promise = new Promise(resolve => {
        setTimeout(resolve, ms);
      });

      return promise;
    };

    delay(2000).then(() => {
      setMessage("2초 후 메시지가 변경됐습니다.");
    });
  }, []);

  return (
    <div>
      <h1>{message}</h1>
    </div>
  )
}

export default App

2초 후에 메시지가 변경됨

Promise를 활용해 데이터 가져오기 (with fetct함수)

fetch함수는 Promise 객체를 반환

// (1) 컴포넌트가 마운트될 때 fetch를 사용해 데이터를 비동기적으로 가져옴
// (2) Promise의 then 메서드를 사용해 데이터를 설정
// (3) 에러가 발생하면 catch 메서드를 통해 에러를 처리
import React, { useState, useEffect } from "react";

function App() {
  const [post, setPost] = useState(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((response) => response.json())
      .then((json) => setPost(json))
      .catch((error) => console.error("데이터 펫칭 오류! => ", error));
  }, []);

  return <div>{post ? <div>{post.title}</div> : <div>Loading...</div>}</div>;
}

export default App;

여러 비동기 작업을 병렬로 처리

1. 순차처리 (Sequential Processing)

첫 번째 작업이 끝나고나서 그 다음 작업 => 그 다음 작업 끝나고 나서 그 다음 작업 => ...

 

2. 병렬처리 (Parallel Processing)

요청의 순서와 상관없이 모든 요청이 한 번에 들어가고 모든 요청의 결과를 받으면 처리

 

Promise.all 메서드를 사용하면 여러 비동기 작업을 병렬로 처리가능

모든 작업이 완료될 때까지 기다린 후, 결과를 한 번에 처리

import React, { useState, useEffect } from "react";

function App() {
  const [post, setPost] = useState(null);

  useEffect(() => {
    Promise.all([
      fetch("https://jsonplaceholder.typicode.com/posts/1")
        .then((response) => response.json()),
      fetch("https://jsonplaceholder.typicode.com/posts/2")
        .then((response) => response.json()),
    ]).then(([response1, response2]) => {
      console.log("response1 => ", response1)
      console.log("response2 => ", response2)
    })
  }, []);

  return <div>{post ? <div>{post.title}</div> : <div>Loading...</div>}</div>;
}

export default App;

async / await

async와  await키워드는  Promise객체를 더욱 간편하게 다룰 수 있도록 도와줌

async함수는 항상 Promise를 반환하며, await키워드는 Promise가 이행될 때까지 기다림

이 방법을 사용하면 비동기 코드를 더 동기 코드처럼 작성할 수 있어 가독성이 향상

 

async / await 개념

  • async 함수
    async키워드로 정의된 함수는 항상 Promise를 반환
    함수 내부에서 명시적으로  return문으로 값을 반환하면 자동으로 Promise.resolve()를 통해 감싸져서 반환
  • await 키워드
    await키
    워드는 Promise가 이행될 때까지 기다리고, 이행된 Promise의 결과를 반환
    await는 async함수 내부에서 사용

async / await 필요성

  • 가독성 : async / await 구문을 사용하면 비동기 코드를 동기 코드처럼 작성할 수 있어 가독성이 크게 향상
  • 에러 처리: try - catch구문을 사용하여 비동기 작업에서 발생하는 오류를 간편하게 처리
  • 코드의 간결함: 콜백 지옥이나 체이닝피할 수 있어 코드가 간결해집니다.

예시 코드

import React, { useState, useEffect } from "react";

function App() {
  const [post, setPost] = useState(null);

  useEffect(() => {
    const fetchPost = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/posts/1"
        ); // fetch의 결과(Promise)를 response에 담음
        const data = await response.json(); // fetch의 결과를 json형식으로 바꿔 data에 저장
        setPost(data); // post state를 data로 설정
      } catch (error) {
        console.error("Error fetching post:", error);
      }
    };

    fetchPost();
  }, []);

  return <div>{post ? <div>{post.title}</div> : <div>Loading...</div>}</div>;
}

export default App;

'React' 카테고리의 다른 글

React : REST API 예시  (0) 2024.06.10
React : HTTP, REST API  (0) 2024.06.10
React: Supabase  (0) 2024.05.22
React: Router DOM - 2 Dynamic Route, useParams, Outlet  (0) 2024.05.22
React: Router DOM - 1  (0) 2024.05.22