[아이티센 부트캠프] React 4

이언덕·2026년 5월 22일

아이티센 부트캠프

목록 보기
106/115
post-thumbnail

React Router가 필요한 이유와 설치 흐름

React Router는 URL에 따라 어떤 컴포넌트를 보여줄지 결정하는 클라이언트 라우터다.
클라이언트 라우터는 서버가 아니라 브라우저 안에서 화면 전환을 처리하는 도구를 의미한다.


기존 Spring MVC에서는 사용자가 어떤 주소로 이동할 때마다 서버가 새 HTML을 만들어 응답하는 흐름이 중심이었다.
반면 React SPA에서는 처음 받은 앱 안에서 URL만 바뀌고, 현재 주소에 맞는 컴포넌트만 교체된다.
즉, React Router는 React 앱에서 주소와 컴포넌트를 연결해 주는 라우팅 도구다.


1-1. Spring MVC와 React SPA 화면 전환의 차이

Spring MVC는 서버가 화면을 만들어 준다

Spring MVC에서는 브라우저가 서버에 페이지를 요청한다.
예를 들어 사용자가 /posts/1 주소로 이동하면 브라우저는 서버에 해당 주소의 페이지를 요청한다.


서버에서는 Controller가 요청을 받는다.
Controller는 어떤 View를 보여줄지 결정하고, 화면에 필요한 데이터를 Model에 담는다.
그 다음 서버는 완성된 HTML 페이지를 브라우저에 다시 내려준다.


이 흐름에서는 화면을 만드는 중심이 서버에 있다.
브라우저는 서버가 보내준 HTML을 받아서 화면에 표시한다.


정리하면 Spring MVC 화면 전환 흐름은 아래와 같다.

  • 브라우저가 서버에 페이지를 요청한다.
  • 서버의 Controller가 요청을 처리한다.
  • Controller가 View 이름과 Model 데이터를 결정한다.
  • 서버가 완성된 HTML을 만든다.
  • 브라우저는 새 HTML을 받아 화면 전체를 다시 표시한다.

Spring MVC는 서버가 화면을 만들고, 브라우저는 그 결과를 받아 보여주는 방식이다.


React SPA는 브라우저 안에서 컴포넌트를 바꾼다

React SPA에서는 처음에 하나의 앱을 브라우저가 내려받는다.
여기서 SPA는 Single Page Application의 줄임말이다.
초보자 기준에서는 “처음에 앱 하나를 받고, 이후에는 화면 전체를 다시 받지 않고 필요한 부분만 바꾸는 구조”로 이해하면 된다.


React SPA에서 사용자가 다른 주소로 이동하면 브라우저 주소창의 URL은 바뀐다.
하지만 서버에서 새 HTML 페이지를 매번 다시 받아오지는 않는다.
대신 브라우저 안에서 React Router가 현재 URL에 맞는 컴포넌트를 선택한다.


예를 들어 /about 주소로 이동하면 React Router는 /about에 연결된 컴포넌트를 찾는다.
그리고 그 컴포넌트를 화면에 렌더링한다.
여기서 렌더링은 컴포넌트를 화면에 보이도록 계산하고 반영하는 과정을 의미한다.


정리하면 React SPA 화면 전환 흐름은 아래와 같다.

  • 처음에 브라우저가 React 앱을 내려받는다.
  • 사용자가 링크를 눌러 URL을 바꾼다.
  • 서버에서 새 HTML을 다시 받지 않는다.
  • React Router가 현재 URL과 맞는 컴포넌트를 찾는다.
  • 찾은 컴포넌트를 브라우저 안에서 렌더링한다.

React SPA는 서버가 새 화면을 만들어 주는 방식이 아니라, 브라우저 안에서 현재 주소에 맞는 컴포넌트를 선택하는 방식이다.

Spring MVC는 서버의 Controller가 요청을 처리하고 완성된 HTML을 다시 내려준다.
React SPA는 브라우저 안에서 URL이 바뀌면 React Router가 현재 주소에 맞는 컴포넌트를 선택하고, 필요한 데이터는 REST API를 통해 JSON으로 받아 화면에 그린다.


두 방식의 차이를 표로 정리하기

Spring MVC와 React SPA는 둘 다 주소에 따라 화면이 바뀐다는 점에서는 비슷해 보인다.
하지만 화면을 만드는 위치가 다르다.


Spring MVC는 서버에서 HTML을 만든다.
그래서 서버의 Controller와 View 관계가 중요하다.
반면 React SPA는 브라우저에서 컴포넌트를 렌더링한다.
그래서 Route와 페이지 컴포넌트의 연결이 중요하다.


데이터 형식도 다르다.
Spring MVC에서는 Model 객체와 HTML이 함께 화면 구성에 사용된다.
React SPA에서는 서버에서 받은 JSON 데이터를 React state에 저장하고, 그 state를 기준으로 화면을 그린다.


보안 관점도 구분해야 한다.
Spring Security처럼 서버에서 권한을 검사하는 흐름은 실제 보안 처리에 해당한다.
반면 React Router에서 특정 라우트 접근을 막는 것은 브라우저 쪽 화면 처리이므로 사용자 경험을 위한 장치에 가깝다.


React 라우트 보호는 UX용이고, 실제 보안 검사는 서버에서 반드시 처리해야 한다.

React Router는 서버에서 HTML을 만드는 도구가 아니라, 브라우저에서 URL에 맞는 컴포넌트를 선택하는 클라이언트 라우터다.
화면 생성 위치는 서버가 아니라 브라우저이고, 서버 데이터는 보통 REST API의 JSON 형태로 받아 React state에 저장한 뒤 화면에 반영한다.


1-2. SPA 라우팅의 핵심

SPA는 페이지 전체를 다시 받지 않는다

SPA는 Single Page Application의 줄임말이다.
한글로 풀면 단일 페이지 애플리케이션이라는 뜻이다.
말 그대로 처음에는 하나의 앱을 받고, 그 안에서 필요한 화면만 바꿔 보여주는 구조다.


전통적인 멀티페이지 방식에서는 사용자가 /home으로 이동하면 서버에 새 페이지를 요청한다.
서버는 /home에 해당하는 새 HTML을 만들어 응답한다.
브라우저는 이전 화면을 버리고 새 HTML을 다시 표시한다.
이 과정에서 전체 페이지 새로고침이 발생한다.


React Router를 사용하는 SPA에서는 흐름이 다르다.
사용자가 /home으로 이동해도 서버에서 새 HTML을 매번 다시 받지 않는다.
브라우저 주소창의 URL이 바뀌고, JavaScript가 현재 URL에 맞는 컴포넌트를 교체한다.


여기서 JavaScript는 브라우저 안에서 실행되는 코드다.
React 컴포넌트도 결국 브라우저 안에서 JavaScript로 실행된다.
따라서 React Router의 화면 전환은 브라우저 안에서 일어나는 컴포넌트 교체로 볼 수 있다.


URL은 바뀌지만 앱은 새로고침되지 않는다

React Router를 사용하면 사용자는 주소가 바뀌는 것을 볼 수 있다.
예를 들어 /about, /contact, /students/1 같은 주소로 이동할 수 있다.


하지만 이 이동은 일반적인 새 페이지 요청과 다르다.
브라우저가 서버에 새 HTML을 요청해서 전체 화면을 다시 그리는 것이 아니다.
현재 실행 중인 React 앱 안에서 어떤 컴포넌트를 보여줄지만 바뀐다.


이 차이는 초보자가 반드시 잡아야 한다.
주소가 바뀌기 때문에 서버 페이지 이동처럼 보일 수 있다.
하지만 React SPA에서는 주소 변화가 곧 서버의 새 HTML 응답을 의미하지 않는다.


React Router의 핵심은 URL 변화에 맞춰 브라우저 안에서 컴포넌트만 바꾸는 것이다.

React Router SPA에서는 브라우저가 새 페이지를 다시 로드하지 않는다.
전통적인 멀티페이지 방식은 /home 이동 시 서버에서 새 HTML을 받지만, React Router SPA는 /home으로 주소만 바꾸고 JavaScript가 해당 주소에 맞는 컴포넌트를 교체한다.


1-3. React Router 설치 흐름

React 자체에는 라우팅 기능이 기본으로 들어 있지 않다

React는 화면을 만들기 위한 UI 라이브러리다.
UI는 사용자가 보는 화면 요소를 의미한다.
버튼, 입력창, 목록, 카드 같은 화면 조각을 컴포넌트로 만들 수 있게 해준다.


하지만 React 자체가 주소에 따라 화면을 자동으로 바꿔 주지는 않는다.
/, /about, /contact 같은 주소와 컴포넌트를 연결하려면 라우팅 도구가 필요하다.
그 역할을 하는 대표 도구가 React Router다.


이 예제에서는 react-router 패키지를 설치해서 사용한다.
실제 코드에서도 Link, Routes, Route, useParams, useSearchParams를 react-router에서 가져온다.


현재 예제 기준은 react-router-dom이 아니라 react-router에서 필요한 기능을 가져오는 방식이다.


Vite 프로젝트 생성과 React Router 설치

React Router 예제를 만들려면 먼저 Vite React 프로젝트를 생성한다.
Vite는 빠르게 React 개발 환경을 만들어 주는 도구다.
초보자 기준에서는 React 프로젝트를 실행할 수 있는 기본 틀을 만들어 주는 도구로 이해하면 된다.


프로젝트 생성과 실행 흐름은 아래와 같다.

// ReactRouterInstallCommand.sh
npm create vite@latest my-react-route-app -- --template react
cd my-react-route-app
npm install
npm install react-router
npm run dev

npm create vite@latest my-react-route-app -- --template react는 새 Vite React 프로젝트를 만든다.
프로젝트 이름은 my-react-route-app이다.


cd my-react-route-app은 방금 만든 프로젝트 폴더 안으로 이동하는 명령이다.
터미널에서 프로젝트 안으로 들어가야 그 프로젝트에 필요한 패키지를 설치할 수 있다.


npm install은 프로젝트 실행에 필요한 기본 패키지들을 설치한다.
package.json에 적힌 의존성 패키지들이 node_modules 폴더에 설치된다.


npm install react-router는 라우팅 기능을 사용하기 위해 react-router 패키지를 설치하는 명령이다.
이 패키지를 설치해야 BrowserRouter, Link, Routes, Route, useParams, useSearchParams 같은 기능을 사용할 수 있다.


npm run dev는 개발 서버를 실행하는 명령이다.
개발 서버가 실행되면 브라우저에서 로컬 주소로 React 앱을 확인할 수 있다.


설치 후 코드에서 가져오는 방식

설치가 끝나면 필요한 라우터 기능을 코드에서 가져와 사용할 수 있다.
가져온다는 것은 다른 파일이나 패키지에 있는 기능을 현재 파일에서 쓸 수 있게 import한다는 뜻이다.


기본 라우팅 예제에서는 아래 기능을 사용한다.

// ReactRouterBasicImportExample.jsx
import { Link, Routes, Route } from 'react-router'; // 링크 이동과 라우트 연결 기능을 가져온다.

Link는 새로고침 없이 주소를 바꾸는 컴포넌트다.
Routes는 여러 Route 중 현재 주소와 맞는 것을 찾는 영역이다.
Route는 특정 주소와 특정 컴포넌트를 연결한다.


학생 상세 화면 예제에서는 아래 기능까지 함께 사용한다.

// ReactRouterParamsImportExample.jsx
import {
  Routes,
  Route,
  Link,
  useParams,
  useSearchParams,
} from 'react-router'; // URL 값 읽기와 query string 처리를 위해 가져온다.

useParams()는 /students/1처럼 경로 안에 들어간 값을 읽는 Hook이다.
useSearchParams()는 ?mode=detail처럼 URL의 물음표 뒤에 붙은 값을 읽거나 바꾸는 Hook이다.


여기서 Hook은 함수형 컴포넌트에서 React 기능을 사용할 수 있게 해주는 함수다.
useParams()와 useSearchParams()도 컴포넌트 안에서 라우터 정보를 읽기 위해 사용하는 Hook이다.


1-4. 라우터 보안에서 주의할 점

React Router의 라우트 보호는 화면 처리다

React Router를 사용하면 특정 주소에 접근하지 못하게 막는 것처럼 처리할 수 있다.
예를 들어 로그인하지 않은 사용자가 /mypage에 들어가려고 하면 로그인 페이지로 보내는 방식이 가능하다.


하지만 이 처리는 브라우저에서 실행되는 코드다.
브라우저 코드는 사용자가 확인할 수 있고, 상황에 따라 조작될 수도 있다.
그래서 이것만으로 실제 보안이 완성됐다고 보면 안 된다.


React Router의 라우트 보호는 사용자 경험을 위한 처리에 가깝다.
로그인하지 않은 사용자가 잘못된 화면을 보지 않도록 안내하는 역할을 한다.
하지만 실제 데이터 접근 권한은 서버에서 다시 검사해야 한다.


예를 들어 관리자 화면 버튼을 React에서 숨겼다고 해서 관리 기능이 안전해지는 것은 아니다.
사용자가 직접 API 주소를 호출할 수도 있기 때문이다.
따라서 서버는 요청을 받을 때마다 로그인 여부와 권한을 확인해야 한다.


실제 보안 검사는 서버에서 해야 한다

실제 보안은 서버에서 처리해야 한다.
서버는 클라이언트에서 온 요청을 믿지 않고, 요청마다 인증과 권한을 확인해야 한다.


인증은 사용자가 누구인지 확인하는 과정이다.
예를 들어 로그인한 사용자인지 확인하는 것이 인증이다.
권한은 그 사용자가 해당 기능을 사용할 수 있는지 확인하는 과정이다.
예를 들어 일반 사용자가 관리자 기능을 사용할 수 없게 막는 것이 권한 검사다.


React Router는 화면 이동을 부드럽게 만들고, 잘못된 접근을 사용자에게 안내하는 데 도움을 준다.
하지만 서버 데이터 보호는 서버의 인증과 권한 검사로 처리해야 한다.


React 라우트 보호는 UX용이고, 실제 보안 검사는 서버에서 반드시 처리해야 한다.


정리하면 React Router는 화면 전환 도구이고, 서버 보안을 대신하는 도구가 아니다.
라우터는 브라우저 안에서 현재 주소와 컴포넌트를 연결한다.
실제 데이터 접근 가능 여부는 서버에서 최종적으로 판단해야 한다.


핵심 정리

React Router는 URL에 따라 어떤 컴포넌트를 보여줄지 결정하는 클라이언트 라우터다.
서버에서 새 HTML을 매번 다시 받는 방식이 아니라, 브라우저 안에서 현재 주소에 맞는 컴포넌트를 선택해 화면을 바꾼다.


Spring MVC는 서버의 Controller가 요청을 처리하고, 서버가 완성된 HTML을 만들어 응답한다.
반면 React SPA는 처음 받은 앱 안에서 URL만 바꾸고, React Router가 현재 주소와 맞는 컴포넌트를 렌더링한다.


React 자체에는 주소와 컴포넌트를 연결하는 라우팅 기능이 기본으로 들어 있지 않다.
그래서 예제 프로젝트에서는 npm install react-router로 react-router를 설치하고, 코드에서 필요한 라우터 기능을 import해서 사용한다.


마지막으로 React Router의 라우트 보호는 브라우저에서 실행되는 화면 처리다.
실제 인증과 권한 검사는 반드시 서버에서 처리해야 하며, React Router는 보안을 대신하는 도구가 아니라 화면 전환을 담당하는 도구다.




React Router 핵심 요소와 라우팅 환경 만들기

React Router를 사용하려면 먼저 핵심 요소의 역할을 알아야 한다.
라우팅은 단순히 링크를 클릭하는 기능이 아니라, 현재 URL을 기준으로 어떤 컴포넌트를 화면에 보여줄지 결정하는 흐름이다.


이 흐름을 만들기 위해 BrowserRouter, Routes, Route, Link 같은 요소를 함께 사용한다.
각 요소는 역할이 다르기 때문에 하나씩 구분해서 이해해야 한다.
즉, React Router는 URL을 보고 알맞은 컴포넌트를 선택하기 위해 여러 요소가 함께 동작하는 구조다.


2-1. React Router 핵심 요소

라우터 요소는 각각 맡은 역할이 다르다

React Router를 처음 보면 비슷한 이름의 요소가 여러 개 나와서 헷갈릴 수 있다.
하지만 각 요소의 역할을 나누어 보면 흐름이 단순해진다.


BrowserRouter는 라우팅 기능을 사용할 수 있는 바깥 환경을 만든다.
Routes는 여러 Route 중 현재 주소와 맞는 것을 찾는다.
Route는 특정 주소와 특정 컴포넌트를 연결한다.
Link는 새로고침 없이 주소를 바꾼다.


초보자 기준에서는 아래처럼 생각하면 된다.

  • BrowserRouter: 라우터 기능을 사용할 수 있는 큰 영역
  • Link: 사용자가 누르는 이동 링크
  • Routes: 현재 주소와 맞는 길을 찾는 영역
  • Route: 주소와 화면 컴포넌트를 연결한 설정

이 네 가지가 함께 있어야 기본적인 화면 이동이 만들어진다.


핵심 요소 표로 정리하기

React Router의 핵심 요소는 아래처럼 정리할 수 있다.

요소역할쉽게 이해하기
BrowserRouter브라우저 주소창과 React Router를 연결한다.앱 전체를 라우터 영역 안에 넣는 바깥 껍데기다.
Routes여러 Route 중 현재 URL과 맞는 것을 선택한다.현재 주소에 맞는 길을 찾는 목록이다.
Routepath와 element를 연결한다.이 주소에서는 이 컴포넌트를 보여준다는 설정이다.
Link새로고침 없이 URL을 이동한다.a 태그처럼 보이지만 페이지 전체를 다시 받지 않는다.
NavLink이동 링크에 현재 선택 상태를 적용할 수 있다.메뉴에서 현재 페이지를 표시할 때 쓴다.
useNavigate()코드에서 페이지 이동을 처리한다.버튼 클릭 후 특정 주소로 보내고 싶을 때 쓴다.
useParams()경로 안의 동적 값을 읽는다./students/1의 1 같은 값을 꺼낸다.
useSearchParams()쿼리 문자열을 읽거나 바꾼다.?mode=detail의 mode 값을 꺼내거나 바꾼다.

이 표에서 먼저 확실히 잡아야 할 것은 BrowserRouter, Routes, Route, Link다.
나머지 useParams()와 useSearchParams()는 뒤에서 학생 상세 화면 예제로 자세히 다룬다.

React Router의 핵심 요소는 브라우저 주소창과 컴포넌트를 연결하고, 새로고침 없이 화면을 바꾸기 위해 사용된다.
BrowserRouter는 라우팅 환경을 만들고, Routes는 현재 주소와 맞는 Route를 찾고, Route는 주소와 컴포넌트를 연결한다.
Link는 사용자가 새로고침 없이 다른 주소로 이동할 수 있게 한다.


2-2. BrowserRouter로 App 감싸기

BrowserRouter는 라우팅 환경을 만든다

BrowserRouter는 React 앱 전체에 라우팅 기능을 제공하는 컴포넌트다.
컴포넌트는 화면을 구성하는 조각을 의미한다.
여기서 BrowserRouter도 화면에 직접 큰 내용을 보여주는 컴포넌트라기보다, 라우터 기능을 사용할 수 있는 환경을 만들어 주는 컴포넌트로 보면 된다.


Link, Routes, Route 같은 라우터 요소는 아무 곳에서나 동작하는 것이 아니다.
라우팅 환경 안에 있어야 현재 주소를 읽고, 주소를 바꾸고, 주소에 맞는 컴포넌트를 찾을 수 있다.
그 환경을 제공하는 요소가 바로 BrowserRouter다.


그래서 보통 main.jsx에서 <App />을 BrowserRouter로 감싼다.
main.jsx는 React 앱이 실제 브라우저 화면에 붙기 시작하는 진입 파일이다.


main.jsx에서 BrowserRouter 적용하기

BrowserRouter는 보통 앱 전체를 감싸는 위치에 둔다.
그래야 App 안쪽의 모든 컴포넌트에서 라우터 기능을 사용할 수 있다.

// main.jsx
import { StrictMode } from 'react'; // 개발 중 검사 기능을 사용한다.
import { createRoot } from 'react-dom/client'; // React 앱 시작점을 만든다.
import { BrowserRouter } from 'react-router'; // 라우팅 환경을 만드는 컴포넌트다.
import App from './App.jsx'; // 최상위 App 컴포넌트다.

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>
);

BrowserRouter가 <App />을 감싸고 있다.
이 구조 덕분에 App 안에서 Link, Routes, Route를 사용할 수 있다.


만약 BrowserRouter로 감싸지 않은 상태에서 Link나 Routes를 사용하면 라우터 환경을 찾지 못할 수 있다.
초보자 기준에서는 라우터 기능을 쓰려면 먼저 앱 전체를 라우터 영역 안에 넣어야 한다고 이해하면 된다.

BrowserRouter는 React 앱 전체에 라우팅 기능을 제공하기 위해 main.jsx에서 <App />을 감싼다.
이렇게 감싸야 App과 그 안쪽 컴포넌트들이 현재 주소를 읽고, 주소를 변경하고, 주소에 맞는 화면을 선택할 수 있다.


2-3. BrowserRouter가 필요한 이유

Link는 주소를 바꾸기 위해 라우터 환경이 필요하다

Link는 사용자가 클릭할 수 있는 이동 링크다.
겉으로는 HTML의 a 태그와 비슷해 보인다.
하지만 중요한 차이가 있다.


일반적인 a 태그는 브라우저가 서버에 새 페이지를 요청하게 만들 수 있다.
그러면 페이지 전체가 다시 로드될 수 있다.
반면 Link는 React Router 안에서 주소만 바꾸고, 페이지 전체를 새로고침하지 않는다.


이 동작을 하려면 Link가 현재 라우터 환경을 알고 있어야 한다.
그래야 서버에 새 HTML을 요청하지 않고, 브라우저 안에서 주소만 바꾸는 흐름을 만들 수 있다.


정리하면 Link는 아래 역할을 한다.

  • 사용자가 클릭할 수 있는 이동 링크를 만든다.
  • 브라우저 주소창의 URL을 바꾼다.
  • 페이지 전체 새로고침을 발생시키지 않는다.
  • 바뀐 주소에 맞는 컴포넌트를 Routes가 찾도록 만든다.

Link는 주소를 바꾸는 출발점이다.


Routes와 Route는 현재 주소를 기준으로 화면을 고른다

Routes는 여러 Route 중 현재 주소와 맞는 것을 찾는 영역이다.
Route는 주소와 컴포넌트를 연결하는 설정이다.


예를 들어 아래처럼 라우트를 만들 수 있다.

// RouteBasicExample.jsx
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/contact" element={<Contact />} />
</Routes>

이 코드는 세 개의 주소를 세 개의 컴포넌트와 연결한다.
/ 주소에서는 Home 컴포넌트를 보여준다.
/about 주소에서는 About 컴포넌트를 보여준다.
/contact 주소에서는 Contact 컴포넌트를 보여준다.


여기서 path는 주소 경로다.
경로는 브라우저 주소에서 도메인 뒤에 붙는 부분을 의미한다.
예를 들어 http://localhost:5173/about에서 /about이 경로다.


element는 해당 주소에서 화면에 보여줄 컴포넌트다.
element={<About />}라고 쓰면 /about 주소에서 About 컴포넌트를 보여준다는 뜻이다.


Routes는 현재 주소와 맞는 Route를 찾고, Route는 그 주소에서 보여줄 컴포넌트를 지정한다.


2-4. 라우팅 환경 전체 흐름

기본 흐름은 BrowserRouter에서 시작된다

React Router의 기본 화면 전환은 BrowserRouter에서 시작된다.
BrowserRouter가 앱 전체를 감싸면 그 안에서 라우터 기능을 사용할 수 있다.


그 다음 사용자가 Link를 클릭한다.
Link는 브라우저 주소창의 URL을 바꾼다.
주소가 바뀌면 Routes가 현재 주소와 맞는 Route를 찾는다.
마지막으로 선택된 Route의 element에 적힌 컴포넌트가 화면에 렌더링된다.


전체 흐름은 아래와 같다.

  • BrowserRouter가 앱 전체에 라우팅 환경을 만든다.
  • 사용자가 Link를 클릭한다.
  • Link가 브라우저 주소창의 URL을 바꾼다.
  • Routes가 현재 URL과 맞는 Route를 찾는다.
  • 선택된 Route의 element 컴포넌트가 화면에 렌더링된다.

이 흐름을 알면 다음 세트에서 App.jsx 코드를 읽기 쉬워진다.


각 요소를 한 문장으로 다시 정리하기

BrowserRouter, Link, Routes, Route는 하나씩 따로 외우는 것보다 흐름으로 연결해서 이해해야 한다.


한 문장으로 정리하면 아래와 같다.

  • BrowserRouter는 라우팅 기능을 사용할 수 있는 환경을 만든다.
  • Link는 사용자가 클릭했을 때 주소를 바꾼다.
  • Routes는 현재 주소와 맞는 Route를 찾는다.
  • Route는 주소에 맞는 컴포넌트를 화면에 보여준다.

BrowserRouter는 라우터 기능을 쓰기 위한 바깥 환경이고, Routes, Route, Link는 그 안에서 실제 화면 전환을 만드는 요소다.


핵심 정리

React Router는 여러 요소가 함께 동작하면서 화면 전환을 만든다.
BrowserRouter는 라우팅 환경을 제공하고, Link는 새로고침 없이 주소를 바꾼다.


Routes는 현재 주소와 맞는 Route를 찾는다.
Route는 path와 element를 연결해서 특정 주소에서 어떤 컴포넌트를 보여줄지 결정한다.


BrowserRouter로 앱을 감싸야 그 안에서 Link, Routes, Route 같은 라우터 기능을 사용할 수 있다.
따라서 보통 main.jsx에서 <App />을 <BrowserRouter>로 감싼다.


기본 라우팅 흐름은 BrowserRouter가 환경을 만들고, Link가 주소를 바꾸고, Routes가 맞는 Route를 찾고, Route가 연결된 컴포넌트를 화면에 보여주는 구조다.




BrowserRouter, Link, Routes, Route 기본 예제

13세트에서는 React Router의 핵심 요소와 BrowserRouter가 필요한 이유를 정리했다.
이번 세트에서는 실제 App.jsx 예제로 주소와 컴포넌트가 어떻게 연결되는지 확인한다.


이 예제의 핵심은 Link를 클릭했을 때 페이지 전체가 새로고침되는 것이 아니라는 점이다.
Link는 브라우저 주소창의 URL을 바꾸고, Routes는 현재 URL과 맞는 Route를 찾는다.
그 결과 Route의 element에 연결된 컴포넌트가 화면에 표시된다.
즉, React Router의 기본 흐름은 Link로 주소를 바꾸고, Routes와 Route로 현재 주소에 맞는 컴포넌트를 보여주는 것이다.


3-1. App.jsx 기본 구조

예제에서 확인할 화면

이 예제는 가장 기본적인 라우팅 흐름을 확인하는 코드다.
라우팅은 주소에 따라 다른 화면을 보여주는 구조를 의미한다.


App.jsx에는 세 개의 화면 컴포넌트가 있다.
각 컴포넌트는 현재 주소에 맞게 선택되어 화면에 표시된다.


예제에서 사용하는 컴포넌트는 아래와 같다.

  • Home: 홈 화면을 보여준다.
  • About: 소개 화면을 보여준다.
  • Contact: 문의 화면을 보여준다.

각 컴포넌트는 단순히 문장 하나를 보여준다.
그래서 이번 예제에서는 화면 내용보다 주소와 컴포넌트가 어떻게 연결되는지에 집중하면 된다.


전체 코드

아래 코드는 App.jsx의 기본 라우팅 예제다.
react-router에서 Link, Routes, Route를 가져오고, 세 주소를 세 컴포넌트와 연결한다.

// App.jsx
import { Link, Routes, Route } from "react-router"; // 라우팅에 필요한 기능을 가져온다.

function Home() {
  return <h2>홈 화면입니다.</h2>; // 홈 주소에서 보여줄 화면이다.
}

function About() {
  return <h2>소개 화면입니다.</h2>; // 소개 주소에서 보여줄 화면이다.
}

function Contact() {
  return <h2>문의 화면입니다.</h2>; // 문의 주소에서 보여줄 화면이다.
}

function App() {
  return (
    <div>
      <h1>React Router 기초 예제</h1>

      <nav>
        <Link to="/">홈</Link>{" | "}
        <Link to="/about">소개</Link>{" | "}
        <Link to="/contact">문의</Link>
      </nav>

      <hr />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </div>
  );
}

export default App;

이 코드의 큰 구조는 두 부분으로 나눌 수 있다.


첫 번째는 Link가 들어 있는 nav 영역이다.
nav는 화면 이동 메뉴를 담는 영역으로 이해하면 된다.
여기서는 홈, 소개, 문의 링크를 보여준다.


두 번째는 Routes와 Route가 들어 있는 영역이다.
이 영역은 현재 주소를 확인하고, 그 주소와 맞는 컴포넌트를 화면에 보여준다.

Link는 사용자가 클릭할 수 있는 이동 링크를 만들고, Routes와 Route는 현재 주소에 맞는 컴포넌트를 찾아 화면에 보여준다.
/ 주소에서는 Home, /about 주소에서는 About, /contact 주소에서는 Contact 컴포넌트가 선택된다.


3-2. Link로 주소 이동 만들기

Link는 새로고침 없이 URL을 바꾼다

Link는 사용자가 클릭할 수 있는 이동 링크를 만든다.
겉으로는 HTML의 a 태그와 비슷하게 보인다.
하지만 동작 방식은 다르다.


일반적인 a 태그는 브라우저가 서버에 새 페이지를 요청할 수 있다.
그 과정에서 화면 전체가 다시 로드될 수 있다.


반면 React Router의 Link는 페이지 전체 새로고침 없이 주소만 바꾼다.
주소가 바뀌면 Routes가 현재 주소와 맞는 Route를 다시 찾는다.


예제의 Link 코드는 아래와 같다.

// LinkBasicExample.jsx
<nav>
  <Link to="/">홈</Link>{" | "}
  <Link to="/about">소개</Link>{" | "}
  <Link to="/contact">문의</Link>
</nav>

to="/"는 홈 주소로 이동하라는 뜻이다.
to="/about"은 소개 주소로 이동하라는 뜻이다.
to="/contact"는 문의 주소로 이동하라는 뜻이다.


여기서 to는 이동할 주소를 적는 속성이다.
초보자 기준에서는 Link가 눌렸을 때 주소창을 어떤 주소로 바꿀지 정하는 값으로 이해하면 된다.


Link와 a 태그의 차이

Link와 a 태그는 둘 다 링크처럼 보일 수 있다.
하지만 React SPA에서는 보통 내부 화면 이동에 Link를 사용한다.


차이는 아래처럼 정리할 수 있다.

  • a 태그: 서버에 새 페이지를 요청하면서 전체 새로고침이 발생할 수 있다.
  • Link: React Router 안에서 주소만 바꾸고 컴포넌트만 교체한다.

이 차이 때문에 React Router 예제에서는 내부 화면 이동을 만들 때 Link를 사용한다.
Link는 주소를 바꾸는 출발점이고, 실제로 어떤 화면을 보여줄지는 Routes와 Route가 결정한다.


3-3. Routes와 Route로 주소와 컴포넌트 연결하기

Routes는 현재 주소와 맞는 Route를 찾는다

Routes는 여러 Route 중 현재 주소와 맞는 것을 찾는 영역이다.
예제에서는 세 개의 Route가 Routes 안에 들어 있다.


코드는 아래와 같다.

// RoutesBasicExample.jsx
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/contact" element={<Contact />} />
</Routes>

Routes는 현재 브라우저 주소를 확인한다.
그리고 안쪽의 Route들을 확인하면서 현재 주소와 맞는 path를 찾는다.


현재 주소가 /이면 첫 번째 Route가 선택된다.
현재 주소가 /about이면 두 번째 Route가 선택된다.
현재 주소가 /contact이면 세 번째 Route가 선택된다.


Route는 path와 element를 연결한다

Route는 특정 주소와 특정 컴포넌트를 연결한다.
여기서 path는 주소 경로이고, element는 그 주소에서 보여줄 컴포넌트다.


예를 들어 아래 코드를 보면,

// RouteHomeExample.jsx
<Route path="/" element={<Home />} />

path="/"는 주소가 /일 때를 의미한다.
element={<Home />}는 그 주소에서 Home 컴포넌트를 보여주겠다는 뜻이다.


아래 코드도 같은 방식으로 이해하면 된다.

// RouteAboutContactExample.jsx
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />

path="/about"은 /about 주소를 의미하고, 그때 About 컴포넌트를 보여준다.
path="/contact"는 /contact 주소를 의미하고, 그때 Contact 컴포넌트를 보여준다.


Route는 “이 주소에서는 이 컴포넌트를 보여준다”라는 연결 규칙이다.


3-4. 처음 홈 화면이 보이는 흐름

처음 주소가 /일 때

개발 서버를 실행하고 처음 화면에 들어가면 보통 주소는 / 상태다.
이때 Routes는 현재 주소와 맞는 Route를 찾는다.


현재 주소가 /이면 아래 Route가 선택된다.

// HomeRouteMatchExample.jsx
<Route path="/" element={<Home />} />

이 Route의 element는 <Home />이다.
그래서 화면에는 Home 컴포넌트가 렌더링된다.


Home 컴포넌트는 아래처럼 작성되어 있다.

// HomeComponentExample.jsx
function Home() {
  return <h2>홈 화면입니다.</h2>; // 홈 화면 문장을 반환한다.
}

그래서 화면에는 홈 화면입니다.라는 문장이 보인다.

처음 주소가 /이면 Route path="/"에 연결된 Home 컴포넌트가 화면에 표시된다.
브라우저는 새 HTML을 다시 받은 것이 아니라, 현재 React 앱 안에서 / 주소와 맞는 컴포넌트를 보여준다.


처음 화면 데이터 흐름

처음 홈 화면이 보이는 흐름을 값 기준으로 펼치면 아래와 같다.

// 처음 홈 화면 흐름
현재 URL: /

Routes가 확인하는 Route 목록:
1. path="/" → Home
2. path="/about" → About
3. path="/contact" → Contact

현재 URL과 맞는 Route:
path="/"

화면에 렌더링되는 컴포넌트:
Home

화면 출력:
홈 화면입니다.

이 흐름을 보면 Routes가 현재 주소를 기준으로 맞는 Route를 찾는다는 점이 보인다.


3-5. 소개 화면으로 이동하는 흐름

소개 링크를 누르면 URL이 바뀐다

사용자가 소개 링크를 누르면 아래 Link가 동작한다.

// AboutLinkExample.jsx
<Link to="/about">소개</Link>

이 Link는 브라우저 주소창의 URL을 /about으로 바꾼다.
페이지 전체를 새로고침하는 것이 아니라, React Router 안에서 주소 이동을 처리한다.


주소가 /about으로 바뀌면 Routes는 다시 현재 주소와 맞는 Route를 찾는다.
이때 선택되는 Route는 아래 코드다.

// AboutRouteMatchExample.jsx
<Route path="/about" element={<About />} />

element에 <About />이 들어 있으므로 About 컴포넌트가 화면에 표시된다.


About 컴포넌트는 아래처럼 작성되어 있다.

// AboutComponentExample.jsx
function About() {
  return <h2>소개 화면입니다.</h2>; // 소개 화면 문장을 반환한다.
}

그래서 화면에는 소개 화면입니다.라는 문장이 보인다.

소개 링크를 누르면 주소가 /about으로 바뀌고, Route path="/about"에 연결된 About 컴포넌트가 표시된다.
이동 과정에서 서버가 새 HTML을 내려주는 것이 아니라, 현재 React 앱 안에서 보여줄 컴포넌트만 바뀐다.


소개 화면 데이터 흐름

소개 링크를 눌렀을 때 흐름은 아래처럼 정리할 수 있다.

// 소개 화면 이동 흐름
사용자 클릭:
소개 Link

Link의 이동 주소:
to="/about"

변경된 URL:
/about

Routes가 찾은 Route:
path="/about"

화면에 렌더링되는 컴포넌트:
About

화면 출력:
소개 화면입니다.

여기서 중요한 점은 Link가 화면을 직접 바꾸는 것이 아니라는 점이다.
Link는 주소를 바꾼다.
그리고 Routes가 바뀐 주소를 보고 맞는 컴포넌트를 선택한다.


3-6. 문의 화면으로 이동하는 흐름

문의 링크를 누르면 Contact 컴포넌트가 보인다

사용자가 문의 링크를 누르면 아래 Link가 동작한다.

// ContactLinkExample.jsx
<Link to="/contact">문의</Link>

이 Link는 브라우저 주소창의 URL을 /contact로 바꾼다.
주소가 바뀌면 Routes가 다시 현재 주소를 확인한다.


현재 주소가 /contact이면 아래 Route가 선택된다.

// ContactRouteMatchExample.jsx
<Route path="/contact" element={<Contact />} />

element에 <Contact />가 들어 있으므로 Contact 컴포넌트가 화면에 표시된다.


Contact 컴포넌트는 아래처럼 작성되어 있다.

// ContactComponentExample.jsx
function Contact() {
  return <h2>문의 화면입니다.</h2>; // 문의 화면 문장을 반환한다.
}

그래서 화면에는 문의 화면입니다.라는 문장이 보인다.

문의 링크를 누르면 주소가 /contact로 바뀌고, Route path="/contact"에 연결된 Contact 컴포넌트가 표시된다.
Link가 주소를 바꾸면 Routes가 현재 주소와 맞는 Route를 찾아 화면을 바꾼다.


문의 화면 데이터 흐름

문의 링크를 눌렀을 때 흐름은 아래처럼 정리할 수 있다.

// 문의 화면 이동 흐름
사용자 클릭:
문의 Link

Link의 이동 주소:
to="/contact"

변경된 URL:
/contact

Routes가 찾은 Route:
path="/contact"

화면에 렌더링되는 컴포넌트:
Contact

화면 출력:
문의 화면입니다.

이 흐름은 소개 화면과 구조가 같다.
다만 Link의 to 값과 Route의 path 값이 /contact로 달라질 뿐이다.


3-7. 다시 홈으로 돌아오는 흐름

홈 링크를 누르면 다시 / 주소로 돌아간다

사용자가 홈 링크를 누르면 아래 Link가 동작한다.

// HomeLinkExample.jsx
<Link to="/">홈</Link>

이 Link는 브라우저 주소창의 URL을 /로 바꾼다.
주소가 /가 되면 Routes는 다시 path="/"인 Route를 선택한다.


선택되는 코드는 아래와 같다.

// HomeRouteAgainExample.jsx
<Route path="/" element={<Home />} />

그래서 Home 컴포넌트가 다시 화면에 표시된다.

다시 홈 링크를 누르면 주소가 /로 돌아오고, Home 컴포넌트가 다시 화면에 표시된다.
이미 받아 둔 React 앱 안에서 주소만 바뀌고, 현재 주소에 맞는 컴포넌트가 선택된다.


홈으로 돌아오는 데이터 흐름

홈 링크를 눌렀을 때 흐름은 아래와 같다.

// 홈으로 돌아오는 흐름
사용자 클릭:
홈 Link

Link의 이동 주소:
to="/"

변경된 URL:
/

Routes가 찾은 Route:
path="/"

화면에 렌더링되는 컴포넌트:
Home

화면 출력:
홈 화면입니다.

이 흐름을 보면 이동할 때마다 서버에서 새 화면을 받는 것이 아니라, 현재 주소에 맞는 컴포넌트가 다시 선택된다는 점을 알 수 있다.


3-8. 기본 라우팅 흐름 정리

React Router의 기본 라우팅 흐름은 한 번에 연결해서 봐야 한다.
Link, Routes, Route를 따로 외우는 것보다 실제 이동 순서를 보는 것이 더 이해하기 쉽다.


예를 들어 사용자가 소개 링크를 클릭하면 흐름은 아래처럼 이어진다.

// 기본 라우팅 흐름
사용자가 소개 Link 클릭

<Link to="/about">소개</Link> 실행

브라우저 URL이 /about으로 변경

Routes가 현재 URL 확인

<Route path="/about" element={<About />} /> 선택

About 컴포넌트 렌더링

화면에 "소개 화면입니다." 출력

이 흐름에서 Link는 주소를 바꾸는 역할만 한다.
Routes는 바뀐 주소를 보고 맞는 Route를 찾는다.
Route는 해당 주소에서 보여줄 컴포넌트를 지정한다.


Link는 이동을 시작하고, Routes는 현재 주소와 맞는 Route를 찾고, Route는 연결된 컴포넌트를 화면에 보여준다.

BrowserRouter가 라우팅 환경을 만들고, Link가 주소를 바꾸면, Routes가 현재 주소와 맞는 Route를 찾아 해당 컴포넌트를 화면에 보여준다.
이 구조 덕분에 React SPA에서는 페이지 전체를 새로고침하지 않고도 주소에 맞는 화면 전환을 만들 수 있다.


핵심 정리

App.jsx 기본 예제는 React Router의 가장 기본적인 라우팅 흐름을 보여준다.
Link, Routes, Route를 사용해서 /, /about, /contact 주소를 각각 Home, About, Contact 컴포넌트와 연결한다.


Link는 사용자가 클릭할 수 있는 이동 링크를 만든다.
Link의 to 값에 따라 브라우저 주소창의 URL이 바뀐다.
하지만 페이지 전체가 새로고침되는 것은 아니다.


Routes는 현재 URL을 기준으로 맞는 Route를 찾는다.
Route는 path와 element를 연결해서 특정 주소에서 어떤 컴포넌트를 보여줄지 정한다.


처음 주소가 /이면 Home 컴포넌트가 보인다.
소개 링크를 누르면 주소가 /about으로 바뀌고 About 컴포넌트가 보인다.
문의 링크를 누르면 주소가 /contact로 바뀌고 Contact 컴포넌트가 보인다.
다시 홈 링크를 누르면 주소가 /로 바뀌고 Home 컴포넌트가 다시 보인다.


기본 라우팅의 핵심은 Link가 주소를 바꾸고, Routes가 현재 주소와 맞는 Route를 찾고, Route가 연결된 컴포넌트를 화면에 보여주는 흐름이다.




useParams와 useSearchParams로 학생 상세 화면 만들기

App2.jsx는 React Router에서 URL 안의 값을 읽어 화면을 바꾸는 예제다.
앞의 세트에서는 /, /about, /contact처럼 정해진 주소에 컴포넌트를 연결했다.


이번에는 주소 안에 학생 번호가 들어간다.
예를 들어 /students/1?mode=detail이라는 주소에는 학생 번호 1과 화면 모드 detail이 함께 들어 있다.
이 값을 컴포넌트에서 읽기 위해 useParams()와 useSearchParams()를 사용한다.
즉, useParams()는 경로 안의 값을 읽고, useSearchParams()는 물음표 뒤의 값을 읽거나 바꾸는 Hook이다.


4-1. Path Parameter와 Query String 구분하기

URL 안에는 여러 종류의 값이 들어갈 수 있다

React Router를 사용할 때 주소는 단순히 화면 이동만 의미하지 않는다.
주소 안에 화면에서 사용할 값이 들어갈 수 있다.


예를 들어 아래 주소를 볼 수 있다.

// URLExample.txt
/students/1?mode=detail

이 주소에는 두 가지 값이 들어 있다.
하나는 /students/1의 1이고, 다른 하나는 ?mode=detail의 detail이다.


/students/1에서 1은 경로 안에 들어 있는 값이다.
이런 값을 path parameter라고 한다.
path parameter는 주소 경로의 일부로 들어오는 값이다.


?mode=detail은 물음표 뒤에 붙은 값이다.
이런 값을 query string이라고 한다.
query string은 검색 조건, 필터 조건, 화면 모드처럼 선택 상태를 주소에 남길 때 자주 사용한다.


useParams와 useSearchParams의 역할

useParams()는 경로 안에 들어 있는 값을 읽는다.
예를 들어 /students/1에서 1을 읽을 때 사용한다.


useSearchParams()는 물음표 뒤에 붙은 값을 읽거나 바꾼다.
예를 들어 ?mode=detail에서 mode 값을 읽거나, mode=edit처럼 바꿀 때 사용한다.


둘은 모두 URL 값을 다룬다는 점에서는 비슷하다.
하지만 읽는 위치가 다르다.


정리하면 아래와 같다.

  • useParams(): /students/1의 1처럼 경로 안의 값을 읽는다.
  • useSearchParams(): ?mode=detail의 mode처럼 물음표 뒤의 값을 읽거나 바꾼다.

useParams()와 useSearchParams()는 비슷해 보이지만, 읽는 위치가 다르다.

/students/1의 1은 useParams()로 읽고, ?mode=detail의 mode 값은 useSearchParams()로 읽는다.
studentId는 어떤 학생을 보여줄지 결정하고, mode는 같은 학생 상세 화면 안에서 어떤 내용을 보여줄지 결정한다.


4-2. App2.jsx 기본 구조

예제에서 사용하는 데이터

App2.jsx에는 학생 정보 배열이 있다.
이 배열은 서버에서 받아온 데이터가 아니라, 예제 안에 직접 작성된 임시 데이터다.


코드는 아래와 같다.

// App2.jsx
const students = [
  { id: 1, name: "둘리", score: 90 }, // 1번 학생 데이터다.
  { id: 2, name: "또치", score: 85 }, // 2번 학생 데이터다.
  { id: 3, name: "도우너", score: 78 }, // 3번 학생 데이터다.
];

각 학생 객체에는 id, name, score가 들어 있다.
id는 학생 번호이고, name은 학생 이름이고, score는 학생 점수다.


이 예제에서는 주소의 studentId 값을 읽어서 students 배열에서 해당 학생을 찾는다.
예를 들어 주소가 /students/2?mode=detail이면 studentId는 2이고, 배열에서 id가 2인 또치를 찾는다.


전체 코드

아래 코드는 App2.jsx의 전체 흐름을 이해하기 위한 실제 코드다.
Routes, Route, Link, useParams, useSearchParams를 react-router에서 가져온다.

// App2.jsx
import {
  Routes,
  Route,
  Link,
  useParams,
  useSearchParams,
} from "react-router"; // 라우팅과 URL 값 읽기에 필요한 기능을 가져온다.

const students = [
  { id: 1, name: "둘리", score: 90 }, // 1번 학생 데이터다.
  { id: 2, name: "또치", score: 85 }, // 2번 학생 데이터다.
  { id: 3, name: "도우너", score: 78 }, // 3번 학생 데이터다.
];

function App2() {
  return (
    <div className="container">
      <h2>useParams / useSearchParams 예제</h2>

      <nav>
        <Link to="/">홈</Link>{" | "}
        <Link to="/students/1?mode=detail">1번 학생</Link>{" | "}
        <Link to="/students/2?mode=detail">2번 학생</Link>{" | "}
        <Link to="/students/3?mode=detail">3번 학생</Link>
      </nav>

      <hr />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/students/:studentId" element={<StudentDetail />} />
      </Routes>
    </div>
  );
}

function Home() {
  return (
    <div>
      <h2>홈</h2>
      <p>상단의 학생 링크를 클릭해 보세요.</p>
    </div>
  );
}

function StudentDetail() {
  const { studentId } = useParams(); // URL 경로에서 studentId를 읽는다.
  const [searchParams, setSearchParams] = useSearchParams(); // query string을 읽고 바꾼다.

  const mode = searchParams.get("mode") ?? "detail"; // mode 값이 없으면 detail을 기본값으로 쓴다.

  const student = students.find((item) => item.id === Number(studentId)); // 문자열 studentId를 숫자로 바꿔 학생을 찾는다.

  if (!student) {
    return <h2>존재하지 않는 학생입니다.</h2>; // 학생이 없으면 안내 문구를 보여준다.
  }

  return (
    <div className="card">
      <h2>학생 상세 화면</h2>

      <p>
        <strong>useParams() 결과:</strong> studentId = {studentId}
      </p>

      <p>
        <strong>useSearchParams() 결과:</strong> mode = {mode}
      </p>

      <hr />

      <h3>{student.name}</h3>

      {mode === "detail" && (
        <p>{student.name} 학생의 기본 정보를 보고 있습니다.</p>
      )}

      {mode === "edit" && (
        <p>{student.name} 학생의 정보를 수정하는 화면입니다.</p>
      )}

      {mode === "score" && (
        <p>{student.name} 학생의 점수는 {student.score}점입니다.</p>
      )}

      <div className="buttonBox">
        <button onClick={() => setSearchParams({ mode: "detail" })}>
          기본 정보
        </button>

        <button onClick={() => setSearchParams({ mode: "edit" })}>
          수정 모드
        </button>

        <button onClick={() => setSearchParams({ mode: "score" })}>
          점수 보기
        </button>
      </div>
    </div>
  );
}

export default App2;

이 코드는 크게 세 부분으로 나눌 수 있다.

  • students 배열: 화면에서 찾을 학생 데이터다.
  • App2: 링크와 라우트 구조를 만든다.
  • StudentDetail: URL 값을 읽고 학생 상세 화면을 출력한다.

이 예제의 핵심은 StudentDetail 안에서 studentId와 mode를 읽는 부분이다.


처음 화면

처음 주소가 /이면 Home 컴포넌트가 보인다.
Home 컴포넌트는 학생 링크를 클릭하라는 안내 문구를 보여준다.

처음 주소가 /이면 홈 화면이 보이고, 상단 링크를 누르면 각 학생 상세 주소로 이동할 수 있다.
아직 학생 링크를 클릭하지 않았기 때문에 StudentDetail 컴포넌트는 화면에 보이지 않는다.


4-3. Route path에서 :studentId 이해하기

콜론이 붙은 값은 동적 값이다

App2.jsx에는 아래 Route가 있다.

// StudentRouteExample.jsx
<Route path="/students/:studentId" element={<StudentDetail />} />

여기서 :studentId가 중요하다.
앞에 콜론 :이 붙은 값은 고정된 글자가 아니라, 실제 주소에서 바뀔 수 있는 값을 의미한다.


예를 들어 아래 주소들은 모두 같은 Route에 걸린다.

// PathParameterMatchExample.txt
/students/1
/students/2
/students/3
/students/10

모두 /students/ 뒤에 어떤 값이 붙어 있다.
React Router는 그 값을 studentId라는 이름으로 꺼낼 수 있게 해준다.


그래서 /students/1이면 studentId는 "1"이 된다.
/students/2이면 studentId는 "2"가 된다.
/students/10이면 studentId는 "10"이 된다.


useParams로 studentId 읽기

StudentDetail 컴포넌트 안에서는 useParams()로 studentId를 읽는다.

// UseParamsStudentIdExample.jsx
const { studentId } = useParams(); // URL 경로에서 studentId 값을 꺼낸다.

useParams()는 객체를 반환한다.
이 예제에서는 그 객체 안에 studentId가 들어 있다.


주소가 /students/1?mode=detail이면 studentId 값은 "1"이다.
주소가 /students/2?mode=detail이면 studentId 값은 "2"이다.


여기서 주의해야 할 점이 있다.
useParams()로 읽은 값은 숫자가 아니라 문자열이다.
따라서 students 배열의 숫자 id와 비교하려면 숫자로 바꿔야 한다.


아래 코드가 그 역할을 한다.

// FindStudentExample.jsx
const student = students.find((item) => item.id === Number(studentId)); // 문자열을 숫자로 바꿔 비교한다.

Number(studentId)는 문자열을 숫자로 바꾸는 코드다.
예를 들어 "1"은 1이 되고, "2"는 2가 된다.


useParams()로 읽은 값은 문자열이므로, 숫자 데이터와 비교할 때는 Number()로 변환해야 한다.


4-4. 1번 학생 상세 화면 흐름

1번 학생 링크를 누르면 studentId와 mode를 읽는다

상단에서 1번 학생 링크를 누르면 아래 Link가 실행된다.

// StudentOneLinkExample.jsx
<Link to="/students/1?mode=detail">1번 학생</Link>

이 링크를 누르면 주소는 /students/1?mode=detail이 된다.
이 주소는 /students/:studentId 라우트와 맞는다.
그래서 StudentDetail 컴포넌트가 화면에 렌더링된다.


이때 useParams()는 studentId를 읽는다.
useSearchParams()는 mode를 읽는다.


값 흐름은 아래처럼 볼 수 있다.

// 1번 학생 URL 값 흐름
현재 URL:
/students/1?mode=detail

useParams() 결과:
studentId = "1"

useSearchParams() 결과:
mode = "detail"

Number(studentId):
Number("1") → 1

students.find() 결과:
{ id: 1, name: "둘리", score: 90 }

students.find()는 배열에서 조건에 맞는 첫 번째 값을 찾는다.
이 예제에서는 item.id === Number(studentId) 조건으로 학생을 찾는다.


studentId가 "1"이고 Number(studentId)는 1이므로, 배열에서 id가 1인 둘리 객체를 찾는다.

주소의 /students/1에서 studentId 값은 1로 읽히고, ?mode=detail에서 mode 값은 detail로 읽힌다.
studentId를 숫자로 바꾼 뒤 students 배열에서 id가 1인 학생을 찾아 둘리의 기본 정보를 화면에 표시한다.


detail 모드 화면

mode 값이 "detail"이면 아래 조건이 참이 된다.

// DetailModeRenderExample.jsx
{mode === "detail" && (
  <p>{student.name} 학생의 기본 정보를 보고 있습니다.</p>
)}

현재 mode 값은 "detail"이다.
그래서 둘리 학생의 기본 정보를 보고 있습니다.라는 문장이 출력된다.


조건 계산을 펼치면 아래와 같다.

// detail 모드 조건 계산
mode === "detail"

"detail" === "detail"

결과: true

화면 출력:
둘리 학생의 기본 정보를 보고 있습니다.

이 흐름에서 mode는 어떤 학생을 찾는 값이 아니다.
학생을 찾는 값은 studentId다.
mode는 이미 찾은 학생을 어떤 방식으로 보여줄지 결정하는 값이다.


4-5. 다른 학생 링크를 눌렀을 때 흐름

2번 학생 링크를 누르면 studentId가 바뀐다

2번 학생 링크를 누르면 주소가 /students/2?mode=detail이 된다.

// StudentTwoLinkExample.jsx
<Link to="/students/2?mode=detail">2번 학생</Link>

이 주소도 /students/:studentId 라우트와 맞는다.
하지만 이번에는 studentId 값이 "2"다.


값 흐름은 아래와 같다.

// 2번 학생 URL 값 흐름
현재 URL:
/students/2?mode=detail

useParams() 결과:
studentId = "2"

useSearchParams() 결과:
mode = "detail"

Number(studentId):
Number("2") → 2

students.find() 결과:
{ id: 2, name: "또치", score: 85 }

이번에는 배열에서 id가 2인 학생을 찾는다.
그래서 또치 정보가 화면에 표시된다.

/students/2?mode=detail로 이동하면 studentId는 2가 되고, 배열에서 id가 2인 학생 정보를 찾아 화면에 표시한다.
mode는 그대로 detail이므로 또치 학생의 기본 정보 문구가 출력된다.


주소가 바뀌면 같은 컴포넌트가 다른 값을 보여준다

1번 학생과 2번 학생은 모두 같은 StudentDetail 컴포넌트를 사용한다.
컴포넌트가 달라지는 것이 아니라, URL에서 읽은 studentId 값이 달라진다.


이 차이가 중요하다.
주소가 /students/1?mode=detail이면 studentId는 "1"이다.
주소가 /students/2?mode=detail이면 studentId는 "2"다.


StudentDetail은 studentId 값을 기준으로 다른 학생을 찾는다.
그래서 같은 컴포넌트지만 다른 학생 정보를 보여줄 수 있다.


동적 라우트의 핵심은 같은 컴포넌트를 사용하되, 주소에서 읽은 값에 따라 다른 데이터를 보여주는 것이다.


4-6. useSearchParams로 mode 값 읽기

mode는 query string에서 읽는다

학생 상세 화면에서는 학생 번호뿐 아니라 화면 모드도 읽는다.
화면 모드는 URL의 물음표 뒤에 있는 mode 값으로 결정된다.


예를 들어 아래 주소를 볼 수 있다.

// ModeQueryStringExample.txt
/students/1?mode=detail

여기서 mode=detail은 query string이다.
StudentDetail 컴포넌트에서는 아래 코드로 mode 값을 읽는다.

// ReadModeExample.jsx
const [searchParams, setSearchParams] = useSearchParams(); // query string을 읽고 바꿀 준비를 한다.

const mode = searchParams.get("mode") ?? "detail"; // mode 값이 없으면 detail을 기본값으로 사용한다.

searchParams.get("mode")는 현재 주소에서 mode 값을 꺼낸다.
주소가 /students/1?mode=detail이면 결과는 "detail"이다.
주소가 /students/1?mode=edit이면 결과는 "edit"이다.
주소가 /students/1?mode=score이면 결과는 "score"이다.


?? "detail"은 왼쪽 값이 null 또는 undefined일 때 오른쪽 값을 사용한다는 뜻이다.
즉, 주소에 mode가 없으면 기본값으로 "detail"을 사용한다.


정리하면 아래와 같다.

// mode 값 읽기 예시
/students/1?mode=detail → mode = "detail"
/students/1?mode=edit → mode = "edit"
/students/1?mode=score → mode = "score"
/students/1 → mode 값이 없으므로 "detail" 사용

useParams()는 경로 값을 읽고, useSearchParams()는 쿼리 문자열 값을 읽는다.


4-7. setSearchParams로 query string 바꾸기

버튼을 누르면 mode 값이 바뀐다

학생 상세 화면에는 세 개의 버튼이 있다.
각 버튼은 같은 학생 상세 화면 안에서 mode 값만 바꾼다.


코드는 아래와 같다.

// SetSearchParamsButtonsExample.jsx
<div className="buttonBox">
  <button onClick={() => setSearchParams({ mode: "detail" })}>
    기본 정보
  </button>

  <button onClick={() => setSearchParams({ mode: "edit" })}>
    수정 모드
  </button>

  <button onClick={() => setSearchParams({ mode: "score" })}>
    점수 보기
  </button>
</div>

setSearchParams({ mode: "detail" })은 주소의 query string을 ?mode=detail로 바꾼다.
setSearchParams({ mode: "edit" })은 주소의 query string을 ?mode=edit로 바꾼다.
setSearchParams({ mode: "score" })은 주소의 query string을 ?mode=score로 바꾼다.


여기서 중요한 점은 학생 번호는 그대로 유지된다는 것이다.
예를 들어 현재 주소가 /students/1?mode=detail인 상태에서 수정 모드 버튼을 누르면 주소는 /students/1?mode=edit가 된다.
학생 번호 1은 그대로이고, mode 값만 바뀐다.


주소가 바뀌면 React Router는 이를 라우팅 변화로 처리한다.
그래서 같은 StudentDetail 컴포넌트가 다시 렌더링되고, 새 mode 값에 맞는 화면을 보여준다.

setSearchParams()를 호출하면 주소의 mode 값이 바뀌고, 현재 라우트 컴포넌트가 다시 렌더링되어 화면 상태가 달라진다.
학생 번호는 그대로 유지되고, 같은 학생 상세 화면 안에서 detail, edit, score 모드만 바뀐다.


기본 정보 버튼 흐름

기본 정보 버튼을 누르면 아래 코드가 실행된다.

// DetailButtonExample.jsx
<button onClick={() => setSearchParams({ mode: "detail" })}>
  기본 정보
</button>

이 버튼은 주소의 mode 값을 "detail"로 바꾼다.
그래서 화면에는 학생의 기본 정보 문구가 보인다.


흐름을 펼치면 아래와 같다.

// 기본 정보 버튼 흐름
사용자 클릭:
기본 정보 버튼

실행 코드:
setSearchParams({ mode: "detail" })

변경된 query string:
?mode=detail

mode 값:
"detail"

화면 출력:
학생의 기본 정보를 보고 있습니다.



4-8. mode 값에 따라 화면 다르게 보여주기

수정 모드 화면

수정 모드 버튼을 누르면 아래 코드가 실행된다.

// EditButtonExample.jsx
<button onClick={() => setSearchParams({ mode: "edit" })}>
  수정 모드
</button>

이 코드가 실행되면 mode 값은 "edit"이 된다.
주소도 ?mode=edit로 바뀐다.


StudentDetail 안에서는 아래 조건이 참이 된다.

// EditModeRenderExample.jsx
{mode === "edit" && (
  <p>{student.name} 학생의 정보를 수정하는 화면입니다.</p>
)}

현재 학생이 둘리라면 화면에는 둘리 학생의 정보를 수정하는 화면입니다.가 출력된다.

수정 모드 버튼을 누르면 mode=edit로 바뀌고, 같은 학생 상세 화면에서 수정 안내 문구가 표시된다.
주소의 학생 번호는 그대로 유지되며, query string의 mode 값만 바뀐다.


점수 보기 화면

점수 보기 버튼을 누르면 아래 코드가 실행된다.

// ScoreButtonExample.jsx
<button onClick={() => setSearchParams({ mode: "score" })}>
  점수 보기
</button>

이 코드가 실행되면 mode 값은 "score"가 된다.
주소도 ?mode=score로 바뀐다.


StudentDetail 안에서는 아래 조건이 참이 된다.

// ScoreModeRenderExample.jsx
{mode === "score" && (
  <p>{student.name} 학생의 점수는 {student.score}점입니다.</p>
)}

현재 학생이 둘리라면 화면에는 둘리 학생의 점수는 90점입니다.가 출력된다.

점수 보기 버튼을 누르면 mode=score로 바뀌고, 학생 점수 정보가 화면에 표시된다.
mode 값이 score가 되면 student.score 값을 사용해 해당 학생의 점수를 출력한다.


mode 조건 분기 흐름 정리

mode 값에 따라 화면에 보이는 문장은 달라진다.
하지만 사용하는 컴포넌트는 계속 StudentDetail 하나다.


정리하면 아래와 같다.

// mode 값에 따른 화면 분기
mode = "detail"
→ 기본 정보 문구 출력

mode = "edit"
→ 수정 모드 문구 출력

mode = "score"
→ 점수 문구 출력

이처럼 query string은 같은 상세 화면 안에서 보기 방식을 바꿀 때 유용하다.
학생 번호처럼 어떤 데이터를 찾을지 결정하는 값은 path parameter로 두고, 화면 모드처럼 부가적인 선택 상태는 query string으로 둘 수 있다.


4-9. 존재하지 않는 학생 번호 처리

배열에 없는 번호가 들어올 수 있다

사용자가 항상 1, 2, 3 링크만 누른다고 보장할 수 없다.
주소창에 직접 /students/10처럼 입력할 수도 있다.


하지만 students 배열에는 id가 10인 학생이 없다.
이 경우 students.find() 결과는 undefined가 된다.


아래 코드가 학생을 찾는 부분이다.

// FindMissingStudentExample.jsx
const student = students.find((item) => item.id === Number(studentId)); // 해당 id 학생을 찾는다.

주소가 /students/10이면 studentId는 "10"이다.
Number(studentId)는 10이 된다.
하지만 배열 안에는 id가 10인 객체가 없다.


값 흐름은 아래와 같다.

// 존재하지 않는 학생 번호 흐름
현재 URL:
/students/10

useParams() 결과:
studentId = "10"

Number(studentId):
Number("10") → 10

students.find() 결과:
undefined

이 상태에서 바로 student.name에 접근하면 오류가 발생할 수 있다.
student가 없는데 그 안의 name을 읽으려고 하기 때문이다.


if 조건으로 먼저 검사한다

이 문제를 막기 위해 student가 없는지 먼저 검사한다.

// MissingStudentGuardExample.jsx
if (!student) {
  return <h2>존재하지 않는 학생입니다.</h2>; // 학생이 없으면 여기서 화면을 반환한다.
}

if (!student)는 student 값이 없을 때 참이 된다.
이 조건이 참이면 학생 상세 화면을 만들지 않고, 바로 존재하지 않는 학생입니다. 문구를 반환한다.


이렇게 하면 student.name이나 student.score에 접근하기 전에 안전하게 흐름을 멈출 수 있다.


외부에서 들어오는 URL 값은 항상 올바르다고 믿으면 안 되고, 실제 데이터가 있는지 먼저 확인해야 한다.

/students/10처럼 students 배열에 없는 번호가 들어오면 상세 정보를 보여주지 않고, 존재하지 않는 학생이라는 안내 화면을 보여준다.
이 처리가 없으면 없는 학생의 name이나 score를 읽으려고 하면서 오류가 발생할 수 있다.


4-10. 전체 흐름 정리

학생 상세 화면의 값 이동

App2.jsx의 전체 흐름은 주소에서 값을 읽고, 그 값으로 화면을 결정하는 구조다.


전체 흐름은 아래와 같다.

// App2 전체 흐름
사용자가 학생 링크 클릭

Link가 URL 변경

예: /students/1?mode=detail

Routes가 /students/:studentId Route 선택

StudentDetail 컴포넌트 렌더링

useParams()로 studentId 읽기

useSearchParams()로 mode 읽기

Number(studentId)로 숫자 변환

students.find()로 학생 찾기

mode 값에 따라 화면 문구 선택

버튼 클릭 시 setSearchParams()로 mode 변경

StudentDetail 다시 렌더링

변경된 mode에 맞는 화면 출력

이 흐름에서 studentId와 mode는 역할이 다르다.
studentId는 어떤 학생을 찾을지 결정한다.
mode는 찾은 학생을 어떤 화면 상태로 보여줄지 결정한다.


따라서 두 값을 구분해서 읽어야 한다.
studentId는 useParams()로 읽고, mode는 useSearchParams()로 읽는다.


App2.jsx의 핵심은 URL에서 학생 번호와 화면 모드를 읽고, 그 값에 따라 같은 컴포넌트에서 다른 화면을 보여주는 것이다.


핵심 정리

useParams()는 URL 경로 안에 들어 있는 동적 값을 읽는 Hook이다.
/students/1에서 1처럼 경로 일부로 들어온 값을 읽을 때 사용한다.


useSearchParams()는 URL의 물음표 뒤에 붙은 query string을 읽거나 바꾸는 Hook이다.
?mode=detail, ?mode=edit, ?mode=score처럼 화면 모드를 주소에 남길 때 사용할 수 있다.


App2.jsx에서는 Route path="/students/:studentId"로 학생 상세 주소를 처리한다.
StudentDetail 컴포넌트는 useParams()로 studentId를 읽고, useSearchParams()로 mode를 읽는다.


useParams()로 읽은 studentId는 문자열이다.
그래서 students 배열의 숫자 id와 비교하려면 Number(studentId)로 숫자 변환을 해야 한다.


setSearchParams()를 사용하면 같은 학생 상세 화면 안에서 mode 값만 바꿀 수 있다.
mode가 detail이면 기본 정보, edit이면 수정 모드, score이면 점수 보기 화면이 출력된다.


마지막으로 주소는 사용자가 직접 바꿀 수 있으므로, 존재하지 않는 학생 번호도 들어올 수 있다.
그래서 students.find() 결과가 없을 때는 if (!student)로 먼저 검사하고, 안전하게 안내 문구를 보여줘야 한다.


useParams()는 어떤 데이터를 볼지 결정하고, useSearchParams()는 같은 화면 안에서 어떤 방식으로 볼지 결정하는 데 사용된다.

0개의 댓글