9.24(ํ™”)

1. ์ฝ”๋“œ์Šคํ”Œ๋ฆฌํŒ… - lazyLoading

๐Ÿ‘‰ ์ฝ”๋“œ์Šคํ”Œ๋ฆฌํŒ…์€ router์—์„œ import๋˜๋Š” ๋ถ€๋ถ„์„ ์ „์ฒด import๊ฐ€ ์•„๋‹ˆ๋ผ ๋ณ€์ˆ˜๋กœ ๋”ฐ๋กœ ๋นผ๋‚ด์–ด ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ์ ‘๊ทผํ•  ๋•Œ๋งŒ ๋กœ๋“œ๋˜๋„๋ก ํ•˜๋Š” ๊ธฐ๋ฒ•

const MainPage = lazy(() => import("../pages/MainPage"))

๐Ÿ‘‰ ๋ ˆ์ด์ง€๋กœ๋”ฉ์€ Suspense์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๋”ฉ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ฃผ๋Š” ๊ธฐ๋ฒ•

const Loading = <LoadingComponent/>

element: <Suspense fallback={Loading}><MainPage/></Suspense>

๐Ÿ‘‰ fallback์„ ํ†ตํ•ด์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ๋ ๋•Œ๋™์•ˆ Loading์ปดํฌ๋„ŒํŠธ๋ฅผ ๋กœ๋“œ




2. Outlet, children

๐Ÿ‘‰ MainRouter์—์„œ todo์˜ ์˜ˆ์ œ๋กœ ์„ค๋ช…์„ ํ•˜์ž๋ฉด, url์— ์ž…๋ ฅ์‹œ์— todo๋ฅผ ์ž…๋ ฅํ–ˆ์„๋•Œ listํ™”๋ฉด์ด ์ถœ๋ ฅ๋˜์–ด์•ผํ•˜๊ณ  ์ดํ›„์— todo์™€ ๊ด€๋ จ๋œ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„  todo/list, todo/input๊ณผ ๊ฐ™์ด url ์„ค๊ณ„๊ฐ€ ์ด๋ฃจ์–ด์ ธ์•ผํ•œ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉ๋˜๋Š”๊ฒƒ์ด children์ธ๋ฐ

 path:"/todo",
        element: <Suspense fallback={Loading}><TodoIndex/></Suspense>,
        children: [
            {
                path:"list",
                element: <Suspense fallback={Loading}><TodoList/></Suspense>
            },
            {
                path:"",
                element: <Navigate to='list' replace={true}></Navigate> // ์ฃผ์†Œ์ฐฝ์— todo๋ฅผ ์ž…๋ ฅํ•˜๋ฉด listPage๋กœ redirect
            }
        ]

๐Ÿ‘‰ ํ•ด๋‹น์ฝ”๋“œ์—์„œ๋Š” todo์˜ ํ•˜์œ„ํŽ˜์ด์ง€์˜ ๊ฐœ๋…์œผ๋กœ IndexํŽ˜์ด์ง€ ์•„๋ž˜์— children์„ ์‚ฌ์šฉํ•˜์—ฌ path,element๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์—ˆ๊ณ , list๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” todo๋ฅผ ์ž…๋ ฅํ–ˆ์„๋•Œ todoํ™”๋ฉด์ด ๋‚˜์˜ค์ง€์•Š๊ณ  todoListํ™”๋ฉด์ด ์ถœ๋ ฅ๋˜๋„๋ก Navigate๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

๋˜ํ•œ Indexpage์—์„œ๋Š” Outletํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€์— ๋‹ด๊ธฐ๋Š” ๋‚ด์šฉ๋“ค์ด Outlet์•„๋ž˜์— ์ž‘์„ฑ๋˜๋„๋ก ์„ค๊ณ„ํ•œ๋‹ค.

import {Outlet} from "react-router-dom";
import BasicLayout from "../../layouts/BasicLayout.tsx";


function TodoIndex() { // Outlet์€ mainRouter์˜ children
    return (
        <BasicLayout>
            <div>Todo Index Page</div>
        <div>
            <Outlet></Outlet>
        </div>
        </BasicLayout>
    );
}

export default TodoIndex;




3. layout

๐Ÿ‘‰ layout์€ ๊ฐœ๋ฐœ ์ดˆ๊ธฐ๋‹จ๊ณ„์— ๊ฐ„๋žตํ•˜๊ฒŒ ํ‹€์„ ์žก์•„์ค˜์•ผ ๋‚˜์ค‘์— ๋‹ค ๋’ค์ง‘์–ด ์—Ž๋Š” ์‚ฌํƒœ๊ฐ€ ๋ฐœ๋ฐœํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ„๋žตํ•˜๊ฒŒ ๋ฏธ๋ฆฌ ํ•ด๋†“๋Š”๋‹ค.

๐Ÿ‘‰ layout ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณ„๋„๋กœ ์ƒ์„ฑํ•ด์„œ BasicLayout๊ณผ ๊ฐ™์€ tsxํŒŒ์ผ์„ ํƒœ๊ทธ์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.

๐Ÿ‘‰ ์ด๋•Œ layoutํŒŒ์ผ์—์„œ๋Š” ํ•˜์œ„ ํŽ˜์ด์ง€๋“ค์ด ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ์ธ children์˜ ๊ณต๊ฐ„์„ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋‘ฌ์•ผํ•œ๋‹ค.




4. useSearchParams / useParams

๐Ÿ‘‰ ํ•ด๋‹น ํ‚ค์›Œ๋“œ๋“ค์€ ๋‘˜๋‹ค url์—์„œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•๋“ค์ด๋‹ค
๐Ÿ“Œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํ‚ค์›Œ๋“œ๋Š” Router๋Š” url ์ •๋ณด๊ฐ€ ์žˆ๊ณ , url์—์„œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์•ผํ•˜๋Š”๊ฒƒ.

4-1. useSearchParams

import {useState} from "react";
import {useSearchParams} from "react-router-dom";

function TodoListComponent() {

    const [query, setQuery] = useSearchParams(); // url์˜ page,size์™€ ๊ฐ™์€ ์ •๋ณด๋ฅผ ์ถ”์ถœํ• ์ˆ˜์žˆ๋‹ค.
    
    const changePage = (pageNum:number) => {
        query.set("page",""+pageNum); // ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์˜ page๊ฐ’์— pageNum์„ ๋„ฃ์–ด
        setQuery(query) //์ฟผ๋ฆฌ๋ฅผ ์ ์šฉ์‹œ์ผœ์ค€๋‹ค.
    }

    const page: number = Number(query.get("page")) || 1
    const size: number = Number(query.get("size")) || 10

    console.log(size);
    console.log(page);

    return (
        <div>
            <div>Todo List Component</div>
        </div>
    );
}

export default TodoListComponent;

๐Ÿ‘‰ ์—ฌ๊ธฐ์„œ query๋Š” ํ˜„์žฌ url์„ ๋‚˜ํƒ€๋‚ด๊ณ , ํ˜„์žฌ query์˜ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ๋‚ด๋ถ€์— page๋ถ€๋ถ„์„ pageNum์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ setQuery(์ ์šฉ)ํ•˜๋Š”๊ฒƒ์ด๋‹ค.

๐Ÿ‘‰ http://localhost:5173/todo/list?page=11&size=2 ์ด์™€ ๊ฐ™์€ url์—์„œ page,size์˜ ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•.

4-2. useParams

import {useParams} from "react-router-dom";

function TodoReadPage() {

    const {mno} = useParams()

    console.log(mno)

    return (
        <div>
            <div>Todo Read Page</div>
        </div>
    );
}

export default TodoReadPage;

๐Ÿ‘‰ http://localhost:5173/todo/read/321 ์ด์™€ ๊ฐ™์€ url์—์„œ read์˜ ๊ฐ’์„ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•.

๐Ÿ“Œ ๊ทธ๋ ‡๋‹ค๋ฉด ๋‘๊ฐ€์ง€์˜ ์ฐจ์ด์ ์ด ๋ญ˜๊นŒ?

  • ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด: useSearchParams๋กœ ์ฒ˜๋ฆฌ (์˜ˆ: ?page=11&size=2)
  • ๊ฒฝ๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ: useParams๋กœ ์ฒ˜๋ฆฌ (์˜ˆ: /todo/read/:mno์—์„œ :mno)




5. ํŽ˜์ด์ง€ ์ด๋™

๐Ÿ‘‰ ํŽ˜์ด์ง€ ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•˜์—ฌ ํŽ˜์ด์ง€ ์ด๋™์ด ๋˜๋Š”๊ฒƒ์€ ๋งŽ์€ ๋ฐฉ๋ฒ•๋“ค์ด ์กด์žฌํ•˜์ง€๋งŒ, ๋™์ผํ•œ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„๋•Œ ๋˜ํ•œ ๋ฆฌ๋กœ๋”ฉ์ด๋˜๊ณ  ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ, router์—์„œ๋Š” url query๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ์ž‘๋™์ด ๋˜๋Š” useEffect๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๋˜๋ฉด ์–ด๋– ํ•œ ๊ฐ’ ์ž์ฒด๊ฐ€ ๋™์ผํ•œ ํŽ˜์ด์ง€ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ์ด ๋˜์–ด์•ผํ•œ๋‹ค.

  • refresh ํ† ๊ธ€
    ๐Ÿ‘‰ ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ refresh ํ† ๊ธ€์ด๋‹ค. on/off์™€ ๊ฐ™์€ ๊ฐœ๋…์œผ๋กœ ํŽ˜์ด์ง€๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„๋•Œ ํ† ๊ธ€์ด ์‹คํ–‰๋˜์–ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฐ’์„ ์–ต์ง€๋กœ ๋งŒ๋“œ๋Š”๋ฐฉ๋ฒ•

  • time
    ๐Ÿ‘‰ ๋‘๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ํ˜„์žฌ ์‹œ๊ฐ„์„ url์— ์ง‘์–ด๋„ฃ์–ด ๊ณ„์† ๋ณ€๊ฒฝ๋˜๋Š” url๊ฐ’์„ ๋งŒ๋“œ๋Š”๊ฒƒ์ด๋‹ค.

  • useLocation
    ๐Ÿ‘‰ ๊ฐ€์žฅ ๊น”๋”ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ useLocation ๋‚ด๋ถ€์˜ key์†์„ฑ์„ ์ด์šฉํ•˜๋Š”๋ฐ, key์†์„ฑ์€ url์„ ๋ฆฌ๋กœ๋“œ ํ• ๋•Œ๋งˆ๋‹ค ์ƒ์„ฑ๋˜๋Š” key๊ฐ’์„ ์ด์šฉํ•œ๋‹ค.


    const location = useLocation();

    useEffect(() => {
        setLoading(true)
        getTodoList(page,size).then(data => {
            setPageResponse(data)
            setLoading(false)
        })
    },[query, location.key])

    return (
        <div>
            {loading&& <LoadingComponent/>}
            <div>Todo List Component</div>
            <PageComponent pageResponse={pageResponse}></PageComponent>
        </div>
    );
}

export default TodoListComponent;

๐Ÿ‘‰ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” TodoListComponent๋กœ useLocation์„ ์ด์šฉํ•˜์—ฌ url์„ ํ˜ธ์ถœํ• ๋•Œ๋งˆ๋‹ค location์„ ์ƒ์„ฑํ•ด์ฃผ๊ณ , useEffect์— ๊ฐ์ฒด์—๋Š” query์™€ location.key๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.




6. useNavigate / createSearchParams

๐Ÿ‘‰ useNavigate๋Š” Link์™€ ๋น„์Šทํ•œ๋ฐ, Link๋Š” ์ •์ ์œผ๋กœ ์ž‘์„ฑํ•ด๋†“์€ url์œผ๋กœ ์ด๋™ํ•˜๋Š” ํƒœ๊ทธ์ด๊ณ , useNavigate๋Š” ๋™์ ์œผ๋กœ ๋ณ€๊ฒฝ๋˜์–ด ์ž‘์„ฑ๋œ url๋กœ ์ด๋™ํ•˜๋Š” ํƒœ๊ทธ์ด๋‹ค.

 const navigate = useNavigate();

    const queryStr = createSearchParams({page:page, size:size});
    // page์™€ size์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ์ฟผ๋ฆฌ์ŠคํŠธ๋ง ๋งŒ๋“ค๊ธฐ

    console.log("queryStr====================="+queryStr);

    const moveToRead = (mno:number) => {
        navigate({pathname:`/todo/read/${mno}`, search:`?${queryStr}`})
    }
    // mnoํด๋ฆญ์‹œ ๋™์ ์œผ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒฝ๋กœ๋ฅผ ์ƒ์„ฑ

๐Ÿ‘‰ ํ˜„์žฌ ์ƒํ™ฉ์„ ์„ค๋ช…ํ•˜์ž๋ฉด, list์— ์ถœ๋ ฅ๋˜์–ด์žˆ๋Š” todoList๋ฅผ ์„ ํƒํ–ˆ์„๋•Œ readํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋Š”๋ฐ, ์ด๋™์€ ๋˜์ง€๋งŒ ์ด๋™๋œ ์ดํ›„์— url์— ๊ธฐ์กด์˜ page์™€ size๊ฐ€ ๊ธฐ๋ก์ด ์•ˆ๋˜๋Š” ์ƒํ™ฉ์ด๋‹ค.

๐Ÿ‘‰ ์ด๋Ÿฌํ•œ ๋ถˆ์ƒ์‚ฌ๋ฅผ ๋ง‰๊ธฐ์œ„ํ•ด url์— ์ด์ „์˜ ๋‚ด์šฉ์„ ์ด์–ด๋ถ™ํ˜€์•ผํ•˜๋Š”๋ฐ, ๊ทธ๋•Œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด๊ฐ€ createSearchParams์ด๋‹ค. ํ˜„์žฌ url ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์˜ page์™€ size๋ฅผ ํฌํ•จํ•˜๋Š” url์„ ์ƒ์„ฑํ•˜์—ฌ queryStr์— ๋„ฃ์–ด๋‘๊ณ , ์ด๋™ํ•˜๋ ค๋Š” moveTodRead ๋‚ด๋ถ€์— navigate๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๊ณ , search์—์„œ์˜ ๋ฌผ์Œํ‘œ๋Š” mno๊ฐ€ ์—†์„์ˆ˜๋„ ์žˆ๊ธฐ์— ๋ถ™์ธ๊ฒƒ์ด๊ณ  ์ž‘์„ฑํ•ด๋’€๋˜ url์ธ queyStr์„ ๋„ฃ์–ด์ค€๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€