Post

[Day52] React Router

[Day52] React Router

๐Ÿ”ธ Lint

Lint๋Š” ์ฝ”๋“œ ์Šคํƒ€์ผ๊ณผ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  ์ผ๊ด€๋œ ์ฝ”๋“œ๋ฅผ ์œ ์ง€ํ•˜๊ฒŒ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ

lint ์‹คํ–‰ : ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ(.)๋ถ€ํ„ฐ ๋ชจ๋“  ํŒŒ์ผ์„ ESLint๋กœ ๊ฒ€์‚ฌ

1
npm run lint
  • ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฒ€์‚ฌ ๋ฐ ์ž๋™ ์ˆ˜์ •
  • ํ˜‘์—…ํ•  ๋•Œ ์ฝ”๋“œ ํ€„๋ฆฌํ‹ฐ ์œ ์ง€์— ๋„์›€๋จ

๐Ÿ”ธ React Router

  • React์—์„œ ํŽ˜์ด์ง€ ์ „ํ™˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • SPA(Single Page Application)์—์„œ URL์— ๋”ฐ๋ผ ํ™”๋ฉด์„ ๋ฐ”๊พธ๋Š” ์—ญํ• !

๋ผ์šฐํŒ…

  • URL์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ฝ˜ํ…์ธ ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ
  • ์˜ˆ์ „ ๋ฐฉ์‹: URL ์š”์ฒญ ์‹œ ์„œ๋ฒ„์—์„œ ์ƒˆ HTML ํŒŒ์ผ ์ „๋‹ฌ
    • example.com/home โ†’ ์„œ๋ฒ„์˜ home.html ํŒŒ์ผ ์š”์ฒญ
    • example.com/about โ†’ ์„œ๋ฒ„์˜ about.html ํŒŒ์ผ ์š”์ฒญ
  • SPA: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํŽ˜์ด์ง€ ์ „ํ™˜ ์—†์ด ํ™”๋ฉด๋งŒ ๊ต์ฒด (๋น ๋ฆ„!)

ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ผ์šฐํŒ…

React ๊ฐ™์€ SPA์—์„œ ๋ผ์šฐํŒ…์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ƒˆ ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•˜์ง€ ์•Š๊ณ , ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๊ฐ€ URL๊ณผ ํ™”๋ฉด์„ ์ œ์–ดํ•˜๋Š” ๋ฐฉ์‹

๋™์ž‘ ์›๋ฆฌ

  1. ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ: ํ•„์š”ํ•œ ๋ชจ๋“  JS ํŒŒ์ผ์„ ํ•œ ๋ฒˆ์— ๋‹ค์šด๋กœ๋“œ
  2. URL์ด ๋ฐ”๋€” ๋•Œ:
    • ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด HTML์„ ์š”์ฒญ โŒ
    • ๋ธŒ๋ผ์šฐ์ €์˜ History API (pushState, replaceState)๋ฅผ ์‚ฌ์šฉํ•ด URL๋งŒ ๋ฐ”๊ฟˆ
    • React Router๊ฐ€ URL์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ต์ฒดํ•จ
  3. ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ™”๋ฉด ๊นœ๋นก์ž„ ์—†์ด ๋น ๋ฅธ ์ „ํ™˜์ด ๊ฐ€๋Šฅ!

์„ค์น˜

1
npm i react-router-dom

๊ตฌํ˜„ ๋ฐฉ์‹

1๏ธโƒฃ createBrowserRouter + <RouterProvider>

1
2
// router์— ๊ด€๋ จ๋œ๊ฑฐ๋ฅผ ํ•œ๊ณณ์— ๋ชจ์•„๋‘๋Š”๊ฒŒ ์ข‹์Œ
import { createBrowserRouter, RouterProvider } from "react-router-dom";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// routes/index.jsx
const router = createBrowserRouter([
  {
    element: <Default />,
    children: [
      {
        path: "/",
        element: <MainPage />,  // ๋ถ€๋ชจ
        children: [{ path: "", element: <MainSub1Page /> }], // ์ž์‹ ํŽ˜์ด์ง€ ๋ชฉ๋ก
      },
      { path: "/about", element: <AboutPage /> },
    ],
  },
  { path: "*", element: <NotFound /> },
]);

export default function Router() {
  return <RouterProvider router={router} />;
}

โœ… ํŠน์ง•:

  • React Router v6.4 ์ด์ƒ๋ถ€ํ„ฐ ์ง€์›ํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฉ์‹
  • JS ๊ฐ์ฒด๋กœ ๋ผ์šฐํ„ฐ ๊ตฌ์„ฑ โ†’ ์œ ์ง€๋ณด์ˆ˜ ์‰ฌ์›€
  • ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ, ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋“ฑ ๊ธฐ๋Šฅ ํ™•์žฅ์— ์œ ๋ฆฌ
  • ์ข€ ๋” ๊ตฌ์กฐ์ ์ด๊ณ  ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉ

    createBrowserRouter โž” RouterProvider๋กœ ์—ฐ๊ฒฐ

2๏ธโƒฃ <BrowserRouter> + <Routes>

1
2
3
4
5
<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
  </Routes>
</BrowserRouter>

โœ… ํŠน์ง•:

  • ์˜›๋‚ ๋ถ€ํ„ฐ ์“ฐ๋˜ ์ „ํ†ต์ ์ธ ๋ฐฉ์‹
  • JSX ์•ˆ์—์„œ ์ง์ ‘ <Route>๋กœ ์ž‘์„ฑ
  • ๊ฐ„๋‹จํ•œ ํ”„๋กœ์ ํŠธ์— ์ ํ•ฉ

<BrowserRouter> โž” <Routes> โž” <Route>๋กœ ํŠธ๋ฆฌ ๊ตฌ์กฐ


<Link> ์ปดํฌ๋„ŒํŠธ๋Š” ํŽ˜์ด์ง€ ์ „์ฒด๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜์ง€ ์•Š๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ ํ™”๋ฉด ์ „ํ™˜

1
import { Link } from 'react-router-dom'
1
<Link to="/about">About</Link>
  • <NavLink> ์ปดํฌ๋„ŒํŠธ๋Š” ํ˜„์žฌ ๊ฒฝ๋กœ์™€ ์ผ์น˜ํ•  ๋•Œ ์ž๋™์œผ๋กœ active ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€
    • active๊ฐ€ ๋ถ™์–ด์„œ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ํŽธํ•จ
1
2
3
4
5
<NavLink
  to="/"
  className={({ isActive }) => (isActive ? styles.active : '')}>
  Home
</NavLink>

day52


<Outlet /> : ์ž์‹ ๋ผ์šฐํŠธ ํ‘œ์‹œ ์ž๋ฆฌ

  • children์ด ๋“ค์–ด๊ฐˆ ์œ„์น˜๋ผ๋Š”๊ฒƒ์„ ์„ค์ •
    • ์ž์‹ ๋ผ์šฐํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์ž๋ฆฌ
  • ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์•ˆ์— <Outlet />์„ ๋„ฃ์œผ๋ฉด, ํ˜„์žฌ ๋งค์นญ๋œ ์ž์‹ ๊ฒฝ๋กœ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ‘œ์‹œ๋จ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React from "react";
import { Outlet } from "react-router-dom";
import Footer from "../../components/Footer";
import Header from "../../components/Header";

const Default = () => {
  return (
    <>
      <Header />
      <Outlet /> {/* ์—ฌ๊ธฐ์— ์ž์‹ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋จ */}
      <Footer />
    </>
  );
};

export default Default;

์‹ค๋ฌด์—์„œ ๋งŽ์ด ์“ฐ๋Š” ํด๋” ๊ตฌ์กฐ

1
2
3
4
5
6
7
8
9
src/
โ””โ”€โ”€ pages/
    โ””โ”€โ”€ AboutPage/
        โ”œโ”€โ”€ AboutPage.jsx
        โ”œโ”€โ”€ AboutPage.module.css
        โ”œโ”€โ”€ useAboutData.js
        โ”œโ”€โ”€ AboutService.js
        โ””โ”€โ”€ components/
            โ””โ”€โ”€ AboutCard.jsx
  • ๊ฐ ํŽ˜์ด์ง€๋ณ„๋กœ ํด๋” ๊ด€๋ฆฌ โ†’ ๊ธฐ๋Šฅ ํ™•์žฅ, ์œ ์ง€๋ณด์ˆ˜ ํŽธํ•จ
  • ์Šคํƒ€์ผ, API, ์ปดํฌ๋„ŒํŠธ๊นŒ์ง€ ํ•œ ํด๋”์— ๋ชจ์Œ

๐Ÿ”ธ swiper

์Šคํƒ€์ผ์— ๊ตฌ์กฐ์  ์ ‘๊ทผ

์Šฌ๋ผ์ด๋” ๋‚ด๋ถ€ ๊ตฌ์กฐ๊ฐ€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜ํ•ด ์ œ์–ด๋˜๋Š” ๊ฒฝ์šฐ, ์ง์ ‘ ํด๋ž˜์Šค๋ฅผ ์ค„ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ตฌ์กฐ์ ์œผ๋กœ ์ ‘๊ทผํ•ด ์Šคํƒ€์ผ์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•จ.

1
2
3
.slider > div > div {
  background-color: #9f9f9f;
}

๐Ÿ’ก Swiper๋Š” ๋‚ด๋ถ€ ๊ตฌ์กฐ๊ฐ€ .swiper > .swiper-wrapper > .swiper-slide์ฒ˜๋Ÿผ ๊ตฌ์„ฑ๋จ

day52

onSwiper + useRef๋กœ Swiper ์ง์ ‘ ์กฐ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•

๐Ÿ’ก ์–ธ์ œ ์“ฐ๋‚˜?

  • Swiper์˜ ๊ธฐ๋ณธ ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ์ด ์•„๋‹Œ, ๋‚ด๊ฐ€ ๋งŒ๋“  ์ปค์Šคํ…€ ๋ฒ„ํŠผ์œผ๋กœ ์Šฌ๋ผ์ด๋“œ๋ฅผ ๋„˜๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ
  • ๋˜๋Š” ์Šฌ๋ผ์ด๋“œ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์ œ์–ดํ•˜๊ณ  ์‹ถ์„ ๋•Œ

๐Ÿง  ํ๋ฆ„ ์š”์•ฝ

๋‹จ๊ณ„์„ค๋ช…
1. useRef()Swiper ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ด์•„๋‘˜ ๋นˆ ๋ณ€์ˆ˜(๊ทธ๋ฆ‡) ์ƒ์„ฑ (null๋กœ ์‹œ์ž‘)
2. onSwiperSwiper๊ฐ€ ์ฒ˜์Œ ๋งŒ๋“ค์–ด์งˆ ๋•Œ, ์ธ์Šคํ„ด์Šค๋ฅผ ref.current์— ์ €์žฅ
3. ๋ฒ„ํŠผ ํด๋ฆญref.current.slidePrev() ๋˜๋Š” .slideNext() ํ˜ธ์ถœํ•ด์„œ ์Šฌ๋ผ์ด๋“œ ์ œ์–ด
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useRef } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";

export default function CustomSwiper() {
  const swiperRef = useRef(null); // Swiper ์ธ์Šคํ„ด์Šค ๋‹ด์„ ref ์ƒ์„ฑ

  return (
    <div>
      <Swiper
        // Swiper๊ฐ€ ์ค€๋น„๋˜๋ฉด ๊ทธ ์ธ์Šคํ„ด์Šค๋ฅผ swiperRef.current ์— ๋‹ด๊ธฐ
        onSwiper={(swiper) => (swiperRef.current = swiper)} 
        slidesPerView={1}
        spaceBetween={10}
      >
        <SwiperSlide>Slide 1</SwiperSlide>
        <SwiperSlide>Slide 2</SwiperSlide>
        <SwiperSlide>Slide 3</SwiperSlide>
      </Swiper>

      {/* ์ปค์Šคํ…€ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ, ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•ด์„œ ์Šฌ๋ผ์ด๋“œ๋ฅผ ์ œ์–ด */}
      <button onClick={() => swiperRef.current.slidePrev()}>Prev</button>
      <button onClick={() => swiperRef.current.slideNext()}>Next</button>
    </div>
  );
}

๐Ÿ›  Swiper ์ธ์Šคํ„ด์Šค์—์„œ ์“ธ ์ˆ˜ ์žˆ๋Š” ๋Œ€ํ‘œ ๋ฉ”์„œ๋“œ

๋ฉ”์„œ๋“œ์„ค๋ช…
slideNext()๋‹ค์Œ ์Šฌ๋ผ์ด๋“œ๋กœ ์ด๋™
slidePrev()์ด์ „ ์Šฌ๋ผ์ด๋“œ๋กœ ์ด๋™
slideTo(index)ํŠน์ • ์ธ๋ฑ์Šค๋กœ ์ด๋™
autoplay.start()์ž๋™ ์žฌ์ƒ ์‹œ์ž‘
autoplay.stop()์ž๋™ ์žฌ์ƒ ์ •์ง€

๐Ÿ”ธ useRef

  • ๋ฆฌ๋ Œ๋”๋ง ์—†์ด ๊ฐ’์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” React์˜ ๋น„๋ฐ€ ์ƒ์ž
  • ๊ฐ’์ด ๋ฐ”๋€Œ์–ด๋„ ํ™”๋ฉด์ด ์•ˆ ๋ฐ”๋€œ (โ†’ useState์™€ ๋‹ค๋ฆ„)
  • ref.current๋กœ ๊ฐ’ ์ฝ๊ณ , ์“ธ ์ˆ˜ ์žˆ์Œ

๋Œ€ํ‘œ์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€

์ƒํ™ฉ์˜ˆ์‹œ
DOM ์ง์ ‘ ์ œ์–ด๋ฒ„ํŠผ ํด๋ฆญ ์‹œ input์— focus ์ฃผ๊ธฐ ๋“ฑ
ํƒ€์ด๋จธ / interval ID ์ €์žฅsetTimeout, setInterval ๊ด€๋ฆฌ
Swiper ๊ฐ™์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ธ์Šคํ„ด์Šค ์ €์žฅswiperRef.current.slideNext()
๋ฆฌ๋ Œ๋”๋ง ์—†์ด ๊ฐ’ ์œ ์ง€์ด์ „ props ๊ธฐ์–ต, ์นด์šดํ„ฐ ์ถ”์  ๋“ฑ

ํ•ต์‹ฌ ํŠน์ง•

โœ… 1. ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ ์•ˆ ํ•จ

  • ref.current = ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ”๋„ ํ™”๋ฉด์—” ์˜ํ–ฅ ์—†์Œ

โœ… 2. ๋ Œ๋”๋ง ๊ฐ„ ๊ฐ’์ด ์œ ์ง€๋จ

  • ์ผ๋ฐ˜ ๋ณ€์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋˜๋ฉด ์ดˆ๊ธฐํ™”๋˜์ง€๋งŒ
    ref๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‚ด์•„์žˆ๋Š” ๋™์•ˆ ๊ฐ’์ด ์œ ์ง€๋จ

โœ… 3. DOM ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ

1
2
3
const inputRef = useRef();
<input ref={inputRef} />;
inputRef.current.focus(); // DOM ์ง์ ‘ ์ œ์–ด

โ—์ฃผ์˜ํ•  ์ 

  1. ๋ Œ๋”๋ง ์ค‘์—๋Š” ref.current๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ฐ์ง€ ๋งˆ์„ธ์š”
    • ๋ Œ๋”๋ง ์ค‘ ref.current ๋ณ€๊ฒฝ์€ React ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ์›์น™์„ ๊นธ
    • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋‚˜ useEffect ์•ˆ์—์„œ ์‚ฌ์šฉํ•ด์•ผ ์•ˆ์ „
  2. UI์— ๋ณด์—ฌ์ค„ ๊ฐ’์—” useState๋ฅผ ์“ฐ์„ธ์š”
    • ref๋Š” ํ™”๋ฉด์— ๋ฐ˜์˜๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ!
  3. ์ดˆ๊ธฐํ™”๊ฐ€ ๋ฌด๊ฑฐ์šด ๊ฐ’์€ ์กฐ๊ฑด๋ถ€ ์ดˆ๊ธฐํ™”
    1
    2
    3
    
    if (ref.current === null) {
      ref.current = new VideoPlayer();
    }
    

useState vs useRef

ํ•ญ๋ชฉuseStateuseRef
๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋ฆฌ๋ Œ๋”๋ง?โญ•๏ธโŒ
ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๊ฐ’?โญ•๏ธโŒ
DOM ์ ‘๊ทผ ๊ฐ€๋Šฅ?โŒโญ•๏ธ
๋ Œ๋”๋ง๊ณผ ์ƒ๊ด€์—†๋Š” ๊ฐ’ ์ €์žฅโŒโญ•๏ธ

END

This post is licensed under CC BY 4.0 by the author.