비동기(Asynchronous)
- 일반적으로 프로그램 코드는 한 번에 한 가지씩 순차적으로 진행합니다. 만약에 어떤 함수의 결과가 다른 함수에 영향을 받는 다면, 그 함수는 다른 함수가 끝나고 값을 산출할 떄까지 기다려야 합니다.
setTimeout(() => {
console.log("첫 번째 메시지");
}, 5000);
setTimeout(() => {
console.log("두 번째 메시지");
}, 3000);
setTimeout(() => {
console.log("세 번째 메시지");
}, 1000);
// 콘솔 출력:
// 세 번째 메시지
// 두 번째 메시지
// 첫 번째 메시지
Promise
비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과값을 나타냅니다.
프로미스가 생성된 시점에는 알려지지 않을 수 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있습니다.
new Promise((resilve, reject) => {
//비동기 방식으로 실행시킬 코드
}
이 코드의 실행이 완료될 때까지 기다리지 않고, 다음 코드를 실행시키고 이 코드의 실행이 완료되면, 특정코드를 실행시킵니다.
let myFirstPromise = new Promise((resolve, reject) => {
// 우리가 수행한 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출합니다.
setTimeout(function () {
resolve("성공!");
}, 250);
});
myFirstPromise.then((successMessage) => { //successMessage에는 성공!이 들어가있습니다.
console.log("와! " + successMessage);
}, (failureMeassage) =>{
alert(`${failureMeassage}`)
}
);
async await
promise는 우리 코드에서 promise가 fulfil될 때까지 잠시 중단하고, 결과를 반환합니다. 그리고 실행을 기다리는 다른 코드들을 중지시키지 않고 그대로 실행되게 합니다. 하지만, await 키워드는 웹 API를 포함하여 Promise를 반환하는 함수를 호출할 때 사용할 수 있습니다.
async function fetchUserData() {
try {
console.log("데이터 요청 중...");
// Promise를 반환하는 fetch API 호출
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
// 응답 데이터를 JSON으로 변환
const userData = await response.json();
console.log("데이터 가져오기 성공:", userData);
} catch (error) {
console.error("데이터 가져오기 실패:", error);
}
}
// 함수 호출
fetchUserData();
console.log("다른 작업 실행 중...");
axios
Promise 기반의 HTTP 클라이언트 라이브러리로, 브라우저와 Node.js 환경에서 사용할 수 있습니다. 주로 API 호출을 간편하게 처리하기 위해 사용됩니다.
import axios from 'axios';
async function fetchData() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log('데이터:', response.data);
} catch (error) {
console.error('에러 발생:', error);
}
}
fetchData();
useEffect 함수는 첫번째 인자로 보내는 함수는 아무것도 반환하지 않거나 return을 통해 반환합니다. 주의할 점은useEffect 함수는 프로미스를 반환하면 안됩니다.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function App() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
setData(response.data);
} catch (error) {
console.error('데이터를 가져오는 중 에러 발생:', error);
}
};
fetchData();
}, []); // 빈 배열로 설정하여 컴포넌트가 처음 렌더링될 때만 실행
return (
<div>
<h1>Post</h1>
{data ? (
<div>
<h2>{data.title}</h2>
<p>{data.body}</p>
</div>
) : (
<p>로딩 중...</p>
)}
</div>
);
}
export default App;
로딩 처리나 에러 핸들링할 때 API를 호출하면서 hooks들을 많이 호출하는데 여기서 문제는 여러곳에서 hooks를 호출을 많이 되게 됩니다.
const [ loading, setLoading ] = useState(false)
useEffect(() => {
// setLoading(true)
// api호출
// 실행이 완료되면 setLoading(false)
}, [])
그래서 custom hooks를 사용하면 좋습니다.
Custom Hooks는 React에서 상태 로직을 재사용 가능하도록 캡슐화한 함수입니다. 여러 컴포넌트에서 동일한 로직을 반복적으로 사용할 경우, 이를 Custom Hook으로 분리하여 코드의 중복을 줄이고 유지보수성을 높일 수 있습니다.
특징
1) use로 시작: React에서 Hook으로 인식되도록 이름은 반드시 use로 시작해야 합니다.
예: useFetch, useToggle, useForm React
2) 기본 Hooks 사용: 내부에서 useState, useEffect, useReducer 등 React 기본 Hooks를 호출하여 상태 관리나 부수 효과를 처리합니다.
3) 재사용 가능: 여러 컴포넌트에서 동일한 로직을 쉽게 재사용할 수 있습니다.
4) JSX 포함 금지: Custom Hooks는 로직만 포함하며, JSX 코드를 포함해서는 안 됩니다.
값 토글하는 Custom Hook
import { useState } from "react";
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = () => setValue((prevValue) => !prevValue);
return [value, toggle];
}
export default useToggle;
Custom Hook사용하기
import React from "react";
import useToggle from "./useToggle";
function App() {
const [isOn, toggleIsOn] = useToggle();
return (
<div>
<p>{isOn ? "켜짐" : "꺼짐"}</p>
<button onClick={toggleIsOn}>토글</button>
</div>
);
}
useFetch
React에서 API 데이터를 가져오는 로직을 재사용 가능하게 만들기 위해 사용하는 Custom Hook입니다. 여러 컴포넌트에서 동일한 데이터 패칭 로직을 반복적으로 작성하지 않도록 캡슐화하여 코드의 중복을 줄이고 유지보수성을 높이는 데 유용합니다.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null); // API 데이터를 저장
const [isLoading, setIsLoading] = useState(true); // 로딩 상태 관리
const [error, setError] = useState(null); // 에러 상태 관리
useEffect(() => {
const fetchData = async () => {
setIsLoading(true); // 로딩 시작
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('데이터를 가져오는 중 문제가 발생했습니다.');
}
const result = await response.json();
setData(result); // 데이터 저장
setError(null); // 에러 초기화
} catch (err) {
setError(err.message); // 에러 저장
} finally {
setIsLoading(false); // 로딩 종료
}
};
fetchData();
}, [url]); // URL이 변경될 때마다 실행
return { data, isLoading, error }; // 필요한 값 반환
}
export default useFetch;
import React from 'react';
import useFetch from './useFetch';
function App() {
const { data, isLoading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
if (isLoading) return <p>로딩 중...</p>;
if (error) return <p>에러 발생: {error}</p>;
return (
<div>
<h1>Posts</h1>
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
export default App;
SWR
캐시된 데이터를 먼저 반환하고, 백그라운드에서 최신 데이터를 가져와 UI를 업데이트하는 방식입니다.
use시작하는건 훅이라서 함수안에 있어야합니다. useFeat와 다르게 내부적으로 useEffte가 포함하는게 아니라 컴포넌트 마운트된 직후에 딱한번만 api를 호출하는게 아닙니다.
한번만 데이터를 패치를 하고 캐치를 하고 있는데 리랜더링 될때마다 반복적으로 실행은 되지만 이전에 키값을 가진 것을 실행한 내역이 있다면 실행하지 않고 바로 반환을 해줍니다.
data, error, isLoding을 가지고 실행중일 때, 에러일 때, 로딩중일 때 세가지를 표현할 수 있습니다.
import useSWR from 'swr';
const fetcher = url => fetch(url).then(res => res.json());
function UserProfile() {
const { data, error, isLoading } = useSWR('/api/user', fetcher); ///api/user은 키값
if (error) return <div>Error loading data</div>;
if (isLoading) return <div>Loading...</div>;
return <div>Hello, {data.name}!</div>;
}
fetcher 함수에 id 값을 가져올 수 있습니다.
function useUser (id) {
const { data, error, isLoading } = useSWR(`/api/user/${id}`, fetcher)
return {
user: data,
isLoading,
isError: error
}
}
React Query
React 애플리케이션에서 서버 상태(server state)를 효율적으로 관리하기 위한 데이터 패칭 및 상태 관리 라이브러리입니다. 서버 상태는 비동기적으로 데이터를 가져오고, 캐싱하며, 동기화하고 업데이트하는 작업을 포함하며, React Query는 이를 단순화하여 개발자의 생산성을 높이고 애플리케이션 성능을 최적화합니다.
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<Example />
</QueryClientProvider>
);
}
import { useQuery } from '@tanstack/react-query';
function Example() {
const { isLoading, error, data } = useQuery(['repoData'], () =>
fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => res.json())
);
if (isLoading) return 'Loading...';
if (error) return 'An error has occurred: ' + error.message;
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>
<strong>✨ {data.stargazers_count}</strong>
<strong>🍴 {data.forks_count}</strong>
</div>
);
}
'리액트' 카테고리의 다른 글
[React] Reate Query와 Suspense (0) | 2025.04.15 |
---|---|
[React] React Context API를 활용한 전역 상태 관리 (1) | 2025.04.14 |
[React] SPA와 react-router-dom (0) | 2025.04.10 |
[React] - 리스트와 key (0) | 2025.04.09 |
[React] useEffect 정복하기 (0) | 2025.04.07 |