학습 자료

폴더를 만들면 주소가 생긴다


Article

React로 여러 페이지를 만들려면 라우팅 라이브러리를 깔고 설정을 써야 했다.

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
  <Route path="/products/:id" element={<Product />} />
</Routes>

Next.js에는 이런 파일이 없다. 폴더를 만들면 그게 주소다.

프로젝트를 만든다

npx create-next-app@latest my-app

몇 가지를 묻는다. 지금은 이렇게 답한다.

질문선택이유
TypeScript?NoNext.js 를 먼저 익힌다
ESLint?Yes실수를 잡아준다
Tailwind CSS?취향이 시리즈는 쓰지 않는다
src/ directory?Yes코드와 설정이 섞이지 않는다
App Router?Yes필수. 이 시리즈의 전제다
Turbopack?YesNext 16 의 기본값이다
cd my-app
npm run dev

http://localhost:3000을 열면 시작 페이지가 뜬다.

app 폴더가 전부다

만들어진 폴더 중 지금 볼 것은 하나다.

지금 중요한 것
  • my-app
    • src/app여기가 라우터다
      • layout.js모든 페이지를 감싼다
      • page.js/ 주소의 화면
      • globals.css
    • public이미지 등 정적 파일
    • next.config.ts설정

app/page.js를 열어 통째로 지우고 이렇게 쓴다.

export default function Home() {
  return <h1></h1>;
}

React 편에서 만들던 컴포넌트와 똑같다. export default가 필수라는 것만 다르다. Next.js가 이 파일을 찾아 그 함수를 실행하기 때문이다.

1강에서 말한 "내가 부르는가, 불리는가"가 여기서 드러난다. 우리는 <Home />이라고 쓴 적이 없다.

폴더가 주소다

app/about/page.js를 만든다.

export default function About() {
  return <h1>소개</h1>;
}

/about으로 가면 이 화면이 뜬다. 설정 파일을 고치지 않았다.

폴더 이름이 URL 조각이 된다
  1. app/루트
  2. products//products
  3. shoes//products/shoes
  4. page.js이 파일이 화면

몇 개 더 만들어보면 규칙이 보인다.

파일주소
app/page.js/
app/about/page.js/about
app/blog/page.js/blog
app/blog/first-post/page.js/blog/first-post
app/products/shoes/page.js/products/shoes

폴더는 주소를 만들고, page.js가 화면을 만든다.

폴더만으로는 주소가 안 생긴다

여기가 헷갈리는 지점이다.

page.js 가 없으면 접근할 수 없다
  • app
    • dashboardpage.js 없음 → /dashboard 는 404
      • settingspage.js 있음 → /dashboard/settings 는 정상

/dashboard는 404다. 폴더는 있지만 그 주소에 보여줄 화면이 없기 때문이다. /dashboard/settings는 잘 뜬다.

이걸 이용해 주소를 만들지 않는 폴더를 둘 수 있다. 컴포넌트나 유틸을 app 안에 두어도 라우트가 되지 않는다.

특수한 파일 이름들

page.js 말고도 Next.js가 알아보는 이름이 몇 개 있다. 정해진 이름을 정해진 자리에 두면 Next.js가 알아서 쓴다.

파일명역할언제 보이나
page.js그 주소의 화면항상
layout.js감싸는 껍데기항상
loading.js로딩 중 화면데이터를 기다릴 때
error.js에러 화면예외가 터졌을 때
not-found.js404 화면없는 주소일 때

4강에서 하나씩 다룬다. 지금은 파일 이름이 곧 약속이라는 것만 기억한다.

Page.jspages.js는 동작하지 않는다. 대소문자와 철자가 정확해야 한다.

layout.js — 모든 페이지를 감싼다

app/layout.js를 열어본다.

export default function RootLayout({ children }) {
  return (
    <html lang="ko">
      <body>{children}</body>
    </html>
  );
}

children이 보인다. React 편 6강에서 배운 그것이다.

children 자리에 각 페이지가 들어온다. /about으로 가면 About 컴포넌트가, /blog로 가면 Blog가 그 자리에 꽂힌다.

layout 이 page 를 감싼다
  1. layout.jshtml · body · 헤더
  2. {children}여기에
  3. page.js페이지가 들어온다

그래서 헤더와 푸터를 여기 두면 모든 페이지에 나온다.

export default function RootLayout({ children }) {
  return (
    <html lang="ko">
      <body>
        <header>로고와 메뉴</header>
        <main>{children}</main>
        <footer>저작권</footer>
      </body>
    </html>
  );
}

링크로 이동한다

<a> 태그를 쓰면 페이지 전체가 새로고침된다. 자바스크립트 상태가 전부 날아가고 화면이 깜빡인다.

Next.js는 <Link>를 쓴다.

import Link from "next/link";

export default function Header() {
  return (
    <nav>
      <Link href="/"></Link>
      <Link href="/about">소개</Link>
    </nav>
  );
}
a 태그와 Link

<a href>

브라우저 기본 이동

  • 페이지 전체를 다시 받는다
  • 화면이 깜빡인다
  • React 상태가 사라진다

클라이언트 네비게이션

  • 바뀐 부분만 받는다
  • 깜빡이지 않는다
  • 레이아웃과 상태가 유지된다
  • 화면에 보이면 미리 받아둔다

마지막 항목이 중요하다. <Link>가 화면에 보이면 Next.js가 그 페이지를 미리 내려받는다. 클릭하는 순간 이미 준비돼 있어서 즉시 바뀐다. 이걸 **프리페치(prefetch)**라 부른다.

개발 서버가 알려주는 것

npm run dev를 띄워두고 페이지를 열면 터미널에 로그가 찍힌다.

터미널
  • ▲ Next.js 16 (Turbopack)
  • - Local: http://localhost:3000
  • GET / 200 in 45ms (compile 30ms, render 15ms)

Next 16부터 시간이 어디에 쓰였는지 나눠 보여준다. compile은 코드를 준비하는 데, render는 우리 코드를 실행하는 데 걸린 시간이다.

render가 유난히 길다면 우리 코드가 느린 것이다. 나중에 데이터를 받아올 때 이 숫자를 보게 된다.

정리하면

  • Next.js에는 라우팅 설정 파일이 없다. app 폴더의 구조가 곧 URL이다.
  • 폴더가 주소를 만들고, page.js가 화면을 만든다. page.js가 없으면 그 주소는 404다.
  • 그래서 app 안에 컴포넌트를 둬도 라우트가 되지 않는다.
  • layout.js는 모든 페이지를 감싼다. <html><body>가 여기 있고, children 자리에 페이지가 들어온다.
  • 이동은 **<Link>**로 한다. 바뀐 부분만 받고, 화면에 보이면 미리 받아둔다.
  • Next 16은 Turbopack이 기본이고, next lint제거됐다.

다음 강의에서는 이 컴포넌트들이 어디서 실행되는지 본다. 브라우저가 아니다.

폴더를 만들면 주소가 생긴다 — 디코드랩(DCODELAB)