React Router v7 실습: BrowserRouter부터 Layout·Outlet·상세 경로까지

2026.09.14·수정 2026.09.15·약 12분·작성: 해비·블로그 소개
React Router v7 실습: BrowserRouter부터 Layout·Outlet·상세 경로까지 학습 표지

이 글은 프론트엔드 라이브러리 연결 과정의 일부입니다. React 컴포넌트·props·state·이벤트와 TypeScript 객체·배열 타입을 먼저 익혀 주세요. 원본 강의의 문장을 옮기는 대신 독립적인 예제와 확인 과제를 구성했습니다.

학습 목표: 주소와 화면을 연결하고 공통 레이아웃 안에서 목록·상세 화면을 전환합니다.

예상 학습 시간: 40분. 개인별 차이가 있으며 설치 시간은 제외합니다.

주소에 따라 바뀌는 영역부터 정합니다

라우팅은 현재 URL과 보여줄 화면을 연결하는 작업입니다. 공통 메뉴는 그대로 두고 수업 목록이나 상세 설명만 바뀌도록 구성합니다. React Router에는 여러 사용 모드가 있으며, 이 과정은 BrowserRouter·Routes·Route로 화면을 선언하는 모드를 사용합니다. loader나 action을 쓰는 Data 모드와 설정을 섞지 않습니다.

설치와 파일의 역할

npm install react-router@7.18.3

main.tsx의 BrowserRouter는 브라우저 History와 React 화면을 연결합니다. ZIP에서 /routing/*는 RouterPage의 하위 경로도 받을 수 있게 한 경로입니다. RouterPage 안의 Routes가 lesson/:slug를 다시 판단합니다. 처음에는 한 파일의 Routes만 구성해도 충분하며, 예제에서는 학습 메뉴를 분리하기 위해 두 층을 사용했습니다.

구성 역할 빠뜨렸을 때
BrowserRouter 브라우저 주소와 라우팅 문맥 제공 Link와 라우팅 훅을 문맥 밖에서 사용할 수 없음
Routes·Route 주소와 element 연결 일치하는 화면을 찾을 수 없음
Layout의 Outlet 자식 Route 화면 자리 메뉴와 제목만 보이고 자식은 사라짐
Link 앱 내부 화면 이동 일반 a는 문서 전체 요청이 발생할 수 있음

레이아웃·index·동적 경로

path가 없는 부모 Route는 URL 조각을 추가하지 않고 레이아웃만 공유합니다. index Route는 부모 주소 그 자체에 표시하는 기본 화면입니다. lesson/:slug에서 콜론 뒤 이름은 변수가 되고, useParams로 문자열 값을 읽습니다. URL에 값이 있다는 사실만으로 서버 데이터가 실제 존재한다는 뜻은 아닙니다. 데이터 조회 후 별도의 없음 상태가 필요합니다.

ZIP의 /routing/lesson/layout에서는 수업 안내라는 공통 제목과 선택한 수업: layout이 함께 보입니다. Outlet을 잠시 제거하면 제목만 남습니다. 이 차이를 직접 관찰하면 레이아웃과 페이지의 역할이 분명해집니다.

뒤로 가기와 새로고침

Link로 이동한 뒤 브라우저 뒤로 가기를 누르면 이전 주소와 화면으로 돌아가야 합니다. 배포 서버에서 상세 URL 새로고침이 404라면 React의 Route 설정과 별개로 SPA 진입 HTML을 돌려주는 호스팅 설정을 확인합니다. 개발 서버에서 성공했다고 운영 서버의 직접 진입까지 검증된 것은 아닙니다.

직접 해보기와 확인 질문

lesson/state 상세 링크를 하나 더 추가하세요. Outlet을 제거했을 때와 복구했을 때, /routing/unknown을 열었을 때를 비교하세요.

풀이 기준 보기

state라는 문자열이 상세 화면에 보이고 공통 제목은 유지되어야 합니다. 없는 주소에는 안내문이 나와야 합니다. Outlet은 링크가 아니라 자식 화면이 삽입되는 자리입니다.

공통 실습 실행

아래 공통 ZIP을 풀고 Node.js 24 환경에서 실행합니다. package-lock.json에 실습 버전을 고정했습니다. 본문의 경로와 ZIP의 경로는 같습니다.

npm ci
npm run dev

터미널에 표시된 주소를 열고 이 글에 해당하는 메뉴를 선택하세요. 서버 Todo만 별도 터미널의 npm run server가 필요합니다. 본문 코드에서 생략한 공통 설정과 UI 파일까지 ZIP에 포함되어 있습니다.

프론트엔드 라이브러리 공통 실습 ZIP

src/pages/router-page.tsx

공통 실습 ZIP 안의 전체 파일입니다. 이 파일 하나만으로 독립 실행되지는 않으며 import하는 파일은 ZIP에 함께 들어 있습니다.

import { Link, Outlet, Route, Routes, useParams } from "react-router";

function Layout() {
  return (
    <section>
      <h2>수업 안내</h2>
      <nav>
        <Link to="/routing">목록</Link>
        <Link to="/routing/lesson/layout">레이아웃</Link>
      </nav>
      <Outlet />
    </section>
  );
}
function Detail() {
  const { slug } = useParams();
  return <p>선택한 수업: {slug}</p>;
}
export default function RouterPage() {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route index element={<p>수업을 선택하세요.</p>} />
        <Route path="lesson/:slug" element={<Detail />} />
        <Route path="*" element={<p>수업을 찾을 수 없습니다.</p>} />
      </Route>
    </Routes>
  );
}

src/main.tsx

공통 실습 ZIP 안의 전체 파일입니다. 이 파일 하나만으로 독립 실행되지는 않으며 import하는 파일은 ZIP에 함께 들어 있습니다.

import { createRoot } from "react-dom/client";
import { BrowserRouter, Link, Route, Routes } from "react-router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { Toaster } from "@/components/ui/sonner";
import RouterPage from "./pages/router-page";
import FormPage from "./pages/form-page";
import OverlayPage from "./pages/overlay-page";
import MiddlewarePage from "./pages/middleware-page";
import LocalPage from "./pages/local-page";
import RemotePage from "./pages/remote-page";
import "./index.css";

const client = new QueryClient({
  defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
});
createRoot(document.getElementById("root")!).render(
  <QueryClientProvider client={client}>
    <BrowserRouter>
      <main>
        <h1>라이브러리 단계별 실습</h1>
        <nav>
          <Link to="/routing">라우팅</Link>
          <Link to="/form">입력·알림</Link>
          <Link to="/overlay">복합 UI</Link>
          <Link to="/middleware">미들웨어</Link>
          <Link to="/local">로컬 Todo</Link>
          <Link to="/remote">서버 Todo</Link>
        </nav>
        <Routes>
          <Route path="/" element={<p>위 메뉴에서 실습을 선택하세요.</p>} />
          <Route path="/routing/*" element={<RouterPage />} />
          <Route path="/form" element={<FormPage />} />
          <Route path="/overlay" element={<OverlayPage />} />
          <Route path="/middleware" element={<MiddlewarePage />} />
          <Route path="/local" element={<LocalPage />} />
          <Route path="/remote" element={<RemotePage />} />
          <Route path="*" element={<p>없는 주소입니다.</p>} />
        </Routes>
      </main>
      <Toaster />
    </BrowserRouter>
    <ReactQueryDevtools initialIsOpen={false} />
  </QueryClientProvider>,
);

UI 파일 범위: ZIP의 src/components/ui는 shadcn CLI 생성물이 아닙니다. 레지스트리 접속 실패로 Radix·Sonner·Embla를 사용하는 축약 학습용 구현을 작성했습니다. 공식 설치 절차는 별도 프로젝트에서 비교하세요. 공식 컴포넌트 전체 기능과 동일하다고 보장하지 않습니다.

검증 범위

Node.js 24.19.0, React 19.3.0, React Router 7.18.3, Zustand 5.0.15, TanStack Query 5.102.8, Tailwind CSS 4.3.3에서 타입 검사와 Vite 빌드를 확인했습니다. 자동 테스트는 jsdom 기반입니다. 실제 브라우저의 포커스·레이아웃·화면낭독기와 모든 네트워크 경쟁 상황은 별도 확인 대상입니다.

npm test
npm run build

공식 문서

2026-09-14 확인. 공식 최신 문서의 버전과 실습 고정 버전이 다를 수 있으므로 설치 버전은 ZIP을 기준으로 비교합니다.

이 글이 도움이 되었나요?

조회 중

React 학습 순서

필수 19개 · 전체 23개

읽음 기록 관리

전체 과정 목차 (23개)
  1. 필수 길잡이 · React 학습 순서: 컴포넌트·props·state부터 상태관리까지
  2. 필수 학습 · React Vite 사용법: Vite 8 프로젝트 생성·실행·빌드
  3. 필수 학습 · React 컴포넌트 개념 정리: UI 재사용 구조 잡기
  4. 필수 학습 · React JSX 문법 사용법: 조건부 렌더링과 리스트 처리
  5. 필수 학습 · React props 사용법: 부모에서 자식으로 데이터 전달하는 구조
  6. 필수 학습 · React useState 사용법: state와 객체 배열 업데이트 기준
  7. 선택 참고 · React state 업데이트 안됨 문제 해결
  8. 필수 선수 · React 리스트 key 경고 해결 기준: index key를 피해야 하는 이유
  9. 필수 학습 · React 제어 컴포넌트 폼과 상태 끌어올리기 첫 실습 가이드
  10. 필수 학습 · React useRef 실습: 입력 포커스와 state의 역할 나누기
  11. 필수 선수 · React useEffect 두 번 실행되는 이유: StrictMode·API 중복 해결
  12. 선택 참고 · React Maximum update depth exceeded 오류 해결: 무한 렌더링 원인 찾기
  13. 선택 참고 · React Cannot update 오류 해결: 렌더링 중 setState 원인
  14. 필수 학습 · React useReducer와 Context 실습: 작업 목록 상태를 여러 컴포넌트에서 공유하기
  15. 필수 학습 · React 컴포넌트 props 타입 지정하기: 부모와 자식 사이의 값 구조 잡기
  16. 선택 참고 · React Hook Form 에러 메시지 표시 문제 해결: validation이 안 보일 때 체크리스트
  17. 필수 길잡이 · React 라이브러리 조합 가이드: Zustand·TanStack Query·shadcn/ui 선택 기준
  18. 필수 학습 · Next.js 커스텀 훅 설계: 프론트엔드 상태 관리 구조 잡기
  19. 필수 학습 · React Compiler 기준: useMemo useCallback 언제 줄일까
  20. 필수 학습 · React·TypeScript 검색 필터 만들기: 상태와 결과 목록 연결
  21. 필수 학습 · React Router v7 실습: BrowserRouter부터 Layout·Outlet·상세 경로까지 현재 글
  22. 필수 학습 · shadcn/ui 시작 실습: Vite 설정·components.json·입력 폼·Sonner
  23. 필수 학습 · shadcn/ui 복합 컴포넌트 실습: Dialog·Popover·Carousel과 접근성

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기