ContextAPI는 React 웹 애플리케이션 개발 시 컴포넌트 트리 내부에서 데이터들을 전역적으로 공유할 수 있는 방법을 제공하는 방법입니다.
props
를 단계별로 전달하는 Props Drilling을 해결하기 위해 사용됩니다.Context
를 통해, 중간에 있는 여러 컴포넌트를 거치지 않고도, 컴포넌트 트리 깊은 곳에 있는 컴포넌트에 데이터를 전달할 수 있게 됩니다.전역 상태관리 라이브러리 Redux,Zustand등과 같이 store 내부에 상태나 데이터를 정의하고, Props Drilling 없이 전역적으로 상태를 참조하고, 사용할 수 있다는 관점에선 동일한 역할을 지닙니다.
Context API를 사용할때, 해당 컨텍스트를 구독하는 모든 컴포넌트들은 상태변화와 상관없이 모두 리렌더링이 발생합니다.
이를 증명하기 위해 Context API
로 간단한 유저상태를 구현하겠습니다.
먼저 UserContext라는 파일을 생성한후, 해당 Context를 전역적으로 공급하기 위한 간단한 코드를 작성했습니다.
users
의 초기상태를 나타내는 initalUsersProfile와, users
의 상태를 변경하는 함수인 setUser
를 내보냈습니다.
/* 📑 UserContext.jsx */
import {createContext, useState} from "react";
/* Context 기본값 설정 */
const initalUsersProfile = {
name: "Seju",
posts: [
"Context API가 상태관리 라이브러리의 대안이 될 수 없는 이유",
"Babel 왜쓰고 왜 사용할까?",
],
};
export const UserContext = createContext({
user: initalUsersProfile,
setUser: () => {},
});
/* Provider로 전역적으로 상태를 공급. */
const UserProvider = ({children}) => {
const [user, setUser] = useState(initalUsersProfile);
return (
<UserContext.Provider value={{user, setUser}}>
{children}
</UserContext.Provider>
);
};
export default UserProvider;
Main.jsx
에서 구성한 UseProvider
함수를 공급합니다.context
를 사용하는 <UserProfile>
컴포넌트와 context
와는 연관이 없는, 정적인 JSX를 가지고 있는 <UserPosts>
컴포넌트를 생성했습니다.main.jsx
Context를 사용하는 UserProfile 컴포넌트
/* 📑 UserProfile.jsx */
import {useContext} from "react";
import {UserContext} from "../context/UserContext";
function UserProfile() {
const {user} = useContext(UserContext);
console.log("UserProfile 컴포넌트 리렌더링 발생");
return (
<>
<div>{user.name}</div>
</>
);
}
export default UserProfile;
Context와 연관이 없는 UserPost 컴포넌트
/* 📑 UserPosts.jsx */
function UserPosts() {
console.log("UserPost 컴포넌트 리렌더링 발생");
return (
<>
<div>Some Static Content</div>
</>
);
}
export default UserPosts;
해당 컴포넌트 두개를 전부 렌더링하고 있는 App.jsx
App.jsx
에서 유저상태를 변경하는 setUser
함수를 가져오고,/* 📑 App.jsx */
function App() {
const {setUser} = useContext(UserContext);
const handleChangeUserName = () => {
setUser((prevUser) => ({
...prevUser,
name: "suwan",
}));
};
return (
<>
<UserProfile />
<UserPosts />
<button onClick={handleChangeUserName}>이름변경</button>
</>
);
}
export default App;
이제 버튼 클릭시 어떤 user
의 상태가 변경되어 리렌더링이 발생하게 됩니다. 어떤 컴포넌트들이 리렌더링이 발생할까요?
Context
와는 전혀 상관없는 정적인 컴포넌트인 UserPosts
컴포넌트에서도 리렌더링이 발생하고 있습니다.Context
를 구독여부에 상관없이 Provider
로 래핑된 컴포넌트는 상태가 변화될 때 모두 리렌더링이 발생함을 의미합니다.이는 1번째 문제와 연관이 있습니다.
이에 반해 전역상태관리 라이브러리는?
store
에 저장하고, 컴포넌트는 스토어의 특정부분을 구독할 수 있도록 설계되었습니다.https://github.com/facebook/react/issues/14110 에서 리액트 팀원이였던 Sebastian Markbåge가 작성한 댓글을 발췌 했습니다.
해당 댓글은 React의 Context API에 대한 사용 사례와 한계를 설명하고 있습니다. 해당 내용에 핵심부분을 번역하면,
Context API
는 변경사항이 드물게 발생하는 경우에 적합하다고 언급하고 있습니다.결론을 요약하자면 Context API는 특정 유형(값이 자주바뀌지 않는 정적인 유형)의 상태관리에서는 선택적으로 사용될 수 있지만, 복잡한 상태관리 요구사항을 가진 애플리케이션은 다른 상태관리 솔루션을 고려해야한다는 점을 설명하고 있습니다.