리액트는 spa(싱글 페이지 어플리케이션) 형태인데
여러 웹페이지처럼 보이게 할 수 있는 방법이 있다.
리액트 라우터라는 라이브러리를 활용하는 것이다 😺
실습을 위해 리액트프로젝트를 생성한다.
npx create-react-app router-example
src 폴더 안에 page 폴더 생성 -> page 폴더 안에 Homepage, Aboutpage 파일 생성 -> 각각 간단하게 문구 입력하기
app.js 에서 작성한 2 파일을 import 후 반환하는 영역 안에 넣고
터미널에서 npm start
로 실행해보기
실행하면 예상대로 하나의 페이지 안에 2개의 컴포넌트가 나타나고 있다.
이제 이를 유동적으로 각 파일을 보여줘서 마치 여러 페이지가 있는 것처럼
작동하게 해보자 !
npm install react-router-dom
import { BrowserRouter } from 'react-router-dom';
root.render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
import { Routes, Route } from 'react-router-dom';
결과
이전과 달리 실행하면 homepage 컴포넌트 내용만 나오고 /about 주소로 입장하면
해당 주소에 설정한 aboutpage 컴포넌트 내용이 나오는 걸 확인할 수 있다.
위처럼 라우터별 페이지를 보이게 할 수 있지만
늘 주소창에 입력을 해서 이동하게 된다.
라우터 라이브러리에서 제공해주는 요소를 활용해서
페이지별 이동을 간편하게 하는 방법을 사용해보자 !
홈페이지를 만들 때 기본적으로 많은 url이 사용되는데
그럴 때마다 라우터의 Link, Navigate 등으로만 처리하는데는 문제가 있다.
만약 쇼핑몰 홈페이지가 있고 내가 원하는 상품의 디테일 페이지에 가고 싶은데 그 상품의 디테일 페이지를 다 다른 url로 달아주고 처리 하기엔 비효율적이기 때문이다.
그래서 중요한 것이 url 디자인이다. 그 중에서 Restful Route 패턴에 대해 알아보자 !
Restful Route : REST 규칙을 이용해서 만든 url 디자인 패턴
이 패턴을 사용하면 http 명령와 url을 매칭시켜 url 디자인을 좀 더 단순하고
통일성 있게 해주는 장점이 있다.
Get : 데이터를 가져올 때 쓰임 (fetch해서 데이터를 가져올 때 늘 쓰고 있는 거!!)
Post : 데이터를 생성할 때 쓰임 ( 새로운 데이터를 만들 때 씀)
Put : 데이터를 수정할 때 쓰임(기존 데이터를 수정할 때 사용 / Patch 라고도 불림)
Delete: 데이터를 삭제할때 쓰임
/items + get 명령어 = 아이템읽어오기
/items + post 명령어 = 아이템 생성하기
/items + put 명령어 = 아이템 수정하기
/items + delete명령어 = 아이템 삭제하기
=> /itmes라는 url 하나로 4가지의 액션을 할 수 있게 되는 것!
하나의 아이템만 가져오고 싶을 때
/items/:id +get 명령어 = id를 가진 아이템읽어오기
/items/:id +put 명령어 = id를 가진 아이템 수정하기
/items/:id +delete 명령어 = id를 가진 아이템 삭제하기
내가 보여주고 싶은 아이템의 아이디를 url 파라미터로 넣어줄 수 있게 된다.
이제 이 파라미터 값을 읽어오려면 ?!
useParams : React Router 라이브러리에서 제공하는 훅이다.
예시로 이런 url이 있다고 하자.
http://example.com/users/123
여기서 123은 사용자의 ID라고 하고
이 URL을 React Router로 관리하고 있다면,
useParams를 사용하여 이 ID 값을 읽어올 수 있다.
import { useParams } from 'react-router-dom';
function UserComponent() {
// useParams 훅을 사용하여 URL 파라미터 읽어오기
let { userId } = useParams();
return (
<div>
<h2>User ID: {userId}</h2>
</div>
);
}
이 컴포넌트를 /users/123 경로에 렌더링하면 userId 값은 "123"이 된다.
이것이 useParams의 역할쓰
http://localhost:3000/products/:1
로 입장
콘솔을 확인해보면 객체 형태로 반환된 것을 확인할 수 있다.
이 객체 안에 id라는 키 값이 있고 1이라는 value값이 있는 것을 확인할 수 있다.
app.js에서 설정한 route를 같이 보면 path에서 설정한 id라는 파라미터가 자동으로
키 값으로 들어가고 1이라고 입력한 파라미터 값이 value로 나타난 것.
<Route path="/products/:id" element={<ProductDetailPage />} />
<Route path="/products/:id/:num" element={<ProductDetailPage />} />
라고 하고 테스트해보면 id엔 aaaa가 num엔 34가 들어간 걸 확인할 수 있다.
destructuring을 하면 객체나 배열을 비구조화하여 그 값을 개별 변수에 할당하여 사용할 수 있음 !! 필요한 값을 간편하게 추출할 때 유용하다.
("비구조화 할당(Destructuring assignment)"은 JavaScript에서 객체나 배열을 분해하여 그 값을 개별 변수에 할당하는 것을 의미)
예시)
파라미터 값을 읽어오는 것처럼 ?뒤에 오는 쿼리값도 읽어올 수 있다.
우선 테스트로 useNavigate를 사용해서 확인해보기
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
const Homepage = () => {
const navigate = useNavigate();
const goProductPage = () => {
navigate('/products?q=pants');
};
return (
<div>
<h1>Homepage</h1>
<Link to="/about">Go to about page</Link>
<button onClick={goProductPage}>go to product page</button>
</div>
);
};
export default Homepage;
?뒤에 붙는게 쿼리인데 이는 선택적인 정보이다.
그래서 쿼리를 어떻게 더 붙여도 url 경로에 영향을 미치지 않는다.
하지만 url 파라미터 값을 여러개 붙이면 페이지가 나오지 않는다. 왜냐하면
이 경우에는 경로 자체가 달라진 것이기 때문이다.
쿼리와 파라미터 차이 !!
쿼리 값을 알아 내는 훅은 useSearchParams라는 친구이다.
query.get("page")로 해서도 확인해보기
해당 쿼리값이 콘솔에서 확인되고 있다.
흐름 예시
import { useSearchParams } from 'react-router-dom';
function MyComponent() {
const [searchParams] = useSearchParams();
// 쿼리 파라미터 값 읽어오기
const myParam = searchParams.get('myParam');
return (
<div>
<h2>My Param: {myParam}</h2>
</div>
);
}
useSearchParams 훅을 사용하여 현재 URL의 쿼리 파라미터를 가져옴 -> get 메서드를 사용하여 특정 쿼리 파라미터 값을 읽어옴 - > 이 코드를 실행하면 현재 URL의 myParam이라는 쿼리 파라미터의 값을 가져와서 출력한다.
만약 URL이 http://example.com?myParam=value
와 같은 형태라면 "My Param: value"라는 문구가 출력되는 형태이다.
접근하면 안 되는 페이지
ex) 로그인해야만 볼 수 있는 마이페이지
접근하면 안 되는 페이지로 이동하려고 할 때
리다이렉트 되게 해보자 !
Navigate 컴포넌트 활용 +
로그인 유무 상태를 하나 생성해서 테스트 해보면 authenticate가 true일 때가 login 상태라는 뜻임 - > true일 경우엔 user 경로에 들어갈 수 있고 false 일 땐 login 페이지로 리다이렉트 처리 된다.
아래 코드는 다른 방식으로 접근한 예제 소스
AuthContext를 사용하여 로그인 상태를 관리하고,
로그인이 필요한 페이지인 PrivateRoute 컴포넌트에서는
useContext를 사용하여 로그인 상태를 확인하고 리다이렉션을 처리 - >
사용자가 로그인되어 있지 않은 경우 <Navigate to="/login" replace />
를 사용하여 로그인 페이지로 리다이렉션한다.
import React, { useState } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
// 로그인 상태를 관리하는 컨텍스트
const AuthContext = React.createContext();
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<Router>
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/mypage" element={<PrivateRoute />} />
</Routes>
</AuthContext.Provider>
</Router>
);
}
function LoginPage() {
const { setIsLoggedIn } = React.useContext(AuthContext);
const handleLogin = () => {
// 실제 로그인 로직을 여기에 추가
// 로그인에 성공하면 setIsLoggedIn(true)를 호출하여 isLoggedIn을 true로 설정
setIsLoggedIn(true);
};
return (
<div>
<h2>로그인 페이지</h2>
<button onClick={handleLogin}>로그인</button>
</div>
);
}
function PrivateRoute() {
const { isLoggedIn } = React.useContext(AuthContext);
// 사용자가 로그인되어 있지 않다면 로그인 페이지로 리다이렉션
if (!isLoggedIn) {
return <Navigate to="/login" replace />;
}
// 사용자가 로그인되어 있다면 마이 페이지로 이동
return (
<div>
<h2>마이 페이지</h2>
{/* 마이 페이지의 내용을 추가 */}
</div>
);
}
export default App;
replace prop은 기존의 브라우저 히스토리를 대체하는 역할
즉, 사용자가 뒤로가기 버튼을 누르거나 이전 페이지로 돌아갈 때, 리다이렉션 이전의 페이지가 히스토리에 남지 않도록 한다.
보통 로그인 페이지와 같이 사용자가 다시 돌아갈 필요가 없는 페이지에서 replace prop을 사용한다.
로그인 페이지에서 로그인에 성공한 후에는 사용자가 뒤로가기 버튼을 누르더라도 다시 로그인 페이지로 돌아가는 것이 아니라, 로그인 페이지 이전의 페이지로 돌아가는 것이 더 자연스럽기 때문에 로그인 페이지로 리다이렉션할 때 replace prop을 사용하여 히스토리를 대체함으로써 사용자가 뒤로가기 버튼을 눌렀을 때 로그인 페이지로 되돌아가지 않도록 할 수 있다.