paint-brush
React.js Context API로 상태 관리를 단순화하는 방법 - 튜토리얼~에 의해@codebucks
669 판독값
669 판독값

React.js Context API로 상태 관리를 단순화하는 방법 - 튜토리얼

~에 의해 CodeBucks11m2024/08/02
Read on Terminal Reader

너무 오래; 읽다

이 블로그는 Context API를 사용하여 React에서 상태를 관리하는 방법에 대한 포괄적인 가이드를 제공합니다. 소품 드릴링을 방지하고 성능을 향상하며 Context API를 효과적으로 구현하는 방법을 설명합니다. 실용적인 예제와 최적화 팁이 포함되어 있어 React 애플리케이션에서 상태 관리를 간소화하려는 개발자에게 적합합니다.
featured image - React.js Context API로 상태 관리를 단순화하는 방법 - 튜토리얼
CodeBucks HackerNoon profile picture
0-item

안녕하세요👋🏻,


이 문서는 여러 구성 요소 간의 상태를 관리하는 보다 효과적인 방법을 배우고 싶어하는 초보자를 위해 특별히 작성되었습니다. 또한 코드를 유지 관리하고 이해하기 어렵게 만드는 일반적인 소품 드릴링 문제를 해결하는 것을 목표로 합니다. 어떤 종류의 문제 컨텍스트 API가 해결하는지부터 시작해 보겠습니다.


비디오 형식을 선호한다면 여기 내 YouTube 채널에서 시청할 수 있는 튜토리얼이 있습니다.👇🏻


프롭 드릴링이란 무엇입니까?

부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달해야 하는 경우가 있는데, 그 사이에 여러 컴포넌트를 통해 props를 전달하게 되는 경우가 있다는 것을 알고 계십니까? 이를 프롭 드릴링 이라고 하며 빠르게 지저분해질 수 있습니다. 이를 명확히 하기 위해 예제를 살펴보겠습니다.


React.js의 소품 드릴링

다이어그램에 표시된 대로 애플리케이션의 루트에 있는 App 구성 요소에서 일부 데이터를 가져왔다고 가정해 보세요. 이제 Grandchild 구성 요소와 같이 깊게 중첩된 구성 요소가 이 데이터에 액세스해야 하는 경우 일반적으로 Grandchild 에 도달하기 전에 ParentChild 구성 요소를 소품으로 전달합니다. 앱이 성장함에 따라 이는 추악해질 수 있습니다.


또 다른 시각적 표현은 다음과 같습니다.


Reactjs 소품 드릴링 예

위의 예에서 Profile 구성 요소에는 사용자 데이터가 필요하지만 중간 구성 요소가 데이터 자체를 사용하지 않더라도 이 데이터는 AppNavigation 구성 요소를 먼저 통과해야 합니다. 그럼 어떻게 정리할까요? 이것이 바로 Context API가 유용한 곳입니다.


소품 드릴링:

  • 구성 요소의 다시 렌더링 증가
  • 상용구 코드 증가
  • 구성요소 종속성을 생성합니다.
  • 성능 저하

반응 컨텍스트 API

React.js의 Context API를 사용하면 구성 요소 트리의 각 수준을 통해 소품으로 전달할 필요 없이 구성 요소 간에 데이터를 전달할 수 있습니다. 이는 컨텍스트 개체에서 상태를 정의한 다음 구성 요소 트리의 어느 위치에서나 쉽게 액세스할 수 있는 전역 상태 관리 시스템처럼 작동합니다. 예를 들어 이것을 이해해 봅시다.


React.js 컨텍스트 API

다이어그램에서 볼 수 있듯이 여러 구성 요소에서 액세스할 데이터를 저장하는 컨텍스트 개체가 있습니다. 이 데이터는 API 또는 타사 서비스에서 가져옵니다. 구성 요소에서 이 컨텍스트 데이터에 액세스하기 전에 이 데이터가 필요한 모든 구성 요소를 컨텍스트 공급자 구성 요소로 래핑해야 합니다.


탐색 및 프로필 구성 요소의 데이터에만 액세스해야 하는 경우 앱 구성 요소를 마무리할 필요가 없습니다. ContextProvider 사용하여 관련 구성 요소를 래핑한 후에는 이를 사용하는 모든 구성 요소의 컨텍스트 데이터에 직접 액세스할 수 있습니다. 아직 이해하지 못하더라도 걱정하지 마세요. 코드를 자세히 살펴보고 실제로 작동하는 모습을 살펴보겠습니다.


컨텍스트 API를 사용하는 방법?

먼저 Vite.js를 사용하여 React 앱을 만들어 보겠습니다. 프로젝트를 설정하려면 다음 명령을 복사하세요.


 npm create vite@latest


  • 프로젝트 이름을 추가하세요.
  • 반응 선택
  • 옵션에서 타이프스크립트를 선택하세요
 cd project_name // to change to project directory npm install npm run dev


그런 다음 브라우저에서 개발 서버 http://localhost:5173 열 수 있습니다.


먼저 필요한 폴더를 만들어 보겠습니다. 다음은 프로젝트의 폴더 구조입니다.

 src | components | context


구성 요소 폴더에 Profile.jsx 파일을 만들고 다음 코드를 추가해 보겠습니다.

 import React from 'react' const Profile = () => { return ( <div>Profile</div> ) } export default Profile


구성 요소 폴더에 Navbar.jsx 라는 구성 요소를 하나 더 만듭니다.

 import Profile from './Profile' const Navbar = () => { return ( <nav style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "90%", height: "10vh", backgroundColor: theme === "light" ? "#fff" : "#1b1b1b", color: theme === "light" ? "#1b1b1b" : "#fff", border: "1px solid #fff", borderRadius: "5px", padding: "0 20px", marginTop: "40px", }}> <h1>LOGO</h1> <Profile /> </nav> ) } export default Navbar


App.jsx 파일에서 이 <Navbar /> 구성 요소를 가져오겠습니다.

 import Navbar from "./components/Navbar"; function App() { return ( <main style={{ display: "flex", flexDirection: "column", justifyContent: "start", alignItems: "center", height: "100vh", width: "100vw", }} > <Navbar /> </main> ); } export default App;


따라서 기본적으로 <Profile /> 구성 요소는 <Navbar /> 의 하위 요소이고 <Navbar /> <App /> 구성 요소의 하위 요소입니다.

컨텍스트 API 추가

context 폴더에 UserContext.jsx 파일을 생성해 보겠습니다. 파일에 다음 코드를 추가합니다.


 import { createContext, useEffect, useState } from "react"; export const UserContext = createContext(); export const UserProvider = ({ children }) => { const [user, setUser] = useState(null); const fetchUserData = async (id) => { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}` ).then((response) => response.json()); console.log(response); setUser(response); }; useEffect(() => { fetchUserData(1); }, []); return ( <UserContext.Provider value={{ user, fetchUserData }} > {children} </UserContext.Provider> ); };


  • 먼저 createContext 사용하여 빈 UserContext 객체를 생성합니다. 우리는 그것을 반드시 react 에서 import해야 합니다. 컨텍스트 객체 내에 기본값을 추가할 수 있지만 지금은 null로 유지합니다.


  • 다음으로 UserContext.Provider 와 같이 UserContext 사용하여 공급자를 반환하는 UserProvider 만듭니다. 이는 하위 구성 요소를 둘러싸며 값에서 하위 구성 요소에 사용하려는 모든 항목을 전달할 수 있습니다.


  • 현재 우리는 jsonplaceholder API를 사용하여 사용자 데이터를 가져오고 있습니다. jsonplaceholder는 테스트 목적으로 가짜 API 엔드포인트를 제공합니다. fetchUserData 함수는 id 받아들이고 해당 ID를 사용하여 사용자 데이터를 가져옵니다. 그런 다음 응답을 user 상태에 저장합니다.


  • useEffect 에서 fetchUserData 함수를 호출하므로 페이지 로드 시 함수가 호출되고 user 상태에 데이터가 주입됩니다.


이제 <App /> 컴포넌트에서 이 컨텍스트를 사용해 보겠습니다. <UserProvider /> 를 사용하여 <NavBar /> 구성 요소를 래핑합니다. 다음 코드와 동일합니다.

 <UserProvider> <Navbar /> </UserProvider>


<Profile /> 구성 요소에서 user 상태를 사용해 보겠습니다. 이를 위해 useContext 후크를 사용합니다. 이는 UserContext 사용하고 user 상태 및 fetchUserData 함수와 같이 UserProvider 에 전달한 값을 제공합니다. <Profile /> 구성 요소는 이미 공급자와 함께 래핑된 <Navbar /> 구성 요소에 있으므로 래핑할 필요가 없다는 점을 기억하세요.


Profile.jsx 열고 다음 코드를 추가합니다.

 const { user } = useContext(UserContext); if (user) { return ( <span style={{ fontWeight: "bold", }} > {user.name} </span> ); } else { return <span>Login</span>; }


여기서는 UserContextuser 상태를 사용하고 있습니다. user 있으면 사용자 이름을 표시하고, 그렇지 않으면 로그인 메시지만 표시합니다. 이제 출력이 표시되면 navbar 구성 요소에 사용자 이름이 있어야 합니다. 이것이 모든 구성 요소의 컨텍스트에 있는 모든 상태를 직접 사용할 수 있는 방법입니다. 이 상태를 사용하는 구성 요소는 <Provider /> 내에 래핑되어야 합니다.


여러 컨텍스트를 사용할 수도 있습니다. 다음 예제와 같이 공급자 구성 요소를 다른 공급자 구성 요소 내에 래핑하기만 하면 됩니다.

 <ThemeProvider> <UserProvider> <Navbar /> </UserProvider> </ThemeProvider>


위의 예에서는 테마 상태를 관리하는 <ThemeProvider /> 사용하고 있습니다.


위의 YouTube 비디오를 시청하면 여러 컨텍스트 공급자를 사용하는 전체 예를 볼 수 있습니다.

React Context API에서 재렌더링 최적화

여러 구성 요소에서 Context API를 사용할 때 발생하는 한 가지 문제가 있습니다. Context API에서 상태나 값이 변경될 때마다 모든 구성 요소가 변경된 상태를 사용하지 않는 경우에도 해당 특정 컨텍스트를 구독하는 모든 구성 요소를 다시 렌더링합니다. 이 다시 렌더링 문제를 이해하기 위해 컨텍스트를 사용하여 개수 값을 저장하고 표시하는 <Counter /> 구성 요소를 만들어 보겠습니다.


다음 예를 확인하세요. 구성 요소 폴더에 Counter.jsx 파일을 만들고 다음 코드를 붙여 넣을 수 있습니다.


 import { createContext, memo, useContext, useState } from "react"; const CountContext = createContext(); const CountProvider = ({ children }) => { const [count, setCount] = useState(0); return ( <CountContext.Provider value={{ count, setCount }}> {children} </CountContext.Provider> ); }; function CountTitle() { console.log("This is Count Title component"); return <h1>Counter Title</h1>; } function CountDisplay() { console.log("This is CountDisplay component"); const { count } = useContext(CountContext); return <div>Count: {count}</div>; } function CounterButton() { console.log("This is CounterButton component"); const { count, setCount } = useContext(CountContext); return ( <> <CountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </> ); } export default function Counter() { return ( <CountProvider> <CounterButton /> </CountProvider> ); }


위 코드에서:

  • 먼저 createContext를 사용하여 하나의 CountContext 객체를 생성합니다 createContext.


  • CountProvider, 에는 개수 값을 저장하는 하나의 상태가 있습니다. value prop을 통해 countsetCount 메소드를 하위 컴포넌트로 전송합니다.


  • 개별 구성 요소가 몇 번이나 다시 렌더링되는지 확인하기 위해 구성 요소를 별도로 만들었습니다.

    • <CountTitle /> : 이 구성 요소는 제목만 표시하고 컨텍스트의 값도 사용하지 않습니다.

    • <CountDisplay /> : 이 구성 요소는 카운트 값을 표시하고 컨텍스트의 count 상태를 사용합니다.

    • <CounterButton /> : 이 구성 요소는 위 구성 요소와 setCount 사용하여 카운트 값을 증가시키는 버튼을 모두 렌더링합니다.


  • 마지막에는 다른 구성 요소가 카운트 값에 액세스할 수 있도록 <CounterButton /> 구성 요소를 CountProvider 구성 요소 내에 래핑합니다.


이제 코드를 실행하고 Increase 버튼을 클릭하면 상태가 변경될 때마다 모든 구성 요소가 다시 렌더링된다는 것을 로그에서 볼 수 있습니다. <CountTitle /> 은 count 값을 사용하지도 않지만 다시 렌더링됩니다. 이는 < <CounterButton /> <CountTitle /> 의 상위 구성 요소가 count 값을 사용 및 업데이트하고 있으며 이것이 다시 렌더링되기 때문에 발생합니다.


이 동작을 어떻게 최적화할 수 있습니까? 답은 memo 입니다. React memo 하면 props가 변경되지 않은 구성 요소를 다시 렌더링하는 것을 건너뛸 수 있습니다. <CountTitle /> 구성 요소 뒤에 다음 줄을 추가해 보겠습니다.


 const MemoizedCountTitle = React.memo(CountTitle)


이제 <CountTitle /> /> 구성 요소를 렌더링하는 <CounterButton /> 구성 요소에서 다음 코드와 같이 <CountTitle /> <MemoizedCountTitle /> 로 바꿉니다.


 <> <MemoizedCountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </>


이제 개수를 늘리고 로그를 확인하면 <CountTitle /> 구성 요소가 더 이상 렌더링되지 않는 것을 확인할 수 있습니다.

Redux와 컨텍스트 API

Redux 보다 예측 가능한 상태 전환을 갖춘 복잡한 상태 관리를 위한 상태 관리 라이브러리입니다. Context API는 간단한 상태 관리 및 소품 드릴링 없이 구성 요소 트리를 통해 데이터를 전달하도록 설계되었습니다. 그렇다면 언제 무엇을 선택해야 할까요?


  • 상태가 자주 변경되지 않는 간단하고 지역화된 상태 관리를 위해 React Context API를 사용하세요.


  • 복잡한 상태 관리 요구 사항에 Redux를 사용하세요. 특히 구조화된 상태 관리의 이점이 추가 설정보다 더 큰 대규모 애플리케이션에서는 더욱 그렇습니다.


또한 상태 관리에 널리 사용되는 옵션인 라이브러리가 하나 더 있습니다. 반응 반동 .


  • React Recoil은 Redux의 강력한 성능과 함께 Context API의 단순성을 제공하는 것을 목표로 하는 React용 상태 관리 라이브러리입니다.


React Recoil 에 대해 더 자세히 알고 싶으시면 댓글로 알려주세요. 피드백을 바탕으로 이 주제에 대한 심층적인 튜토리얼을 만들겠습니다.

결론

React.js Context API는 여러 구성 요소의 상태를 관리하는 강력하고 효율적인 방법을 제공하여 prop 드릴링 문제를 효과적으로 해결합니다. Context API를 사용하면 코드를 단순화하고, 불필요한 재렌더링을 줄이고, 전반적인 애플리케이션 성능을 향상시킬 수 있습니다.


Context API는 간단한 상태 관리에 이상적이지만 ReduxReact Recoil 과 같은 다른 상태 관리 라이브러리를 사용하면 더 복잡한 애플리케이션에 도움이 될 수 있습니다. 이러한 도구를 언제, 어떻게 사용하는지 이해하면 유지 관리 및 확장성이 뛰어난 React 애플리케이션을 구축할 수 있습니다.


이 글을 읽어주셔서 감사합니다. 도움이 되셨기를 바랍니다. React, Redux 및 Next.js를 사용하여 프로젝트를 배우고 구축하는 데 관심이 있다면 내 YouTube 채널( CodeBucks )을 방문하세요.


당신이 읽고 싶어할 만한 다른 기사는 다음과 같습니다.

내 개인 블로그를 방문하세요: DevDreaming