src/
├── components/ <---------------- 3. 기본 레이아웃에 적용될 컨포넌트 추가
│ ├── Header.jsx
│ ├── Footer.jsx
├── layout/ <-------------------- 2. 레이아웃 관련 컨포넌트
│ ├── Default.jsx
├── pages/ <--------------------- 1. 페이지 관련 컨포넌트
│ ├── MainPage.jsx
│ ├── ShopPage.jsx
│ ├── BlogPage.jsx
│ ├── AboutPage.jsx
│ ├── CartPage.jsx
│ └── NotFound.jsx
├── router.jsx <----------------- 4. 라우터 설정 파일
└── main.jsx <------------------- 5. outerProvider 설정
import { createBrowserRouter } from 'react-router-dom'
import Default from './layout/Default'
import MainPage from './pages/MainPage'
import AboutPage from './pages/AboutPage'
import ShopPage from './pages/ShopPage'
import BlogPage from './pages/BlogPage'
import NotFound from './pages/NotFound'
import CartPage from './pages/CartPage'
let router = createBrowserRouter([
{
path: '/',
element: <Default />,
children: [
{
path: '',
element: <MainPage />,
},
{ path: '/about', element: <AboutPage /> },
{ path: '/shop', element: <ShopPage /> },
{ path: '/blog', element: <BlogPage /> },
{ path: '/cart', element: <CartPage /> },
],
},
{
path: '*',
element: <NotFound />,
},
])
export default router
공통 레이아웃인 Default 컴포넌트를 기준으로 main,about shop 등 페이짇 등을 이동이 가능하다 NotFound 페이지는 그 외에 주소로 이동했을 때 로딩된다.
createRoot(document.getElementById('root')).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
)
RouterProvider 를 router 설정해준다.
주요 라우팅 옵션1. path
• 의미: 라우트가 매치될 URL 경로를 지정합니다.
• 예시: /about, /products/:id, *(모든 경로 매치)
2. element
• 의미: 경로가 일치할 때 렌더링할 React 컴포넌트를 지정합니다.
• 예시: <Home />, <Dashboard />
3. errorElement
• 의미: 라우트 내에서 오류가 발생했을 때 렌더링할 컴포넌트를 지정합니다.
• 예시: <NotFound /> or <ErrorPage />
4. children
• 의미: 중첩 라우트를 정의합니다. 부모 라우트에는 <Outlet /> 컴포넌트가 필요합니다.
• 예시: 하위 라우트 배열
5. index
• 의미: 부모 경로에 정확히 일치할 때 렌더링되는 기본 자식 라우트를 표시합니다.
• 예시: index: true
6. caseSensitive
• 의미: URL 매칭 시 대소문자 구분 여부를 결정합니다.
• 예시: caseSensitive: true
7. loader
• 의미: 컴포넌트가 렌더링되기 전에 데이터를 로드하는 함수를 지정합니다.
• 예시: loader: productLoader
8. action
• 의미: 폼 제출 등 데이터 변경 요청을 처리하는 함수를 지정합니다.
• 예시: action: updateProductAction
9. id
• 의미: 라우트에 고유 식별자를 부여합니다(useRouteLoaderData와 함께 사용).
• 예시: id: "product-detail"
10. handle
• 의미: 메타데이터와 같은 사용자 정의 데이터를 라우트에 첨부합니다.
• 예시: handle: { crumb: () => "Products" }
11. shouldRevalidate
• 의미: 라우트의 데이터를 언제 다시 로드할지 결정하는 함수를 지정합니다.
• 예시: shouldRevalidate: ({ currentUrl }) => currentUrl.pathname !== "/login"
react-router-dom에서 NavLink는 링크가 현재 URL과 일치하는지를 감지해서, 특정 스타일을 적용할 수 있도록 도와주는 컴포넌트이다.
className에 함수를 넘기면NavLink 가 그 함수에 isActive, isPending 등의 상태를 담은 객체를 넘겨준다 그럼 isActive는 NavLink 내부에서 현재 경로와 해당 링크의 경로가 일치하는지 확인해서 true/false로 넘겨주기 때문에 현재 링크만 다르게 스타일을 적용할 때 좋다.
import React from 'react'
import css from './Logo.module.css'
const Logo = () => {
return (
<div className={css.logo}>
<svg
// width="556"
// height="113"
viewBox="0 0 556 113"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M51.9442 19.5702C51.4496 19.3724 50.8562 19.0262 50.1638 18.5317C49.4715 18.0372 48.4824 17.5921 47.1966 17.1964C45.9108 16.7019 44.2788 16.3063 42.3006 16.0095C40.3225 15.7128 37.8003 15.6634 34.7341 15.8612V0.876586C39.0861 0.975494 43.4875 1.47004 47.9384 2.36021C52.4882 3.15148 56.1478 4.43728 58.9172 6.21763C58.3238 7.50344 57.6314 8.93761 56.8401 10.5201C56.0489 12.0038 55.3071 13.4379 54.6147 14.8226C53.9223 16.1085 53.3289 17.1964 52.8344 18.0866C52.3398 18.9768 52.0431 19.4713 51.9442 19.5702ZM0.759164 31.4392C0.759164 27.0873 1.50098 23.1804 2.9846 19.7186C4.46822 16.2568 6.44639 13.2401 8.91909 10.6685C11.4907 7.99798 14.458 5.92091 17.8208 4.43728C21.2826 2.85475 24.8928 1.81621 28.6513 1.32167V16.8997C25.4862 17.9877 22.8652 19.9164 20.7881 22.6859C18.711 25.3564 17.6725 28.0764 17.6725 30.8458C17.6725 34.7032 18.711 37.8188 20.7881 40.1926C22.8652 42.5664 25.4862 44.6435 28.6513 46.4238V63.3371C25.3873 61.7546 22.1233 60.0732 18.8594 58.2928C15.5954 56.4136 12.6282 54.2376 9.95763 51.7649C7.28711 49.2922 5.06167 46.4238 3.28132 43.1599C1.59988 39.8959 0.759164 35.989 0.759164 31.4392ZM3.13296 91.9711C4.51768 92.7623 6.24857 93.5536 8.32564 94.3449C10.5016 95.0372 12.7271 95.7296 15.002 96.422C17.2768 97.0154 19.5517 97.5099 21.8266 97.9056C24.1015 98.3012 26.228 98.499 28.2062 98.499H28.6513V112.593H28.3546C25.9808 112.593 23.5081 112.445 20.9364 112.148C18.4637 111.951 15.9416 111.555 13.37 110.961C10.7984 110.368 8.32564 109.577 5.95185 108.588C3.67696 107.5 1.69879 106.214 0.0173518 104.73L3.13296 91.9711ZM34.7341 49.3911C37.9981 50.8747 41.3115 52.4572 44.6744 54.1387C48.1362 55.8201 51.2518 57.8972 54.0213 60.3699C56.7907 62.7437 59.0656 65.6615 60.8459 69.1233C62.6263 72.5851 63.5164 76.8876 63.5164 82.0308C63.5164 85.5915 62.8241 89.0533 61.4394 92.4162C60.1536 95.779 58.2743 98.8452 55.8016 101.615C53.4278 104.285 50.4606 106.56 46.8999 108.439C43.3392 110.319 39.2839 111.555 34.7341 112.148V97.9056C42.8446 95.9274 46.8999 90.5369 46.8999 81.7341C46.8999 78.2723 45.7624 75.4039 43.4875 73.1291C41.2126 70.8542 38.2948 68.7276 34.7341 66.7495V49.3911Z"
fill="#A18A68"
id="path1"
/>
<path
d="M165.59 2.65693V112H147.786V62.7437H106.542V50.578H147.786V2.65693H165.59ZM82.3585 2.65693H100.459V112H82.3585V2.65693ZM190.96 56.8092C190.96 48.0064 191.998 40.2421 194.075 33.5163C196.251 26.6916 199.219 20.955 202.977 16.3063C206.835 11.5587 211.335 7.94853 216.478 5.47582C221.72 2.90421 227.407 1.42058 233.54 1.02495V15.8612C230.177 16.2568 227.012 17.2954 224.045 18.9768C221.077 20.6582 218.506 23.1309 216.33 26.3949C214.154 29.6589 212.423 33.813 211.137 38.8574C209.851 43.9017 209.159 49.8856 209.06 56.8092C209.159 63.8317 209.851 69.8651 211.137 74.9094C212.423 79.8548 214.154 84.009 216.33 87.3718C218.605 90.7347 221.226 93.3063 224.193 95.0867C227.16 96.7681 230.276 97.7572 233.54 98.0539V112.742C227.506 112.346 221.869 110.912 216.626 108.439C211.384 105.868 206.835 102.258 202.977 97.6089C199.219 92.8613 196.251 87.0751 194.075 80.2505C191.998 73.4258 190.96 65.612 190.96 56.8092ZM239.474 1.02495C245.508 1.32167 251.096 2.75584 256.239 5.32746C261.481 7.89907 265.982 11.5587 269.74 16.3063C273.598 20.955 276.614 26.6916 278.79 33.5163C280.966 40.2421 282.054 48.0064 282.054 56.8092C282.054 65.612 280.966 73.4258 278.79 80.2505C276.614 87.0751 273.598 92.8613 269.74 97.6089C265.982 102.258 261.531 105.868 256.388 108.439C251.244 110.912 245.607 112.346 239.474 112.742V98.0539C242.837 97.6583 246.002 96.6198 248.969 94.9383C251.937 93.158 254.508 90.6358 256.684 87.3718C258.86 84.009 260.591 79.8548 261.877 74.9094C263.163 69.8651 263.855 63.8317 263.954 56.8092C263.855 49.7867 263.163 43.8028 261.877 38.8574C260.591 33.813 258.811 29.6589 256.536 26.3949C254.36 23.032 251.788 20.5099 248.821 18.8284C245.854 17.0481 242.738 16.0095 239.474 15.7128V1.02495ZM306.979 2.65693H324.783V112H306.979V2.65693ZM338.432 2.65693C343.081 2.65693 347.68 3.39875 352.23 4.88237C356.779 6.36599 360.835 8.59143 364.395 11.5587C368.055 14.5259 370.973 18.1361 373.149 22.3891C375.424 26.5433 376.561 31.2414 376.561 36.4836C376.561 42.0224 375.621 47.0667 373.742 51.6165C371.863 56.0674 369.242 59.8259 365.879 62.8921C362.516 65.9582 358.461 68.332 353.713 70.0134C348.966 71.6949 343.724 72.5356 337.987 72.5356H330.865V59.4797H341.102C343.278 59.4797 345.405 59.1335 347.482 58.4412C349.658 57.6499 351.636 56.4136 353.417 54.7321C355.197 53.0507 356.631 50.7263 357.719 47.7591C358.807 44.6929 359.351 40.9344 359.351 36.4836C359.351 34.7032 359.104 32.6756 358.609 30.4007C358.115 28.0269 357.076 25.8015 355.494 23.7244C354.01 21.5484 351.883 19.7681 349.114 18.3833C346.345 16.8997 342.685 16.1579 338.135 16.1579H330.865V2.65693H338.432ZM396.738 2.65693H414.542V112H396.738V2.65693ZM428.191 2.65693C432.84 2.65693 437.439 3.39875 441.989 4.88237C446.539 6.36599 450.594 8.59143 454.155 11.5587C457.814 14.5259 460.732 18.1361 462.908 22.3891C465.183 26.5433 466.32 31.2414 466.32 36.4836C466.32 42.0224 465.381 47.0667 463.501 51.6165C461.622 56.0674 459.001 59.8259 455.638 62.8921C452.275 65.9582 448.22 68.332 443.472 70.0134C438.725 71.6949 433.483 72.5356 427.746 72.5356H420.625V59.4797H430.862C433.038 59.4797 435.164 59.1335 437.241 58.4412C439.417 57.6499 441.395 56.4136 443.176 54.7321C444.956 53.0507 446.39 50.7263 447.478 47.7591C448.566 44.6929 449.11 40.9344 449.11 36.4836C449.11 34.7032 448.863 32.6756 448.368 30.4007C447.874 28.0269 446.835 25.8015 445.253 23.7244C443.769 21.5484 441.643 19.7681 438.873 18.3833C436.104 16.8997 432.444 16.1579 427.894 16.1579H420.625V2.65693H428.191ZM487.091 2.65693H505.191V112H487.091V2.65693ZM550.887 62.7437H511.274V50.578H550.887V62.7437ZM511.274 98.499H555.486V112H511.274V98.499ZM555.486 2.65693V16.1579H511.274V2.65693H555.486Z"
fill="black"
className="black-path"
/>
</svg>
</div>
)
}
export default Logo
.logo:hover .gold-path {
fill: #a18a68;
}
.logo:hover .black-path {
fill: var(--light-colors-dark-gray-light);
}
강사님께서 id 를 줬는데 안되셔서 nth-child를 이용햇지만 className을 이용하면 잘 들어갔다!!.
이 방식은 완전히 벡터 기반 그래픽이라서 크기, 색상, path 등을 직접 제어할 수 았어서 CSS로 색상 바꾸거나 마우스 호버 스타일 주기도 쉬움.
특히 애니메이션 줄 때 유용하며 하나의 단점은 복잡한 SVG는 코드가 길어진다는 게 단점이다.
useLocation은 React Router에서 제공하는 hook으로 현재 브라우저의 URL 정보를 가져올 수 있다. 현재 페이지의 경로,쿼리 매개변스,매시 등 접근이 가능하다.
const [isOn, setIsOn] = useState(false)
const location = useLocation()
console.log(location)
console.log(location.pathname)
const addClassOn = () => {
setIsOn(!isOn)
}
useEffect(() => {
setIsOn(false)
}, [location.pathname])
반응형 페이지를 구성할 때 NavBar가 열려 있을 시 페이지를 이동할 경우 pathname을 받아와pathname이 바뀌므로 닫힘 상태로 바꿔준다.
import React, { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import axios from 'axios';
function ProductSearchPage() {
const location = useLocation();
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
// 쿼리 파라미터에서 query 값 추출
const queryParams = new URLSearchParams(location.search);
const query = queryParams.get("query");
useEffect(() => {
if (query) {
setLoading(true);
axios
.get(`/api/products?search=${query}`)
.then((res) => setProducts(res.data))
.catch((err) => console.error(err))
.finally(() => setLoading(false));
}
}, [query]);
return (
<div>
<h2>Search Results for "{query}"</h2>
{loading ? (
<p>Loading...</p>
) : products.length > 0 ? (
<ul>
{products.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
) : (
<p>No results found.</p>
)}
</div>
);
}
export default ProductSearchPage;
검색을 하게되면 ?query=(값) 페이지로 이동을 하게된다. 그럼 이동한 페이지에서 아래의 코드를 이용해 query값을 받아와
const queryParams = new URLSearchParams(location.search);
const query = queryParams.get("query");
백엔드에 query값을 포함한 정보를 요청할 때 사용이 된다.
import React, { useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
function App() {
const location = useLocation(); // 현재 URL 정보 가져오기
useEffect(() => {
if (location.hash) {
const element = document.querySelector(location.hash); // 해시값에 해당하는 요소 찾기
if (element) {
// 요소가 있으면 해당 위치로 부드럽게 스크롤 이동
element.scrollIntoView({ behavior: 'smooth' });
}
}
}, [location.hash]); // location.hash가 변경될 때마다 실행
return (
<div>
<h1>Welcome to the Page!</h1>
<nav>
<ul>
<li>
<Link to="#section1">Go to Section 1</Link>
</li>
<li>
<Link to="#section2">Go to Section 2</Link>
</li>
</ul>
</nav>
<div id="section1" >
<h2>Section 1</h2>
<p>This is Section 1.</p>
</div>
<div id="section2" >
<h2>Section 2</h2>
<p>This is Section 2.</p>
</div>
</div>
);
}
export default App;
Link를 클릭하면 해시(#section1, #section2)가 URL에 추가되고, useEffect에서 이를 감지하여 해당 섹션으로 스크롤을 이동시킬 때 사용이 됩니다.
const navigate = useNavigate();
const movePage = () => {
navigate('/product',{
state: {
mdName:model.mdname
}
});
}
products.jsx
const [selectedCategories, setSelectedCategories] = useState(new Set());
const location = useLocation();
let modelName = location.state?.mdName.replace(/\s+/g, "") || null; // 존재하면 공백 제거 없음연 null
useEffect(() => {
if (modelName) {
if(modelName ==="Suv")
modelName = modelName.toUpperCase(); // suv만 대소문자 달라서
setSelectedCategories(new Set([modelName]));
}
}, [modelName]);
useNavigate의 state 속성을 통해 model의 이름을 mdName이라는 state로 전해주었고 이동된 products 페이지에서 useLocation을 통해 state.mdName을 받아와 이동하게 되면 카테고리가 자동 선택 되도록 하였음