크롬 익스텐션에서 BrowserRouter가 작동하지 않는 이유
recall은 코딩 테스트 문제를 기록하고 복습하는 크롬 익스텐션이다.
내부에 여러 페이지가 있어 라우팅이 필요했다. 웹 앱에서 늘 쓰던
BrowserRouter를 붙였는데, 팝업을 열자마자 빈 화면이 나왔다.
1. 문제 상황
recall 익스텐션은 /, /problems, /live, /settings 등 여러 화면을 전환해야 한다.
// Router.tsx
export function AppRouter() {
return (
<Layout>
<Routes>
<Route path="/" element={<DashboardPage />} />
<Route path="/problems" element={<ProblemsPage />} />
<Route path="/problems/:id" element={<ProblemDetailPage />} />
<Route path="/live" element={<LiveCodingPage />} />
<Route path="/settings" element={<SettingsPage />} />
</Routes>
</Layout>
);
}
SPA에서 라우팅이 필요하면 React Router를 쓴다. 웹 앱이면 BrowserRouter를 감싸면 끝이다. 익스텐션도 React로 만든 SPA니까 당연히 똑같이 하면 되겠지, 라고 생각했다.
2. 내가 처음 한 생각
// 처음에 이렇게 붙였다
import { BrowserRouter } from "react-router";
export function App() {
return (
<BrowserRouter>
<AppRouter />
</BrowserRouter>
);
}
BrowserRouter는 HTML5 History API(pushState, replaceState)를 사용한다. 주소창 URL이 /about처럼 바뀌고, 뒤로가기도 된다.
웹 앱에서는 이게 자연스럽다. 그런데 익스텐션 팝업을 열면 주소창에는 이런 게 찍힌다.
chrome-extension://abcdefg1234567890/popup.html
pushState로 URL을 바꾸면 어떻게 될까? 브라우저는 이 URL을 실제로 서빙할 서버를 찾으려 한다. 익스텐션 팝업에는 서버가 없다. 결과는 빈 화면이었다.
3. 실제로 해 본 선택
React Router의 라우터는 크게 세 가지다.
| 라우터 | URL 동기화 방식 | 서버 필요 여부 |
|---|---|---|
| BrowserRouter | HTML5 History API | 필요 (SEO 및 경로 유지 목적) |
| HashRouter | #/path (URL 해시) | 불필요 |
| MemoryRouter | 메모리 내부 관리 | 불필요 |
BrowserRouter
window.location과 History API를 완전히 동기화한다. URL이 깔끔하지만(/about), 서버에서 해당 경로로 오는 요청을 처리할 수 없으면 404가 난다.
HashRouter URL 해시 부분(#)을 라우팅에 활용한다. https://example.com/#/about 형태다. 해시 뒤 부분은 서버로 전송되지 않아 서버 설정이 필요 없다. 익스텐션에서 쓸 수도 있지만, 팝업이 닫혔다가 다시 열리면 해시가 초기화된다.
MemoryRouter 라우팅 상태를 URL이 아닌 메모리에만 저장한다. 주소창에 아무것도 반영되지 않는다. 브라우저 환경이 아니거나 URL을 쓸 수 없는 환경(React Native, 테스트, 크롬 익스텐션)을 위해 만들어졌다.
익스텐션 팝업에서 URL은 아무 의미가 없다. 팝업은 닫히면 상태가 사라지고, URL을 바꿔도 저장되지 않는다. 그래서 MemoryRouter가 정답이다.
// App.tsx — 현재 구조
import { MemoryRouter } from "react-router";
export function App() {
return (
<QueryClientProvider client={queryClient}>
<MemoryRouter initialEntries={["/"]}>
<AppRouter />
</MemoryRouter>
</QueryClientProvider>
);
}
initialEntries={[”/”]} 옵션으로 팝업이 열릴 때 항상 대시보드부터 시작하게 했다.
여담: React Router v6의 새 철학
React Router v6.4부터 “Data Router”라는 개념이 생겼다. createBrowserRouter로 라우터를 만들면 loader, action,