검색

검색어를 입력하세요

2026年 06月 03日

2026-06-03

흐릿해졌던 Next.js 개념 정리하기

Route Group (folderName)

https://nextjs.org/docs/app/api-reference/file-conventions/route-groups

라우트를 그룹화해야 할 때 활용한다. 기능별로 분류가 필요하다던가…

app
├── (shop)
│   ├── cart
│   │   └── page.tsx
│   └── product
│       └── page.tsx
└── (user)
    ├── mypage
    │   └── page.tsx
    └── privacy
        └── page.tsx

중요한 것은 Route 그룹은 실제로 path에 드러나지 않기 때문에 path conflict에 주의해야 한다는 것이다.

(marketing)/about/page.jsx(shop)/about/page.jsx 은 동일하게 /about 을 통해서 접근하려고 하기 때문에 에러가 발생한다.

layout.tsx

큰 껍데기. 중첩된다.

layout은 해당 segment와 하위 라우트를 감싸고, navigation 시 상태를 유지할 수 있다.

Route Group마다 root layout을 다르게 두면 그룹 간 이동 시 full page reload가 날 수 있음에 주의해야 한다.

error.tsx, loading.tsx

route segment 단위의 에러 바운더리와 suspense.

물론 컴포넌트 단위로 처리해야 할 경우에는 ErrorBoundary와 Suspense를 직접 쓸 수도 있다.

같은 segment의 layout.tsx에서 발생한 에러는 그 segment의 error.tsx가 잡지 못하고 상위 boundary가 잡는다.

app router의 서버 컴포넌트

App Router의 page와 layout은 기본적으로 Server Component이며 서버에서 실행된다

useState, useEffect, 이벤트 핸들러처럼 브라우저에서의 상호작용이나 상태 관리가 필요한 기능은 Client Component에서만 사용할 수 있다. 이런 기능이 필요한 컴포넌트 파일에는 'use client'를 선언해서 Client Component boundary를 만들어야 한다.

server streaming

https://nextjs.org/docs/app/guides/streaming#the-static-shell

컴포넌트를 준비되는 대로 렌더링할 수 있음

서버사이드 페치와 tanstack query

https://tanstack.com/query/latest/docs/framework/react/guides/ssr

https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#streaming-with-server-components

서버에서 페치해서 직접 주입해주는 느낌. 중요한 것은 HydrationBoundarydehydrate(queryClient)로 주입해줘야 한다는 것이다..! 빼먹으면 서버에서 prefetch한 결과가 클라이언트 Query Cache로 hydrate되지 않아서 클라이언트의 useQuery가 별도 fetch를 다시 수행할 수 있다. 그냥 데이터 두번 불러오는 사람 됨

서버사이드 페치와 await

export default function Page() {
  // await 없이 Promise를 바로 생성 → 두 요청이 동시에 시작됨
  const userPromise = getUser();
  const purchasesPromise = getPurchases();

  return (
    <>
      <Suspense fallback={<p>Loading user...</p>}>
        <UserInfo userPromise={userPromise} />
      </Suspense>
      <Suspense fallback={<p>Loading purchases...</p>}>
        <PurchaseList purchasesPromise={purchasesPromise} />
      </Suspense>
    </>
  );
}

핵심은 Page 컴포넌트에서 await 없이 Promise를 변수에 담아서 자식 컴포넌트에 prop으로 내려주는 것.

이렇게 하면 두 요청이 동시에 시작되고, 각 자식 컴포넌트가 await해서 각자의 Suspense boundary에서 독립적으로 로딩/렌더링된다.

Page에서 await Promise.all하면 둘 다 끝날 때까지 해당 render가 막히고, 각각의 Suspense boundary에서 먼저 끝난 것부터 streaming하기 어렵다

하이드레이션 우선순위

https://nextjs.org/docs/app/guides/streaming#inp-interaction-to-next-paint

사용자가 인터랙션하는 컴포넌트를 우선적으로 하이드레이션한다.

메모이제이션

React 19.2

리액트 컴파일러에서 메모이제이션 내장.

설정을 통해서 프로젝트에서 Compiler를 활성화해야 한다.

이렇게 최적화 기능들이 많이 생겼는데,,, 실제로 프로젝트를 진행할 때는 잘 생각을 못하게 되서 항상 쓰던 대로 쓰게 되는 것 같다.

사이드프로젝트를 고민해봐야겠다.