앞선 포스트에서는 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 는 현재 가장 많이 사용되고 있는 자바스크립트 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 내에서 호출하여) 사용해야 한다.
// 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];
}
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;
JSX {}
vs Template Literals ${}
{}
to insert JavaScript directly into the UI.${}
to evaluate JavaScript inside backticks (``
).When to Use ${}
${}
inside template literals (``
) (Styled Components, console.log
with backticks, etc.).${}
inside JSX (<>...</>
) (React JSX automatically evaluates JavaScript).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전문가양성 #개발자교육 #개발자취업