- [React.js] 리액트 19의 혁신적인 `use` 훅: 비동기 데이터와 컨텍스트를 더 스마트하게2025년 09월 04일 11시 11분 05초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
리액트 19의 혁신적인
use훅: 비동기 데이터와 컨텍스트를 더 스마트하게!리액트 19의 등장은 프론트엔드 개발에 많은 변화를 예고하고 있다. 그중에서도 단연 눈길을 끄는 것은 바로 새로운 훅,
use훅이다. 이 훅은 기존 리액트 훅의 제약을 깨고, 비동기 데이터 처리와 컨텍스트 값 접근 방식을 혁신적으로 변화시킨다. 오늘은use훅이 무엇인지, 어떤 상황에서 사용하며, 리액트 쿼리 및 컨텍스트와는 어떤 관계를 맺는지 자세히 알아보자.
1.
use훅이란 무엇인가?use훅은 리액트 컴포넌트 내에서 Promise와 Context의 값을 읽어올 수 있게 해주는 새로운 훅이다. 가장 큰 특징은 기존 훅들과 달리if나for와 같은 조건문, 반복문 내부에서도 호출할 수 있다는 점이다. 이는 리액트 개발에 훨씬 더 큰 유연성을 제공한다.2.
use훅, 언제 사용하는가?use훅은 크게 두 가지 주요 상황에서 빛을 발한다.2.1. 비동기 Promise의 결과 읽기
use훅의 가장 강력한 기능 중 하나는 비동기 작업(Promise)의 결과를 마치 동기 코드처럼 처리할 수 있게 해주는 것이다.use(promise)를 사용하면, 해당 Promise가 해결(resolve)될 때까지 리액트 컴포넌트는 Suspense에 의해 일시 중단된다. 데이터가 준비되면 컴포넌트는 다시 렌더링되며, 에러 발생 시 가장 가까운 Error Boundary가 이를 처리한다.이는
useEffect와useState를 조합하여 로딩, 에러, 데이터 상태를 수동으로 관리하던 기존의 복잡한 비동기 데이터 페칭 로직을 매우 간결하게 만들어준다.예제 코드:
import { use, Suspense } from 'react'; // (가정) 비동기적으로 사용자 데이터를 가져오는 함수 async function fetchUserData(userId) { const response = await fetch(`/api/users/${userId}`); if (!response.ok) { throw new Error('사용자 데이터를 불러오지 못했습니다.'); } return response.json(); } function UserProfile({ userId }) { // use 훅을 사용하여 Promise의 결과를 '동기적으로' 읽어온다. // Promise가 해결될 때까지 Suspense가 작동한다. const user = use(fetchUserData(userId)); return ( <div> <h2>{user.name}님의 프로필</h2> <p>이메일: {user.email}</p> <p>가입일: {user.createdAt}</p> </div> ); } export default function App() { return ( <Suspense fallback={<div>사용자 정보를 불러오는 중...</div>}> <UserProfile userId={123} /> </Suspense> ); }App컴포넌트에서UserProfile이 데이터를 기다리는 동안Suspense의fallback메시지가 표시된다. 데이터가 성공적으로 로드되면UserProfile이 렌더링된다.2.2. 컨텍스트(Context) 값 읽기
use훅은useContext훅과 유사하게 컨텍스트 값을 읽어올 수 있다. 하지만useContext와 달리if문 안에서도 호출이 가능하다는 유연성을 제공한다. 이는 특정 조건에서만 컨텍스트 값이 필요한 경우에 유용하다.예제 코드:
import { createContext, use } from 'react'; // Context 생성 const ThemeContext = createContext('light'); // 기본값: 'light' function ThemeToggle() { // use 훅으로 ThemeContext 값 읽기 const theme = use(ThemeContext); // 조건부 렌더링 if (theme === 'dark') { return <button style={{ background: 'black', color: 'white' }}>다크 모드</button>; } else { return <button style={{ background: 'white', color: 'black' }}>라이트 모드</button>; } } export default function App() { return ( // Provider로 'dark' 테마 제공 <ThemeContext.Provider value="dark"> <ThemeToggle /> </ThemeContext.Provider> ); }
3. 리액트 쿼리(React Query)와
use훅: 무엇이 다르고 어떻게 함께 쓰는가?use훅의 등장으로 "리액트 쿼리 같은 데이터 페칭 라이브러리가 필요 없어지는 것 아닌가요?"라는 질문이 생길 수 있다. 결론부터 말하면, 아니다. 두 기술은 용도가 다르며, 상호 보완적으로 사용될 수 있다.3.1. 리액트 쿼리와
use훅의 차이점- 리액트 쿼리 (
@tanstack/react-query): 서버 상태(Server State) 관리에 특화된 강력한 라이브러리이다. 데이터 페칭, 캐싱, 동기화, 백그라운드 리페칭, 무한 스크롤 등 복잡한 서버 데이터 관리 로직과 성능 최적화 기능을 포괄적으로 제공한다. use훅: Promise와 Context 값을 '읽어오는' 리액트의 내장 메커니즘이다. 캐싱이나 백그라운드 리페칭과 같은 고급 서버 상태 관리 기능은 제공하지 않으며, 주로 Suspense와 연동하여 비동기 작업의 결과를 간결하게 처리하는 데 중점을 둔다.
즉, 리액트 쿼리는 데이터 관리의 종합 솔루션이라면,
use훅은 비동기 결과를 다루는 리액트의 기본 도구이다.3.2. 리액트 쿼리와
use훅 함께 사용하는 방법리액트 쿼리가 서버 상태를 효율적으로 관리하고,
use훅은 그 외의 비동기 작업이나 Context 값을 유연하게 처리하는 데 사용될 수 있다. 다음 두 가지 경우를 중심으로 살펴보자.리액트 쿼리의
useQuery를 조건부로 호출하지 않고, 옵션으로 컨텍스트 값을 전달할 때- 핵심: 리액트 훅(
useQuery포함)은 항상 컴포넌트의 최상위 레벨에서 호출되어야 한다.use훅으로 컨텍스트 값을 읽어온 후, 그 값에 따라useQuery를if문 안에 넣는 것은 훅 규칙 위반이다. - 올바른 접근:
use훅은 컨텍스트 값을 읽는 데 사용하고, 그 값을useQuery의queryKey나queryFn,enabled옵션으로 전달하여 동적인 쿼리를 구성한다.
import { createContext, use } from 'react'; import { useQuery } from '@tanstack/react-query'; const UserRoleContext = createContext('guest'); // 기본값: guest // 역할에 따라 다른 데이터를 가져오는 쿼리 함수 (예시) async function fetchDataByRole(role) { if (role === 'admin') { const res = await fetch('/api/admin-data'); return res.json(); } else if (role === 'user') { const res = await fetch('/api/user-data'); return res.json(); } return null; // guest인 경우 데이터 없음 } function Dashboard() { // use 훅으로 UserRoleContext의 값 읽기 const userRole = use(UserRoleContext); // useQuery는 항상 최상위에서 호출. userRole 값에 따라 쿼리 동작 제어. const { data, isLoading, error } = useQuery({ queryKey: ['dashboardData', userRole], // userRole이 바뀌면 쿼리 재실행 queryFn: () => fetchDataByRole(userRole), enabled: userRole !== 'guest', // guest일 때는 쿼리 비활성화 }); if (isLoading) return <div>데이터 로딩 중...</div>; if (error) return <div>에러 발생: {error.message}</div>; if (!data) return <div>{userRole === 'guest' ? '로그인이 필요하다.' : '데이터가 없다.'}</div>; return ( <div> <h2>{userRole} 대시보드</h2> <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); } export default function App() { return ( <UserRoleContext.Provider value="admin"> <Dashboard /> </UserRoleContext.Provider> ) }이 예제에서
use훅으로 읽어온userRole값은useQuery의queryKey와queryFn에 인자로 전달되어 쿼리의 내용을 동적으로 변경하고,enabled옵션을 통해 쿼리 실행 여부를 제어한다.- 핵심: 리액트 훅(
서버 데이터와 관련 없는 클라이언트 사이드 비동기 작업을 처리할 때
- 핵심: 리액트 쿼리는 서버 상태 관리가 주 목적이다. 로컬 스토리지 접근, 웹 워커 통신, 클라이언트에서 발생하는 지연 로딩 등 서버와 무관한 비동기 작업은
use훅으로 처리하여 역할 분담을 명확히 할 수 있다.
import { useQuery } from '@tanstack/react-query'; import { use } from 'react'; // (가정) 로컬 스토리지에서 사용자 설정을 비동기적으로 불러오는 함수 // 실제로는 localStorage API는 동기적이지만, 비동기 처리가 필요하다고 가정 const getLocalSettingsAsync = () => { return new Promise((resolve) => { setTimeout(() => { resolve({ theme: 'dark', notifications: true }); }, 300); // 0.3초 지연 }); }; function UserSettings() { // 1. 리액트 쿼리를 사용하여 서버에서 사용자 프로필 정보 페칭 const { data: serverProfile, isLoading: isProfileLoading } = useQuery({ queryKey: ['userProfile'], queryFn: () => fetch('/api/profile').then(res => res.json()), }); // 2. use 훅을 사용하여 서버와 무관한 비동기 클라이언트 로직 처리 // Suspense에 의해 로딩될 때까지 기다린다. const localSettings = use(getLocalSettingsAsync()); if (isProfileLoading) return <div>프로필 로딩 중...</div>; if (!serverProfile) return <div>프로필을 불러오지 못했다.</div>; return ( <div> <h1>환영한다, {serverProfile.name}님!</h1> <h3>사용자 설정 (클라이언트)</h3> <p>테마: {localSettings.theme}</p> <p>알림: {localSettings.notifications ? '켜짐' : '꺼짐'}</p> </div> ); }이 예시에서
useQuery는 서버 API로부터 사용자 프로필을 가져오는 데 집중하고,use훅은 클라이언트 사이드의 비동기 작업(로컬 스토리지 설정 로딩)을 처리한다. 각자의 강점을 활용하여 더욱 효율적인 코드 작성을 가능하게 한다.- 핵심: 리액트 쿼리는 서버 상태 관리가 주 목적이다. 로컬 스토리지 접근, 웹 워커 통신, 클라이언트에서 발생하는 지연 로딩 등 서버와 무관한 비동기 작업은
✨ 컨텍스트(Context)와 전역 상태 관리
컨텍스트 (Context)란?
리액트에서 컴포넌트 트리를 통해 데이터를 전달하는 메커니즘이다. 일반적으로 props를 통해 데이터를 전달하지만, 트리가 깊어지면 'prop drilling' 문제가 발생한다. 컨텍스트는 이 문제를 해결하기 위해 특정 데이터를 전역적으로 관리하여, 트리의 어느 위치에 있는 컴포넌트든 해당 데이터에 직접 접근할 수 있도록 해준다.
Provider로 데이터를 제공하고,useContext훅(또는use훅)으로 데이터를 소비한다.컨텍스트는 위에서 설명했듯이 데이터를 트리 아래로 전달하는 도구이지만, 그 자체로 전역 상태 관리 라이브러리는 아니다.
컨텍스트 vs 전역 상태 관리 라이브러리 (Redux, Zustand 등)
컨텍스트: 데이터를 전달하는 '방법'에 가깝다.
Context.Provider의value가 변경되면 해당 컨텍스트를 사용하는 모든 하위 컴포넌트가 무조건 다시 렌더링된다. 이는 데이터가 자주 변경되는 경우 불필요한 렌더링을 유발하여 성능 저하의 원인이 될 수 있다. 주로 테마, 사용자 인증 정보, 언어 설정 등 자주 변경되지 않는 정적인 데이터를 공유할 때 적합하다.전역 상태 관리 라이브러리 (예: Zustand): 전역 상태를 효율적으로 관리하기 위한 복잡한 로직과 최적화 기법을 내장하고 있다. 상태의 특정 부분만 업데이트되더라도 해당 상태를 사용하는 컴포넌트만 선택적으로 다시 렌더링되도록 설계되어 있다. 또한 개발자 도구, 미들웨어 등 디버깅 및 확장 기능을 제공하여 잦은 업데이트가 발생하는 동적인 상태나 복잡한 로직을 관리하는 데 훨씬 효과적이다.
결론적으로 컨텍스트는 단순한 전역 데이터 공유에는 충분하지만, 복잡하고 동적인 애플리케이션의 전역 상태 관리를 위해서는 리덕스, Zustand와 같은 전용 라이브러리를 고려하는 것이 좋다.
728x90반응형'언어·프레임워크 > React.js' 카테고리의 다른 글
React 로직 재사용 패러다임의 전환: HOC와 Hooks 분석 (0) 2026.01.06 React.memo 없이도 최적화가 가능하다? 컴포지션과 상태 지역화 (4) 2025.08.12 [React.js] Spring Boot와 연동 시 `Invalid URL` 오류 해결기(리액트 프로젝트 중단점 설정) (0) 2025.06.18 [React.js] 네이버 지도 API PDF 변환 시 CORS 오류 완벽 해결법 (0) 2025.06.16 [React.js] 메모이제이션 완벽 가이드: memo, useCallback, useMemo와 Profiler 활용 (0) 2025.05.25 다음글이 없습니다.이전글이 없습니다.댓글 - 리액트 쿼리 (