240610 TIL

웅웅·2024년 6월 10일

 지금까지의 프로젝트에서 비동기에 대한 어려움을 많이 겪었기에 비동기에 대한 개념을 확실하게 잡고 넘어가기로 했다.


[비동기 뿌시기!!]


동기 vs 비동기

 둘을 구분짓는 가장 쉬운 키워드는 순차라고 할 수 있다.

(1) 동기 : 순차적 처리

  요청을 보내면 응답이 올 때까지 대기한다. 일의 순서가 중요한 경우 동기 처리하는 것. 예를 들면 외부 데이터를 json 형태로 통신하여 받아온 후, 해당 내용을 브라우저 상에 react 컴포넌트로 변환하여 제공해야 할 때 필요한 처리이다.
대기하는 동안 다른 작엄을 수행할 수 없기 때문에 비효율적임

(2) 비동기 : 비순차적 처리

  요청을 보내고, 응답을 기다리지 않은 채 다른 작업을 계속 진행한다. 일의 순서가 중요하지 않은 경우에 비동기 처리를 한다.

(3) 예시

- setTimeout

아래와 같은 경우는 비동기적 처리일까, 동기적 처리일까?

    console.log("시작");
    
    setTimeout(() => {
      console.log("타임아웃");
    }, 1000);
    
    console.log("끝");

  답은 비동기적 처리이다. 만약 동기적 처리라면 타임아웃이 출력되는 1초를 기다렸다가 이 출력될 것이다. setTimeout 함수는 지정된 시간(여기서는 1000ms) 후에 콜백 함수를 실행하는 비동기 함수이다.

- fetch

  fetch는 외부 데이터를 가져오기 때문에 비동기적으로 작동할 수 밖에 없다. 따라서 동기적 처리를 위해서는 await 처리나 then 처리 해주어야 한다. 아래 예시에서 fetch가 비동기적으로 동작하여 끝이 먼저 출력되는 걸 확인할 수 있다.

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

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

  useEffect(() => {
    console.log("시작");

    fetch("https://jsonplaceholder.typicode.com/posts/1")
      .then((response) => response.json())
      .then((json) => {
        setPost(json);
        console.log("데이터 가져오기 완료");
      });

    console.log("끝");
  }, []);

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

export default App;

Promise

  비동기 작업의 완료 또는 실패를 처리하기 위해 사용되는 개념

왜 사용할까?

  자바스크립트는 비동기 작업을 처리할 때 콜백 함수를 많이 사용한다. 콜백 함수를 중첩해서 사용하다 보면 코드가 복잡해지기 때문에 유지보수가 어렵고 가독성이 떨어진다. (이를 콜백 지옥이라고 부른다.)

상태 세가지

(1) fullfilled

이행 : resolve로 pending에서 이 상태로 변경한다. resolve 함수가 호출되면 그 값이 then 메서드의 callback 함수로 전달된다.

resolve("작업이 성공적으로 완료되었습니다!");

위 코드에서 "작업이 성공적으로 완료되었습니다!"가 callback 함수로 전달된다.

(2) rejected

실패 : reject로 pending에서 이 상태로 변경한다. 마찬가지 원리로, reject 함수가 호출되면 그 값이 catch 메서드의 callback 함수로 전달된다.

(3) pending

대기 : 초기 상태이다.

예시 코드

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("작업이 성공적으로 완료되었습니다!");
    } else {
      reject("작업이 실패했습니다.");
    }
  }, 2000); // 2초 후에 작업이 완료
});

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

JSONPlaceholder로 Promise 활용해보기

기억하기! fetch 함수는 promise 객체를 반환! response Object를 가진 것을 resolve하는 promise를 return.

코드

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then((response) => response.json())
  .then((json) => console.log(json));

output

{
  id: 1,
  title: '...',
  body: '...',
  userId: 1
}


promise.all

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

  배열 안의 요청을 보내고, 처리 순서와 상관없이 모두가 다 처리되었을 때 결과를 한꺼번에 처리한다.

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(function ([response1, response2]) { //구조분해할당
    console.log('response -> ', response1);
    console.log('response -> ', response2);
  });
}, []);

async/await 키워드

  then보다 더 많이 쓰이는 것이 바로 이 두 가지 키워드이다. async 함수는 항상 Promise를 반환한다. 함수 내부에서 명시적으로 return 문으로 값을 반환하면 자동으로 Promise.resolve()를 통해 감싸져서 반환된다. await 키워드는 Promise가 이행될 때까지 기다리게 한다. await는 async 함수 내부에서 사용할 수 있다. 비동기 코드를 더 동기 코드처럼 작성할 수 있게 한다. 외부데이터를 가져온 후 데이터 처리를 해야하는 경우가 많기 때문에 매우 중요하다.

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

  useEffect(() => {
    const fetchPost = async () => {
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/posts/1"
      );
      const data = await response.json();
      setPost(data);
    };

    fetchPost();
  }, []);

try-catch문을 통해 rejected 상태까지 처리한다.

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"
        );
        const data = await response.json();
        setPost(data);
      } catch (error) {
        console.error("Error fetching post:", error);
      }
    };

    fetchPost();
  }, []);

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

export default App;

0개의 댓글