한입 크기로 먹기 힘든 Next JS Week 3

robin Han·2025년 9월 11일
2

App router

동적 경로로 파라미터 가져오기

export default async function Page({params}: {params : Promise<{id: string}>}){
	// 기본적으로 params를 가져올때 Promise 형태로 가져오기 때문에 async로 사용학 된다.
  const {id} = await params;
}

1. [id] → 단일 동적 세그먼트

특정 한 개 값을 받는 경우.

예: /posts/[id]/page.tsx

// app/posts/[id]/page.tsx
import { useParams } from "next/navigation";

export default function PostPage() {
  const params = useParams(); // { id: '123' }
  return <h1>Post {params.id}</h1>;
}

/posts/123 → params.id = "123"

2. [...id] → Catch-all(필수) 경로

여러 개의 path 세그먼트를 배열로 받음.

최소 1개 이상 필요.

// app/docs/[...id]/page.tsx
import { useParams } from "next/navigation";

export default function DocsPage() {
  const params = useParams(); // { id: ['a', 'b', 'c'] }
  return <div>{JSON.stringify(params)}</div>;
}

/docs/a/b/c → params.id = ["a", "b", "c"]

/docs ❌ (접속 불가, 최소 1개 세그먼트 필요)

3. [[...id]] → Optional Catch-all 경로

[...id]와 비슷하지만, 세그먼트가 없어도 허용됨.
따라서 기본 페이지와 동적 경로를 하나의 파일에서 처리 가능.

// app/shop/[[...id]]/page.tsx
import { useParams } from "next/navigation";

export default function ShopPage() {
  const params = useParams(); // { id?: string[] }
  return <div>{JSON.stringify(params)}</div>;
}

/shop → params.id === undefined
/shop/clothes → params.id = ["clothes"]
/shop/clothes/men/shirts → params.id = ["clothes", "men", "shirts"]

Search Params 같은경우는 훅형태의 useParams 같은걸 사용하게 되는데 어떤 차이가 있을까?

  • SEO나 초기 데이터 패칭 → searchParams (서버 props)
  • 클라이언트에서 URL 변화를 즉시 반영 → useSearchParams

Layout

/app
---/searchpage
-------/page.tsx
-------/layout.tsx -> search 경로에 있는 모든 페이지의 layout

모든 하위의 경로에도 layout이 적용되게 되는데, 이때 하위 layout이 존재한다면 중첩이 된다.

같은 레이아웃을 특정 페이지만 적용하고 싶다면?

  • route group : 경로 상에 적용이 되지 않음
    (with-header) 라는 폴더에 searchPage 폴더와 mypage 폴더를 넣어서 사용하고 layout을 지정해주면 해결됨

Sever Component

어떤 문제가 있어서 ServerComponent가 생겼는지?

"모든 걸 브라우저에 보내지 말고, 서버가 잘 할 수 있는 일은 서버에서 처리하자"

하이드레이션에 필요한 컴포넌트들을 JSBundle에 넣지 않고 server rendering (한번만 실행) 하기위해서
따라서 클라이언트 컴포넌트를 사용해야할때만 사용 'use client' 명시 없으면 server component로 실행이 된다.

  • 서버 컴포넌틑 브라우저에서 실행될 코드가 포함되면 안된다 (useState, useEffect, event 불가능)
  • 클라이언트 컴포넌트는 클라이언트에서만 실행되지 않는다 (서버측에서 사전 랜더링+ 클라이언트 랜더링, 총 2번)
  • 클라이언트 컴포넌에서 서버 컴포넌트를 import할수없다 (즉 부모가 클라이언트 컴포넌트가 자식이 서버컴포넌트 자식을 가지지 못함) 하지만 서버 컴포넌트를 클라이언트 컴포넌트로 자동으로 변환시킴
  • 서버 컴포넌트에서 클라이언트 컴포넌트에세 직렬화 되지않는 Props는 전달 불가하다

    직렬화(Serialization)란 데이터 구조나 객체를 저장하거나 전송할 수 있는 형태로 변환하는 과정을 말해요.

  • 함수는 직렬화가 불가능함
RSC Payload 란 ? 

RSC Payload는 Server Component가 클라이언트로 보내는 데이터 패키지
서버에서 렌더링한 컴포넌트 정보를 그대로 직렬화해서 클라이언트로 전달
클라이언트에서는 이 데이터를 기반으로 React가 UI를 재구성
즉, HTML 전체를 보내는 게 아니라, 최소한의 정보(JSON 형태)만 보내는 방식

Static Pages -> RSC Payload + JS Bundle
Dynamic Pages -> RSC Payload (실제로 페이지를 실행할때 불러옴) JS Bundle이 prefetch 되지않음

!! App router에서는 useRouter() 에 query 속성이 없기 때문에 쿼리 스트링을 가져오기 위해서는 useSearchParams 훅이 필요함

어떻게 Page()라는 컴포넌트 함수는 params라는 파라미터를 가질수있는지 ?

  • page.tsx를 기준으로 해당 함수에 주입

2개의 댓글

comment-user-thumbnail
2025년 9월 11일

왜 먹기 힘든가요?

1개의 답글