Dandy Now!
  • [Next.js] 인프런 강의 "Next.js 필수 개발 가이드 3시간 완성!" 정리(섹션 2: 라우팅과 네비게이션)
    2024년 02월 07일 09시 38분 21초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    섹션 2: 라우팅과 내비게이션

    📌 다이내믹 라우트(Dynamic Routes)

    // src/app/users/[id]/photo/[photoId]/page.tsx
    
    import React from "react";
    
    interface Props {
      params: { id: number; photoId: number };
    }
    
    // props에 params 속성이 존재하고 params에 동적 라우트 속성이 존재한다.
    // props.params.id 혹은 props.params.photoId로 동적 라우트 path variable의 값에 접근할 수 있다.
    // props.params.photoId를 destructuring하여 { params: { photoId } }로 표현할 수 있다.
    
    const PhotoPage = ({ params: { id, photoId } }: Props) => { // props를 생략해도 동작하지만 id, photoId 속성을 이용할 수 없게 된다.
      return ( 
        <div>
          PhotoPage {id} {photoId}
        </div>
      );
    };
    
    export default PhotoPage;

     

    📌 Catch-all Segments

    // src/app/profile/[...username]/page.tsx
    
    import React from "react";
    
    interface Props {
      params: {
        username: string[];
      };
    }
    
    const ProfilePage = (props: Props) => {
      console.log(props);
      /*
      출력 결과
      {
        params: { username: [ 'kim', 'post1', 'post1-1', 'post1-1-1' ] },
        searchParams: {}
      }
      */
      return <div>ProfilePage</div>;
    };
    
    export default ProfilePage;

     

    📌 Optional Catch-all Segments

     

    📌 QueryString

    • "http://localhost:3000/products/computer?sortOrder=name"에서 ?(start of parameters)에서 부터 시작하는 query string을 다룬다.
    // src/app/products/[..slug]/page.tsx
    
    import React from "react";
    
    interface Props {
      params: { slug: string };
      searchParams: { sortOrder: String };
    }
    
    const ProductPage = ({
      params: { slug },
      searchParams: { sortOrder },
    }: Props) => {
      return (
        <div>
          page {slug} {sortOrder}
        </div>
      );
    };
    
    export default ProductPage;

     

    📌 QueryString 기반 정렬

    npm i fast-sort
    • page.tsx에서 searchParams를 자식 컴포넌트(userTable.tsx)의 props로 넘겨 정렬의 키워드로 사용
    // src/app/users/page.tsx
    
    import React from "react";
    import UserTable from "./UserTable";
    
    interface Props {
      searchParams: { sortOrder: string };
    }
    
    const UsersPage = async ({ searchParams: { sortOrder } }: Props) => {
      return (
        <div>
          <h1>This is UESRPAGE</h1>
          <p>{new Date().toLocaleTimeString()}</p>
          <UserTable sortOrder={sortOrder} /> // query string을 props로 넘김
        </div>
      );
    };
    
    export default UsersPage;
    // src/app/users/UserTable.tsx
    
    import { sort } from "fast-sort";
    import Link from "next/link";
    import React from "react";
    
    interface User {
      id: number;
      name: string;
      email: string;
    }
    
    interface Props {
      sortOrder: string;
    }
    
    const UserTable = async ({ sortOrder }: Props) => {
      const res = await fetch("https://jsonplaceholder.typicode.com/users");
      const users: User[] = await res.json();
    
      // fast-sort를 이용한 정렬
      // asc : 오름차순 정렬
      const sortedUsers = sort(users).asc(
        sortOrder === "email" ? (user) => user.email : (user) => user.name
      );
    
      return (
        <>
          <table>
            <thead>
              <tr>
                {/* Link 컴포넌트를 이용한 네비게이션 처리 */}
                <th>
                  <Link href="/users?sortOrder=name">Name</Link>
                </th>
                <th>
                  <Link href="/users?sortOrder=email">Email</Link>
                </th>
              </tr>
            </thead>
            <tbody>
              {/* 정렬된 배열 이용 */}
              {sortedUsers.map((user) => (
                <tr key={user.id}>
                  <td>{user.name}</td>
                  <td>{user.email}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </>
      );
    };
    
    export default UserTable;

     

    📌 Layout

    • app 폴더에 작성하면 모든 컴포넌트에 공통 적용(Global Layout)
    • 각 폴더에 적용하면 해당 폴더 범위만 적용(Local Layout)
    • page.tsx는 반드시 있어야 하지만 layout.tsx는 생략 가능
    // src/app/admin/layout.tsx
    // Local layout 예제이다.
    
    import React from "react";
    
    interface Props {
      children: React.ReactNode; // ReactNode = 리엑트 컴포넌트
    }
    
    // props children에 page.tsx가 들어온다.
    const AdminLayout = ({ children }: Props) => {
      return (
        <div className="flex">
          <aside className="bg-slate-200 p-5 mr-5">Admin Sidebar</aside>
          <div>{children}</div>
        </div>
      );
    };
    
    export default AdminLayout;

     

    📌 Programmatic Navigation

    // src/app/users/new/page.tsx
    
    "use client";
    import { useRouter } from "next/navigation"; // 추가
    import React from "react";
    
    const NewUser = () => {
      const router = useRouter(); // 추가
    
      return (
        // 서버 컴포넌트는 클릭 이벤트 처리 안됨. CSR로 변경해야 함.
        // CSR에서의 라우팅 처리를 Programmatic Navigation이라고 한다.
        <button className="btn btn-primary" onClick={() => router.push("/users")}> // router.push()
          Create
        </button>
      );
    };
    
    export default NewUser;

    🤔Next.js 공식 문서에서는 찾을 수 없는 용어였음. Vue.js 공식 문서에서 용어 발견하였음. https://v3.router.vuejs.org/guide/essentials/navigation.html

    728x90
    반응형
    댓글