React Router와 전역 상태 관리
React Router를 사용하여 SPA를 구현하고, useContext로 전역 상태를 관리할 수 있다.
1️⃣ React Router
- React는 SPA(Single Page Application) 방식으로 동작한다.
- SPA는 하나의 HTML 파일로 여러 화면을 구현하기 때문에, URL과 화면을 연결하는 라우팅 관리가 필요하다.
- React Router는 URL 경로에 따라 적절한 컴포넌트를 렌더링해주는 도구다.
- 페이지 새로고침 없이 화면을 전환할 수 있다.
설치
npm install react-router-dom
기본 설정
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<div>404 Not Found</div>} />
</Routes>
</BrowserRouter>
);
}
export default App;
| 요소 | 역할 |
|---|---|
<BrowserRouter> |
앱 전체에 라우팅 기능 제공 |
<Routes> |
라우트 목록을 감싸는 컨테이너 |
<Route> |
URL 경로와 컴포넌트를 연결 |
path="*" |
정의되지 않은 경로 처리 (404) |
2️⃣ 페이지 이동 방법
1. Link 컴포넌트
- 클릭 가능한 링크를 만들 때 사용
- HTML의
<a>태그와 비슷하지만, 페이지 새로고침이 발생하지 않는다.
import { Link } from 'react-router-dom';
function Nav() {
return (
<nav>
<Link to="/">홈</Link>
<span> | </span>
<Link to="/about">소개</Link>
</nav>
);
}
2. useNavigate Hook
- 조건부 이동이나 이벤트 기반 이동에 사용
- 로그인 성공 후 자동으로 페이지 이동하는 경우 등에 유용
import { useNavigate } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
const handleLogin = () => {
// 로그인 로직...
navigate('/home'); // 로그인 성공 후 홈으로 이동
};
return <button onClick={handleLogin}>로그인</button>;
}
|
비교 |
Link | useNavigate |
|---|---|---|
| 형태 | JSX 태그 | 함수 |
| 사용처 | 클릭 가능한 링크 | 조건부/이벤트 기반 이동 |
| 예시 | <Link to="/about">소개</Link> |
navigate('/about') |
3️⃣ 컴포넌트 구조화
- 화면을 작은 단위로 분리하여 재사용성과 유지보수성을 높인다.
- 공통 레이아웃(Header, Footer 등)을 별도 컴포넌트로 분리한다.
function Header() {
return <header>나의 React 앱</header>;
}
function Footer() {
return <footer>© 2025</footer>;
}
function AppShell({ children }) {
return (
<>
<Header />
{children}
<Footer />
</>
);
}
Router와 결합
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<AppShell>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</AppShell>
</BrowserRouter>
);
}
4️⃣ 폴더 구조 설계
기본 구조
src/
├── components/ # 공통 UI 컴포넌트
│ ├── Header.jsx
│ └── Footer.jsx
├── pages/ # 페이지 컴포넌트
│ ├── Home.jsx
│ └── About.jsx
├── contexts/ # 전역 상태 관리
│ └── ThemeContext.jsx
└── App.jsx # 라우팅 설정
대규모 프로젝트 구조
src/
├── features/ # 기능별 폴더
│ ├── auth/
│ │ ├── pages/
│ │ ├── components/
│ │ └── services/ # API 통신
└── shared/ # 공용 컴포넌트
5️⃣ useContext (전역 상태 관리)
- 컴포넌트 깊이가 깊어질수록 같은 데이터를 여러 단계에 걸쳐 전달해야 하는 Props Drilling 문제가 발생한다.
useContext를 사용하면 상위에서 제공한 값을 하위 어디서나 직접 꺼내 쓸 수 있다.- 여러 컴포넌트에서 공통적으로 사용해야 하는 데이터(테마, 로그인 정보 등)를 관리할 때 유용하다.
Context 생성 (contexts/ThemeContext.jsx)
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext(null);
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const value = { theme, setTheme };
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
return useContext(ThemeContext);
}
Context 사용 (components/Header.jsx)
import { Link } from 'react-router-dom';
import { useTheme } from '../contexts/ThemeContext';
function Header() {
const { theme, setTheme } = useTheme();
const nextTheme = theme === 'light' ? 'dark' : 'light';
return (
<header style={{
background: theme === 'light' ? '#f9f9f9' : '#1a1a1a',
color: theme === 'light' ? '#111' : '#f0f0f0',
padding: '12px 16px'
}}>
<nav>
<Link to="/">홈</Link> | <Link to="/about">소개</Link>
</nav>
<div>
<span>현재 테마: {theme}</span>
<button onClick={() => setTheme(nextTheme)}>테마 전환</button>
</div>
</header>
);
}
export default Header;
App에서 Provider로 감싸기
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { ThemeProvider } from './contexts/ThemeContext';
import Header from './components/Header';
import Home from './pages/Home';
import About from './pages/About';
function App() {
return (
<ThemeProvider>
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<div>404 Not Found</div>} />
</Routes>
</BrowserRouter>
</ThemeProvider>
);
}
export default App;
페이지 컴포넌트에서 Context 사용 (pages/Home.jsx)
import { useTheme } from '../contexts/ThemeContext';
function Home() {
const { theme } = useTheme();
const style = {
padding: 24,
minHeight: '100vh',
background: theme === 'light' ? '#ffffff' : '#111111',
color: theme === 'light' ? '#111111' : '#f5f5f5'
};
return (
<main style={style}>
<h1>홈</h1>
<p>React Router로 연결된 홈 화면입니다.</p>
<p>헤더의 테마 전환 버튼으로 전역 상태를 변경할 수 있습니다.</p>
</main>
);
}
export default Home;
useContext 사용 시 주의사항
- 너무 많은 값을 한 Context에 넣지 말 것: 하위 컴포넌트 전체가 불필요하게 리렌더링될 수 있다.
- 변경 빈도에 따라 Context 분리: 자주 변경되는 값과 그렇지 않은 값을 별도 Context로 관리한다.
- 적절한 사용처: 테마, 로그인 정보, 언어 설정 등 전역적으로 필요한 데이터에 적합하다.
- 성능 최적화:
useMemo를 활용하여 Context value 객체를 메모이제이션할 수 있다.
import { useMemo } from 'react';
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({ theme, setTheme }), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}'GDGoC > FrontEnd' 카테고리의 다른 글
| [Frontend] #06. React - 비동기 통신 (0) | 2026.05.06 |
|---|---|
| [Frontend] #04. React - State와 Hooks (0) | 2026.05.06 |
| [Frontend] #03. React 기초 (0) | 2026.05.06 |
| [Frontend] #02. JavaScript (0) | 2026.05.06 |
| [Frontend] #01. HTML / CSS (0) | 2026.05.06 |