[Next.js] 2주차_App Router 라우팅 & Proxy

재선·2026년 3월 21일

Next.js

목록 보기
2/8
post-thumbnail

파일 기반 라우팅 심화

Next.js의 가장 큰 특징 중 하나가 파일 기반 라우팅이라는 점이다.

파일 기반 라우팅 (File-based Routing)

평소에 사용하던 React에서는 보통 react-router-dom을 사용해서 라우트를 직접 설정한다.

const myPageRoutes: RouteObject = {
  path: "/mypage",
  element: <MyPageLayout />,
  children: [
    { index: true, element: <Navigate to="info" replace /> },
    { path: "info", element: <MyInfoPage /> },
    { path: "settings", element: <SettingsPage /> },
    { path: "subscription", element: <SubscriptionPage /> },
    { path: "reservations", element: <ReservationPage /> },
    { path: "store", element: <StorePage /> },
  ],
};
  {
    path: "workspace/:workspaceId/settings",
    element: <WorkspaceSetting />,
  },

이런 방식으로 React에서는 경로를 설정하고 그다음에 이동할 페이지를 설정해주는 방식인 react-router-dom을 사용합니다.

근데, Next.js에서는 라우팅을 직접 작성하지 않습니다.

폴더와 파일 구조 자체가 URL 경로가 되는거죠.

예를들어

app
├ posts
│   ├ page.tsx
│   ├ [id]
│   │   └ page.tsx

이렇게 파일 구조가 설정되어있다고 하면, 이런 파일 구조에서는

/posts
/posts/1
/posts/2
/posts/3
/posts/4

이렇게 URL이 만들어집니다. 쉽죠?

Next.js에서 파일구조만으로 URL 경로가 정리되는 방식이기 때문에 라우팅을 알아서 해주기 때문에, 직접 작성할 필요가 없습니다.

라우팅 설정을 직접 작성하지 않아도 되기 때문에, 코드 구조가 훨씬 단순해지고, 폴더 구조 만으로 경로 파악이 훨씬 빨라지는 장점이 있습니다.

즉, 폴더구조 === URL 구조 라고 생각하면돼죠.

Dynamic Routes

Dynamic Routes는 URL의 일부가 동적으로 변하는 Route를 의미합니다.

/mypage/store/1
/mypage/store/2
/mypage/store/3 
/mypage/store/4
/mypage/store/5

내가 직전에 개발진행했던 잇츠파인 프로젝트 코드를 보면, 사장님 flow에서 가게를 관리하기 위해 가게마다의 페이지를 이런식으로 표현했다.

하지만, 이 storeId는 계속 바뀌고 늘어나기 때문에 모든 페이지를 각각 만들수가 없다.

사장님으로 등장하는 고객은 계속 증가하고, 증가한 고객 대비 등록되는 가게 수가 계속해서 증가한다.

그렇기에 Next.js에서는 대괄호 [ ]를 사용해서 Dynamic Route(동적 라우트)를 생성할 수 있다.

예시를 들면,

app/mypage/store/[storeId]/page.tsx

이런방식으로 만들어서 URL의 id 값이 자동으로 전달되도록 할 수 있다.

그리고 페이지 코드에서는

export default function Page({params}: {params: {storeId: number} }) {
	return <div>StoreId: {params.storeId}</div>;
}

이런 방식으로 사용 할 수 있다.

사용자가 /store/6으로 접속한다면, params.storeId = 6으로 전달된다.

Dynamic Routes 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

이건 상세페이지나 사용자마다 다른페이지가 나오는 ID값에 따라 주소가 변할 때 많이 사용된다.

우리가 흔히 아는걸로는, 쇼핑몰페이지에서 가게 마다 페이지가 달라져서 가게에 ID값을 메겨서 구분하는페이지나, 혹은 하나의 가게 안에 여러개의 상품들이 각각의 상세페이지를 갖고있으니, 그 상품 별로 ID값을 주어서 페이지 구분하는 방식 등이 있겠다!!

내 잇츠파인 프로젝트 기준으로는 회원가입한 회원별로 마이페이지 또는, 가게 사장님이 가지고 있는 가게 별로 가게 ID값을 URL 주소에 넣어서 페이지 구분하는 방식 등이 있겠지?

즉, ID 기반으로 된 페이지를 만들 때 거의 필수로 사용하는 라우팅 방식인거다.

Catch-all Routes

Catch-all Routes는 여러 개의 경로를 배열 형태로 받을 수 있는 Route이다.

예시로 쇼핑몰사이트를 보면,

/shop
/shop/fashion
/shop/fashion/women
/shop/fashion/women/shoes
/shop/electronics/laptop

이렇게 경로의 깊이가 계속 달라지는 경우가 있지. 그리고 계속 경로의 깊이 계속 깊어진다는 단점이 있잖아?

이럴때 이제 Next.js에서 Catch-all Routes를 사용할 수 있다!!

app/shop/[...category]/page.tsx

이렇게 […category] 형태로 만들면, 여러개의 경로가 이제 배열의 형태로 전달되는겁니다.

예를 들어서,

/shop/fashion/women

로 접속하게 되면, params.slug 값은 [”fashion”, “women”] 가 전달되는거지.

[…slug]로 여러 경로를 배열로 받으면 depth가 깊어지는거 상관이 없어지는거지. 배열 하나로 다 처리되니까, 하나의 페이지에서 처리가능하고!!

(근데 주의할 점은 저기서 /shop 은 포함 안된다!!! 이거는 못잡으니까!!)

즉, URL 뒤에 뭐가 몇 개 붙을지 모를때, 전부 배열로 받아서 처리하는 라우팅을 Catch-all Routes라고 부르는거지.

Catch-all Routes 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

하위 경로가 몇개든 상관없다. 다 잡아낼수있다. 배열로 들어오니까.

보통은 깊은 경로가 생길때 그걸 계속 깊어지기 보다는 하나의 페이지에서 처리할 수 있을때 유용하게 많이 사용되는 것 같다.

결론적으로, Catch-all Routes는 경로 깊이가 유동적인 페이지를 처리할 때 사용하는 라우팅 방식인거다.

Route Group

Route Group은 URL에는 영향을 주지는 않지만, 폴더를 논리적으로 그룹화하는 기능이다.

Next.js에서는 (폴더명) 형태로 그룹을 만들수 있는데, (auth), (workspace), (store) 이렇게

예를 들어 설명해보면

app
├ (auth)
│   ├ login
│		│ 	└ page.tsx
│   ├ signup
│   │   └ page.tsx

이렇게 구조를 만들수있는거지

근데 이제 실제로는 /login , /signup 이렇게 URL이 구성되어있다.

이게 근데 왜 필요해?

왜냐면, Next.js는 폴더 = URL 구조 잖아.

예를 들어 app/auth/login/page.tsx가 URL은 /auth/login 이잖아.

근데 이제 우리는 /auth는 굳이 필요없고, /login으로 url을 만들고싶어. 근데 (auth) 안에는 넣고싶어.

그럴때! 이 Route Group을 사용하는거지.

이 Route Group을 사용해서, (auth)로 정리하면, 실제 URL에서는 /auth 는 사라지고 /login만 남으니까.

  • 일반 폴더 ⇒ URL에도 반영되는 폴더
  • Route Group ⇒ 개발자만 보는 폴더. 약간 숨김처리 느낌으로

login이랑 signup이랑 auth라는 공통점으로 묶을 수 있으니까, 그걸로 묶어놓아서, 폴더구조만 보고도 아! 여기가 회원 관련된 페이지들이 모여있는 곳이구나 하고 한번에 알 수 있도록!!

URL에는 영향을 주지 않고, 폴더만 정리하는 기능!

Route Group 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

  1. 기능별로 그룹 나눌때

    (auth)
    (workspace)
    (store)

    이렇게 폴더 구조만 봐도, 아 여기가 인증이구나. 워크스페이스구나. 스토어 구나 하고 개발자가 코드 파악을 빠르게 할 수 있겠지?

  2. 레이아웃 분리할 때

    app
    ├ (auth)
    │   ├ layout.tsx   👈 로그인 전용 레이아웃
    │   ├ login/page.tsx
    │   └ signup/page.tsx
    
    ├ (main)
    │   ├ layout.tsx   👈 메인 서비스 레이아웃
    │   ├ dashboard/page.tsx

    요렇게 하면,

    /login 은 auth layout 으로, /dashboard는 main layout으로 구성되겠지?

  3. 프로젝트 커질 때 구조 정리

    아직 페이지가 많아지는 경험은 안해봤는데, 이제 잇츠파인 프로젝트 추가 개발 곧 시작이라서 아마 기능들을 추가로 진행한다면, 폴더 구조 정리가 필요할 듯싶다..!

    사실 데모데이까지 기간이 짧아서 빠르게 진행하느라 폴더 구조 정리를 제대로 못한 부분도 있어서 여기서 기능 추가하면 아마 구조 정리가 필요할것 같다..! (하지만, 잇츠파인은 Next.js가 아닌…React로 진행한다는점…!)

    그래서 내 개인프로젝트 진행할때 한번 진행하거나, 아니면,, 잇츠파인 추가 개발 이후에 한번더 폴더구조정리를 깃헙에다가 한번 더 올려봐서!! 과연 프로젝트가 커져서 구조정리하는데 Route Group이 도움이 되었는지 한번 비교하는 작업 해봐도 좋을것같다!!

여기서 중요한건..!! Route Group은 라우팅 기능이 아님!!! URL을 제어하는게 아니라,

그냥 폴더 정리하는것이다..! 우리 로컬에서 폴더 만들어서 파일들 정리해놓는 느낌으로 이해하면 빠를것같다

참고로, 이름은 아무거나 다 가능하다 (aaa), (animal), (food) (의미만 잘 정하면됨!!)

즉, URL은 그대로 두고, 개발하는 개발자 입장에서 파일 구조 파악이 빠르게 진행될 수 있도록 보기 좋게 코드 구조 정리하는 기능인 것이다.

Catch-all 과 Route Group의 차이?

그러면, Catch-all과는 무슨 차이가 있냐? 라고 물어보면 완전 다른개념이다 라고 말할수 있지.

  • Catch-all은 URL을 유연하게 받는 기능
  • Route Group은 폴더 구조를 정리

Next.js 특수 파일

Next.js의 App Router에서는 특정 이름의 파일들이 서로 합의된 예약된 파일명이 있고, 각각이 특별한 역할을 가진다.

대표적으로는 layout.tsx, template.tsx, loading.tsx, error.tsx, not-found.tsx 이런 파일들이 있는데, 각각 한번 무엇인지 파헤쳐보자.

layout.tsx

가장 먼저, layout.tsx

layout.tsx는 페이지의 공통 레이아웃을 정리하는 파일이다.

layout.tsx는 모든 페이지에 공통으로 적용된다. 페이지간의 공유되는 UI라고 보면되고, 리랜더링되지않고, 상태가 유지된다는 특징을 가지고 있다.

예시 코드로 보면,

export default function RootLayout({
	children,
}: {
	children: React.ReactNode;
}) {
	return (
		<html>
			<body>
				<header>Header</header>
				{children}
				<footer>Footer</footer>
			</body>
		</html>
	);
}

이렇게 볼수 있는데, 여기서 이제 children부분에 실제 페이지의 내용이 담기는거다.

즉, layout.tsx는 페이지들의 공통 UI 구조를 담당하는 파일이다. 내가 React를 진행했을때, 했던 layout.tsx과 같은 의미라고 생각하면 된다.

layout은 같은 레벨에서 이동할 때만 유지되고, 다른 레벨로 경로가 바뀔때는 layout이 바뀐다!!

예를 들어,

app
├ layout.tsx
├ page.tsx
├ about/page.tsx

/ → /about 으로 이동하는건. layout이 유지된다!

app
├ (auth)/layout.tsx
├ (main)/layout.tsx

근데, 여기서 auth → main 으로 이동하는건 layout이 바뀐다! 이해됐나??다 여기는 다른 레벨의 경로니까!!

template.tsx

template.tsx는 처음 보네? 뭘까? 라는 생각이 들지만, template.tsx는 페이지가 이동할때 항상 새로 랜더링되는 레이아웃이다. 즉, 페이지가 이동할 때마다 새 인스턴스가 생성된다.

읭? 이렇게 보면, layout.tsx이랑 무슨 차이인가 싶다.

한마디로 설명하면, 페이지 이동할때 리랜더링되냐 안되냐의 차이이다.

  • layout.tsx는 페이지가 이동하더라도(같은 레벨로) 전체적인 틀인 구조이기에 리랜더링되지않는다. 상태가 유지되는 것이다.
  • 하지만, template.tsx는 layout과 동일한 위치에 있지만 항상 새로 마운트된다!

그러면, template는 주로 어디에 사용하는게 좋을까?

페이지가 전환될때마다 애니메이션이 필요한경우,

특정 상태를 초기화하고 싶은경우같은 매번 페이지 이동할때마다 새로운 랜더링이 필요한 경우 layout.tsx가 아닌 template.tsx를 사용하게 되는것이다.

loading.tsx

loading.tsx은 말그대로 네트워크 상태나 로컬 장애로 인해 페이지가 로딩에 걸릴때, 그때 비어있는 화면만 뜨면 사용자는 대기 상태인지 모르고, 고장났나? 라고 인식할수있기때문에, fetching 상태임을 나타내기위해 보여주는 UI를 구현해놓은 파일이다.

그냥 loading.tsx는 진짜 우리가 생각하는 로딩 페이지 인거고,

createloading/ 은 커스텀 로딩/상태 페이지이다. 뒤로가기 버튼같은거 눌러도 계속 로딩중인건 로딩 진행되게.(이름은 임의로한거임)

react에도 있는기능이긴함.

참고로, loading.tsx는 전역 로딩뿐만아니라, 특정 라우트 경로마다 커스텀 로딩 UI를 구현할때도 유용하다!

그리고, loading.tsx는 Suspense 기반 자동 로딩 UI다!

  • React에서는
    <Suspense fallback={<Loading />} />
  • Next.js에서는
    loading.tsx

이렇게 자동으로 Suspense가 적용된다!

그리고 하나더! 라우트 단위로 자동으로 분리가 된다!!

예시로 보면

app
├ loading.tsx         ← 전체
├ dashboard
│   ├ loading.tsx     ← dashboard 전용

이렇게! 라우트 단위로 loading.tsx들이 분리가 되어있는거 보이지?? 이게 loading.tsx의 핵심 특징 중에 하나다!

error.tsx

error.tsx도 비슷하다. 페이지에서 에러가 발생하거나 데이터 요청이 실패해서 가져오지 못할 때 보여주는 화면이다. 이부분도 react에서 주로 많이 다루던 내용이라 넘어가겠다.

not-found.tsx

그 유명한 404 에러코드. not-found.tsx는 존재하지 않는 페이지일 때. 즉, URL이 존재하지 않을 때 보여주는 화면을 구현해놓은 파일이다.

여기서 추가 포인트 하나.

강제로 notFound() 호출이 가능하다!

import { notFound } from "next/navigation";

if (!data) {
  notFound();
}

이러면 not-found.tsx가 뜬다!

Parallel Routes

Parallel Routes는 하나의 페이지에서 여러개의 route를 동시에 랜더링 하는 기술이다.

예를 들어, 플랫폼 비교와 통계를 한화면에서 독립적인 로딩상태로 보여주기 위해서 보통사용한다.

여러개의 route를 각각 동시에 랜더링을 진행 할 수 있기 때문에, 복잡한 대시보드 UI를 구현할때 유용한 기능이다.

Parallel Routes 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

예시로 광고 관련 대시보드를 만든다고 한다면, 대시보드 페이지에서 여러개의 데이터를 동시에 보여줘야하기 때문에,

광고 통계, 플랫폼별 성과, 회원 최근 활동, 알림 과 같은 여러개의 route들을 하나의 페이지 안에서 보여줄 수 있는거지.

이때는 CSR 방식을 사용하면?

⇒ 하나씩 불러오니까, 만약에 알림 API가 늦어진다면? 전체 UI가 느려질수있으니, UX는 떨어진다고 볼수 있겠지??

근데 이제 Parallel Routes를 사용하면?

⇒ 각 부분들이 다 독립적으로 랜더링 되니까, 하나가 늦게 랜더링되더라도 다른것들 먼저 보여줄수있어서 사용자는 먼저 뜬 데이터에 대해서 확인이 가능하다는 장점이 있어서, 이런경우에는 Parallel Routes를 사용하는게 UX적으로 더 효율적일 것이다.

즉, Parallel Routes는 한 화면에서 여러개의 데이터를 독립적으로 랜더링하고 싶을때 사용하는 기능이다

Intercepting Routes

Intercepting Routes는 현재의 페이지 위에다가 다른 route를 겹쳐서 보여주는 기능이다.

잇츠파인에서 했던 예시로 보면 모달 UI가 있다.

사용자가 지도에서 가게를 검색해서 → 가게클릭 → 예약하기 클릭 →하면 /store/reservation/66 이렇게 페이지 이동하는것 대신에 모달을 사용해서 보여주었다.

아래에는 지도에서 가게를 검색한 페이지가 있고, 그위에 식당 상세/예약하는 모달 라우트를 겹쳐서 보여줄 수 있도록 설정한것이다.

사용자가 보기에는 가게 검색 → 클릭 → 식당 상세 → 예약 으로 페이지가 넘어간것처럼 보이지만, 실제로는 가게 검색페이지 + 식당 상세/예약 모달 형식인셈이다.

화면 위에 URL은 위에 올려져있는 route의 URL로 나타난다.

단, 모달이 띄워진 상태에서 새로고침을 하면 모달이 아니라 독립된 하나의 전체 페이지로 랜더링되어서 보여진다. (위에 올려져있던 route의 URL로)

현실에서 접하기에 가장 쉬운 예시는 인스타가 있다! 인스타 게시물 클릭하면 모달처럼 나오는데? 거기 URL은 그 페이지 전용 URL로 바뀌어 있음!! 근데 새로고침하면 그 페이지 URL로 나옴!!

Intercepting Routes 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

모달 UI + URL 유지 가 필요한 경우에 사용하면 좋다.

아까 설명한 예시처럼 /store → /store/reservation/66 이렇게 가게를 선택하고 예약하는 페이지로 이동할때는

보통의 라우팅은 페이지를 완전히 이동하지만,

Interception Routes는 현재의 가게 페이지는 유지하고 예약을 모달로 표시하는 방식 이라는 차이점이 있다.

Intercepting Routes의 장점

장점으로 보면

  1. 모달 닫힘으로 페이지가 유지되기 때문에 뒤로가기 UX가 자연스럽다.
  2. URL이 그대로 있기때문에 공유가 가능하다.
  3. 실제 페이지 경로가 존재하니까 SEO에 유리하다.

즉, Interception Routes는 페이지 이동한것처럼 사용자에게는 보이지만, 실제로는 현재 화면은 유지하고 그위에 UI를 겹치는 패턴을 구현할 때 주로 사용하는 기능이다.

Proxy.ts (구 middleware.ts)

Next.js 16에서는 기존에 middleware.ts가 proxy.ts로 변경되었다.

proxy는 요청이 서버에 도달하기 전에 실행되는 코드다. 즉, 요청이 서버에 도착하기 전에 실행되는 코드 인 셈이다.

즉, 네트워크 경계에서 동작하는 로직으로 보면된다.

우리가 쉽게 이해할만한 작업으로는

  • 로그인 인증 검사
  • 특정 페이지 접근 제한
  • URL 리다이렉트
  • 헤더 수정
  • 요청 rewrite

등이 있다.

이런식으로 proxy는 요청을 가로채서 처리하는 역할을 한다고 말할 수 있다.

Proxy.ts 주 사용처?

그러면 이건 어디에 쓰는게 좋을까?

가장 대표적으로는 인증 처리이다.

로그인을 하지 않은 사용자가 로그인이 필요한 페이지에 접근한다던지, 일반 고객이 사장님용 가게관리 페이지에 접근하려는 경우에는 proxy가 미리 체크해서 서버에 요청이 가기전에 미리 차단을 할수있다.

이 예시만으로도 보면, 보안적인 부분에서 서버까지 요청이 안가도 되니까 보안이 강화되는 장점이 있다.

또한, 어자피 들어가면 안되는 페이지였기에 불필요한 API호출을 방지할수있어서 비용적인 측면에서도 좋은 효과를 낼 수 있다.

그러니까, 즉, React는 보여주고 나서 막게 만드는 방법이고,

Proxy는 애초에 못 들어오게 막는 방법이라는 차이점이 있는거다. 마치 서버 앞에 있는 문지기 같은 느낌인거지.

Next.js 16부터는 proxy.ts에서 무거운 DB조회나 복잡한 검증을 하는 것을 지양하고 있다.

대신에, 토큰 유무나 단순 쿠키 확인 같은 단순한 가벼운 네트워크 경계 제어(리다이렉트 등)만 proxy.ts에서 처리하고, 상세한 권한은 서버 Actions나 Route Handlers쪽으로 넘기는 것을 권장한다.

즉, proxy는 서버 앞에서 동작하면서 미리 체크하는 보안/제어 로직이라고 볼 수 있다.

profile
Just do IT

0개의 댓글