asynchronous programming

앞선 포스트에서는 Promise의 동작 원리에 대해서 자세히 알아봤다.
Promise 는 asynchronous operation을 실행할 때 callback 함수가 너무 많이 중첩되는 현상인 callback hell을 피하기 위해 나온 객체이다.

Callback Hell을 피하기 위해 Promise와 .then을 써보자!

function increase(number) {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      const result = number + 10;
      if (result > 50) {
        const e = new Error("NumberTooBig");
        return reject(e);
      }
      resolve(result);
    }, 1000);
  });
  return promise;
}

.then handler를 써서 promise handle 의 resolve value에 접근할 수 있다.

increase(0)
  .then((number) => {
    console.log("result is " + number);
    return increase(number);
  })
  .then((number) => {
    console.log("result is " + number);
    return increase(number);
  })
  .then((number) => {
    console.log("result is " + number);
    return increase(number);
  })
  .then((number) => {
    console.log("result is " + number);
    return increase(number);
  })
  .then((number) => {
    console.log("result is " + number);
    return increase(number);
  })
  .catch((e) => {
    console.log(e);
  });

async/await은 .then보다 더 쉽게 promise 를 사용 가능케 한다.

// 위의 .then을 쓴 코드블럭과 works exactly the same.
async function runTasks() {
  try {
    // 예외처리
    let result = await increase(0);
    console.log(result);

    result = await increase(result);
    console.log(result);

    result = await increase(result);
    console.log(result);

    result = await increase(result);
    console.log(result);

    result = await increase(result);
    console.log(result);

    result = await increase(result);
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

⚠️ An async function always returns a Promise.

axios 로 API 호출해서 데이터 받아 오기

axios 는 현재 가장 많이 사용되고 있는 자바스크립트 HTTP 클라이언트이다. 이 라이브러리의 특징은 HTTP 요청을 Promise 기반으로 처리한다는 점

//NewsList.js
import { useState, useEffect } from "react";
import styled from "styled-components";
import NewsItem from "./NewsItem";
import axios from "axios";
// import usePromise from "../lib/usePromise";

const NewsListBlock = styled.div`
(...)
`;

const NewsList = () => {
  const [articles, setArticles] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    // async를 사용하는 함수 따로 선언
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await axios.get(
          "https://newsapi.org/v2/top-headlines?country=us&apiKey=API"
        );
        setArticles(response.data.articles);
      } catch (e) {
        console.log(e);
      }
      setLoading(false);
    };
    fetchData();
  }, [category]);

  // 대기 중일 때
  if (loading) {
    return <NewsListBlock>대기 중...</NewsListBlock>;
  }
  // 아직 articles 값이 설정되지 않았을 때
  if (!articles) {
    return null;
  }

  return (
    <div>
      <NewsListBlock>
        {articles.map((article) => (
          <NewsItem key={article.url} article={article} />
        ))}
      </NewsListBlock>
    </div>
  );
};

export default NewsList;

⚠️ useEffect는 async function 이 될 수 없다.
useEffect 는 해당 컴포넌트가 마운트/업데이트 될 때 불려지는 함수이기 때문에 (그리하여 return statement를 쓴다면 cleanup function을 반환해야 하기 때문에) 무조건 Promise 함수를 반환하는 async 함수가 될 수 없다. 만약 useEffect 안에서 async/await 을 쓰고 싶다면, 함수 내부에 async 키워드가 붙은 nested 함수를 만들고 (useEffect 내에서 호출하여) 사용해야 한다.

Customized Hook인 usePromise 로 간소화 해보자

// NewsList.js
(...)
import usePromise from "../lib/usePromise";

const NewsListBlock = styled.div`
(...)
`;

const NewsList = ({ category }) => {
  const [loading, response, error] = usePromise(() => {
    const query = category === "all" ? "" : `&category=${category}`;
    return axios.get(
      `https://newsapi.org/v2/top-headlines?country=us${query}&apiKey=API`
    );
  }, [category]);
  // 대기 중일 때
  if (loading) {
    return <NewsListBlock>대기 중...</NewsListBlock>;
  }
  // 아직 articles 값이 설정되지 않았을 때
  if (!response) {
    return null;
  }
  // 에러가 발생했을 때
  if (error) {
    return <NewsListBlock>에러 발생!</NewsListBlock>;
  }
  // response 값이 유효할 때
  const { articles } = response.data;
  return (
    <div>
      <NewsListBlock>
        {articles.map((article) => (
          <NewsItem key={article.url} article={article} />
        ))}
      </NewsListBlock>
    </div>
  );
};

export default NewsList;
// usePromise.js
import { useState, useEffect } from "react";

export default function usePromise(promiseCreator, deps) {
  // promiseCreater = 프로미스를 생성하는 함수 = async 함수를 반환하는 함수
  // e.g. axios.get("https://newsapi.org/v2/top-headlines?country=us&apiKey=API")

  // deps = usePromise 내부에서 사용한 useEffect의 의존배열
  // e.g. [category]

  // 로딩중 / 완료 / 실패에 대한 상태 관리
  const [loading, setLoading] = useState(false);
  const [resolved, setResolved] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const process = async () => {
      setLoading(true);
      try {
        const resolved = await promiseCreator();
        setResolved(resolved);
      } catch (e) {
        setError(e);
      }
      setLoading(false);
    };
    process();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);

  return [loading, resolved, error];
}

styled-components

const Category = styled.div` //styled(NavLink)
(...)
  //&.active {...} 대신 conditional styling 적용 
  ${(props) => //어떨 때 $ 가 필요할까?
    props.active &&
    css`
      font-weight: 600;
      border-bottom: 2px solid #22b8cf;
      color: #22b8cf;
      &:hover {
        color: #3bc9db;
      }
    `}
  & + & {
    margin-left: 1rem;
  }
`;

const Categories = ({ onSelect, category }) => {
  return (
    <CategoriesBlock>
      {categories.map((c) => (
        <Category
          key={c.name}
          active={category===c.name}
          onClick={() => onSelect(c.name)}
        >
          {c.text}
        </Category>
      ))}
    </CategoriesBlock>
  );
};

export default Categories;

어떨 때 '$' (dollar sign)이 필요할까?

  1. JSX {} vs Template Literals ${}

    • JSX uses {} to insert JavaScript directly into the UI.
    • Template literals require ${} to evaluate JavaScript inside backticks (``).
  2. When to Use ${}

    • ✅ Use ${} inside template literals (``) (Styled Components, console.log with backticks, etc.).
    • ❌ Don't use ${} inside JSX (<>...</>) (React JSX automatically evaluates JavaScript).
  3. Example: JavaScript Template Literals vs JSX

// Template Literal (Styled Components)
const name = "React";
console.log(`Hello ${name}`); // ✅ Needs ${} -> "Hello React"

// JSX
const name = "React";
return <h1>Hello {name}</h1>; // ✅ No ${} needed

본 후기는 [한글과컴퓨터x한국생산성본부x스나이퍼팩토리] 한컴 AI 아카데미 (B-log) 리뷰로 작성 되었습니다.

#한컴AI아카데미 #AI개발자 #AI개발자교육 #한글과컴퓨터 #한국생산성본부 #스나이퍼팩토리 #부트캠프 #AI전문가양성 #개발자교육 #개발자취업

0개의 댓글