
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를 사용하려면 먼저 핵심 요소의 역할을 알아야 한다.
라우팅은 단순히 링크를 클릭하는 기능이 아니라, 현재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가 연결된 컴포넌트를 화면에 보여주는 구조다.
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. 기본 라우팅 흐름 정리
Link 클릭부터 화면 출력까지
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가 연결된 컴포넌트를 화면에 보여주는 흐름이다.
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()는 같은 화면 안에서 어떤 방식으로 볼지 결정하는 데 사용된다.