
React 애플리케이션에서 Context API는 전역 상태를 관리하는데 매우 유용한 도구입니다. 그러나 Context의 잘못된 사용은 애플리케이션의 성능 문제를 초래할 수 있습니다. 특히, Modal과 같은 컴포넌트를 Context로 관리할 때는 Provider의 범위를 신중하게 설정해야 합니다. 그렇지 않으면 불필요한 리렌더링으로 인해 애플리케이션의 성능이 저하될 수 있습니다. 오늘은 이러한 문제를 피하고 최적화하는 방법에 대해 알아보겠습니다.
아래는 잘못된 방식으로 React Context를 사용하여 Modal을 관리하는 예시입니다. 이 예시는 Provider를 애플리케이션 전체에 감싸고 있어 불필요한 리렌더링이 발생할 수 있습니다.
import React, { createContext, useContext, useState } from 'react';
const ModalContext = createContext<{ isOpen: boolean; setIsOpen: (isOpen: boolean) => void } | undefined>(undefined);
export const ModalProvider: React.FC = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<ModalContext.Provider value={{ isOpen, setIsOpen }}>
{children}
</ModalContext.Provider>
);
};
export const useModal = () => {
const context = useContext(ModalContext);
if (!context) {
throw new Error('useModal must be used within a ModalProvider');
}
return context;
};
const App = () => {
return (
<ModalProvider>
<Header />
<MainContent />
<Footer />
</ModalProvider>
);
};
const Header = () => {
console.log('Header rendered');
return <header>Header</header>;
};
const MainContent = () => {
const { isOpen, setIsOpen } = useModal();
console.log('MainContent rendered');
return (
<main>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
{isOpen && <div>Modal Content</div>}
</main>
);
};
const Footer = () => {
console.log('Footer rendered');
return <footer>Footer</footer>;
};
위 코드에서 ModalProvider가 애플리케이션 전체를 감싸고 있기 때문에, setIsOpen 함수가 호출될 때마다 Header, MainContent, Footer와 같은 모든 자식 컴포넌트들이 리렌더링됩니다. ModalContext를 구독하지 않는 컴포넌트들까지도 불필요하게 다시 렌더링되어 성능 저하를 일으킵니다.
아래는 Context Provider의 범위를 최소화하여 불필요한 리렌더링을 방지하는 예시입니다. ModalProvider를 실제로 Modal을 사용하는 컴포넌트에만 감싸도록 변경하였습니다.
import React, { createContext, useContext, useState } from 'react';
const ModalContext = createContext<{ isOpen: boolean; setIsOpen: (isOpen: boolean) => void } | undefined>(undefined);
export const ModalProvider: React.FC = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
return (
<ModalContext.Provider value={{ isOpen, setIsOpen }}>
{children}
</ModalContext.Provider>
);
};
export const useModal = () => {
const context = useContext(ModalContext);
if (!context) {
throw new Error('useModal must be used within a ModalProvider');
}
return context;
};
const App = () => {
return (
<div>
<Header />
<ModalSection />
<Footer />
</div>
);
};
const Header = () => {
console.log('Header rendered');
return <header>Header</header>;
};
const ModalSection = () => {
return (
<ModalProvider>
<MainContent />
</ModalProvider>
);
};
const MainContent = () => {
const { isOpen, setIsOpen } = useModal();
console.log('MainContent rendered');
return (
<main>
<button onClick={() => setIsOpen(!isOpen)}>Toggle Modal</button>
{isOpen && <div>Modal Content</div>}
</main>
);
};
const Footer = () => {
console.log('Footer rendered');
return <footer>Footer</footer>;
};
위 코드에서는 ModalProvider의 범위를 ModalSection으로 좁혔습니다. 이제 setIsOpen이 호출되더라도 Header나 Footer는 리렌더링되지 않으며, ModalProvider 아래에 있는 MainContent만 리렌더링됩니다. 이는 애플리케이션의 성능을 크게 향상시킵니다.
React Context를 사용하여 Modal과 같은 전역 상태를 관리할 때, Provider의 범위를 최소한으로 좁히는 것이 중요합니다. 이렇게 하면 불필요한 리렌더링을 방지하고, 애플리케이션의 성능을 최적화할 수 있습니다. 항상 Context의 구독 범위를 신중하게 설계하여 리액트 앱이 느려지지 않도록 하는 것이 좋습니다.
React Context를 올바르게 사용하여 성능을 최적화해보세요! 💡