๐ข ๋ฆฌ์กํธ๋ฅผ ํ์คํ์ผ๋ก ๊ฐ๋ฐํ ์ ์๋ ํ๋ ์์ํฌ
* ๋ผ์ด๋ธ๋ฌ๋ฆฌ : ํน์ ๊ธฐ๋ฅ ํ๋์ ์ง์ค (lke React)
* ํ๋ ์์ํฌ : ํ๋์ ๊ธฐ๋ฅ์ด ์๋ ์์คํ
์ ์ฒด์ ๋์์ ๊ณ ๋ ค (like Next.js)
๐ข Next.js๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง (Server Side Rendering)์ ์ง์ํ๋ค.
also referred to as "SSR" or "Dynamic Rendering" ๋ฆฌ์กํธ์์๋ ๋ณ๋์ router๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์๋ค.
/pages
๋๋ ํ ๋ฆฌ ํ์์ ์๋ ๊ฒฝ๋ก(path)๊ฐ ๊ทธ ์์ฒด๋ก ํ์ด์ง์ path๊ฐ ๋๋ค.
/pages/home.tsx => www.example.com/home
Routhing
The Pages Router has a file-system based router built on concepts of pages.
When a file is added to the pates directory it's automatically available as a route.
Learn more about routing in the Pages Router
๐ข Next.js๋ฅผ ํตํด ๋ฐฑ์๋ ์ฝ๋๋ ๊ฐ์ด ์์ฑํ ์ ์๋ค.
/pages/api
ํ์์ Node.js ๋ฐฑ์๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด API๋ฅผ ๋ง๋ค ์ ์๋ค.
$ npx create-next-app@latest
/app
ํ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ ๊ทธ ํ์์/page.jsx
์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๋ฉด ์ค์ฒฉ๋ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ง ํ์ด์ง๋ฅผ ๋ง๋ค ์ ์๋ค./app/AAA/page.jsx
=>www.example.com/AAA
(1) app > news > page.jsx
์์ฑํ๊ธฐ
//๊ธฐ๋ณธ์ ์ธ ํ
ํ๋ฆฟ
import React from "react";
const News = () => {
return <div>Hello News</div>;
};
export default News;
localhost:3000/news
๋ฐ๋ก ๋ผ์ฐํธ ์ฒ๋ฆฌ ํด์ค ํ์์์ด path์ ๋ง์ถฐ์ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค.
localhost:3000/news/articles
import React from 'react';
const Articles = () => {
return (
<div>
Hello articles
</div>
);
};
export default Articles;
app
>news
>articles
>page.jsx
we recommend reading the Routing Fundamentsal and Defining Routes pages before continuing.
The App Router inside Next.js 13 introduced new file conventious to easily create pages, shared layouts, and templates.
This page will guide you through how to use theses special files in your Next.js application.
- pages์ layouts ์ ์ฐจ์ด์
A page is UI that is uique to a route.
You can define pages by exporting a component from a
page.js
file.Use nested folders to define a route and a
page.js
file to make the route publicly accessible.ํ์ด์ง๋ ์ฌ์ฉ์ ์ธํฐํ์ด์ค์ธ๋ฐ ๋ผ์ฐํธ์ ๊ณ ์ ํ ๊ฐ์ ๊ฐ์ง ๊ฒฝ๋ก์ด๋ค. (ํ๋์ ๋ผ์ฐํธ์ ํ๋์ฉ ๊ณ ์ ํ๊ฒ ํ ๋น๋ UI ํ์ด์ง)
// `app/page.tsx` is the UI for the `/` URL export default function Page() { return <h1>Hello, Home page!</h1> }// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL export default function Page() { return <h1>Hello, Dashboard Page!</h1> }
A layout is UI that is shared between multiple routes. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.
You can define a layout by default exporting a React component from a
layout.js
file. The component should accept achildren
prop that will be populated with a child layout (if it exists) or a page during rendering.์ฌ๋ฌํ์ด์ง์์ ๊ณต์ ํ ์ ์๋ UI๋ผ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ๊ณตํต์ ์ผ๋ก ๊ฐ์ง ์ ์๋ NAVBAR ๋ฅผ ํ์ด์ง ๋ณด๋ค๋ ๋ ์ด์์์ ๊ฐ๋ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
export default function DashboardLayout({ children, // will be a page or nested layout }: { children: React.ReactNode }) { return ( <section> {/* Include shared UI here e.g. a header or sidebar */} <nav></nav> {children} </section> ) }
- ํ์ผ ๋ช
์
[id].jsx
์ด๋ ๊ฒ ๋ง๋ค์ด ์ฃผ๊ฒ ๋๋ฉด, id ๊ฐ(ํ๋ผ๋ฏธํฐ ๊ฐ)์ ๋ฐ์ ๋์ ์ธ ํ์ด์ง๋ฅผ ๋ง๋ค์ด ์ค ์ ์๋ค./app.news/[id]/page.jsx
=>www.example.com/news/123
- ์ปดํฌ๋ํธ props๋ก params, searchParams ์ ๋ฌ๋ฐ๊ธฐ
//app\new\[id]\page.jsx
import React from "react";
const NewsIdPage = ({ params }) => {
console.log("news id", params.id);
return <div> hello new {params.id}</div>;
};
export default NewsIdPage;
hello new 123
=>http://localhost:3000/new/123
hello new 12356
=>http://localhost:3000/new/12356
- ์ง๊ธ ๋ง๋ค๊ณ ์๋ ํ์ด์ง๋ ํด๋ผ์ด์ธํธ์์ ๋ ๋๋งํ๋ ๊ฒ์ด ์๋ ์๋ฒ์์ ๋ ๋๋ง์ ํ๊ณ ์๋ ๊ฒ์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ์ฝ์์ฐฝ์ ํด๋ผ์ด์ธํธ ๋ ๋๋ง์ ํ ๋ ๋ณด์ฌ์ง๋ ์ฝ์ ์ฐฝ์ด๋ค.
import React from "react";
const NewsIdPage = ({ params, searchParams }) => {
console.log("news id", params.id);
console.log("searchParams", searchParams);
return <div> hello new {params.id}</div>;
};
export default NewsIdPage;
http://localhost:3000/new/12356?test=abc
news id 12356
searchParams {}
GET /new/12356 200 in 93ms
news id 12356
searchParams { test: 'abc' }
GET /new/12356?test=abc 200 in 82ms
{ test: 'abc'
์?
๋ค์์ ๋ง๋ ์ฟผ๋ฆฌ ์คํธ๋ง ์ด๋ค.key, value ํํ๋ก ์ด๋ค ๊ฐ๋ค์ URL์ ํตํด์ ์ ๋ฌํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฐ๋ก ์ฟผ๋ฆฌ ์คํธ๋ง์ด๋ค.
์ฟผ๋ฆฌ ์คํธ๋ง ๊ฐ์ ์ปดํฌ๋ํธ์์
searchParams
์ ํตํด์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
import React from "react";
const NewsIdPage = ({ params, searchParams }) => {
console.log("news id", params.id);
console.log("searchParams", searchParams);
return (
<div>
hello new {params.id}
hello new {searchParams.test}
</div>
);
};
export default NewsIdPage;
ํ๋ฉด์ ๋ณด์ฌ์ง๋ ๊ฒฐ๊ณผ๊ฐ : hello new 12356hello new abc
โ Compiled in 75ms (277 modules) news id 12356 searchParams { test: 'abc' }
params, searchParams
์ ํตํด์ URL ์๋ ์ ๋ณด๋ค์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- import Link form next/link
<Link href="/path">
//Next.js_project\my-app\app\page.js
import Image from "next/image";
import styles from "./page.module.css";
import Link from "next/link";
export default function Home() {
return (
<>
<h1>Hello Next.js</h1>
<Link href="/news">Go to News</Link>
</>
);
}
- useRouter๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
$ npm i prisma -save-dev
$ npx prisma init --datasource-provider sqlite
model Todo {
id String @id @default(uuid())
title String
}
$ npx prisma migrate dev --name init
migrations/
โโ 20240509010404_init/
โโ migration.sql
//app\db.js
//db.js ํ์ผ ์์ฑํ๊ธฐ
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis;
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
- ๋ก์ปฌ DB์์ ๋ฐ์ดํฐ ๋ถ๋ฌ์ค๊ธฐ
- ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด๋ณด๊ธฐ
- ์ฝ์์ ์ฐ์ด๋ณด๊ธฐ
- UI๋ก ๊ทธ๋ ค๋ณด๊ธฐ
import Link from "next/link";
import { prisma } from "./db";
export default async function Home() {
//๊ฐ๋จํ๊ฒ ๋ฐ์ดํฐ ๋ฃ์ด์ฃผ๊ธฐ
await prisma.todo.create({ data: { title: "ํ
์คํธ ๋ฐ์ดํฐ" } });
const todos = await prisma.todo.findMany();
console.log("todos", todos);
return (
<>
<h1>Hello Next.js</h1>
<Link href="/news">Go to News</Link>
</>
);
}
โ Compiled in 253ms (312 modules)
todos [
{ id: 'f9aa9947-5de2-4ed9-9529-0475b4d232b7', title: 'ํ
์คํธ ๋ฐ์ดํฐ' },
{ id: 'd49dd681-83f5-4560-82ce-c1628a30e28b', title: 'ํ
์คํธ ๋ฐ์ดํฐ' },
{ id: 'c22f11ab-6df1-45a0-862d-6f8398a0c82a', title: 'ํ
์คํธ ๋ฐ์ดํฐ' },
{ id: '8466642b-acd4-4ddf-ba12-d753cfe7bc3e', title: 'ํ
์คํธ ๋ฐ์ดํฐ' }
]
GET / 200 in 210ms
- ์ฌ๋ฌ๊ฐ๊ฐ ๋ค์ด๊ฐ๋ ์ด์ : ์ปดํฌ๋ํธ๋ฅผ ๋ก๋ฉํ ๋๋ง๋ค
create
๋ก์ง์ด ์คํ์ด ๋๊ธฐ ๋๋ฌธ์ ๊ทธ๋๋ง๋ค ํ๋์ฉ ๊ณ์ ์์ด๊ธฐ ๋๋ฌธ์ด๋ค.
import Link from "next/link";
import { prisma } from "./db";
export default async function Home() {
//๊ฐ๋จํ๊ฒ ๋ฐ์ดํฐ ๋ฃ์ด์ฃผ๊ธฐ
//await prisma.todo.create({ data: { title: "ํ
์คํธ ๋ฐ์ดํฐ" } });
const todos = await prisma.todo.findMany();
console.log("todos", todos);
return (
<>
<h1>Hello Next.js</h1>
<Link href="/news">Go to News</Link>
{/* ๋ฐ์ดํฐ ๋ฒ ์ด์ค์์ ๋ถ๋ฌ์จ ๋ฐ์ดํฐ๋ค์ map์ ํตํด ๋ก์ง์ ๊ทธ๋ ค๋๊ฐ ์ ์๋ค. */}
{todos.map((todo) => (
<h1>{todo.title}</h1>
))}
</>
);
}
//ํ๋ฉด์ ๋ณด์ด๋ ๊ฒฐ๊ณผ๊ฐ Hello Next.js Go to News ํ ์คํธ ๋ฐ์ดํฐ ํ ์คํธ ๋ฐ์ดํฐ ํ ์คํธ ๋ฐ์ดํฐ ํ ์คํธ ๋ฐ์ดํฐ
state๊ฐ ์๋๋ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ๋ถ๋ฌ์ค๊ธฐ ๋๋ฌธ์ ์๋ก๊ณ ์นจ์ ํด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ด ๊ทธ๋๋ก ๋จ์์๊ฒ ๋๋ค.
๋ง์ฝ
await prisma.todo.create({ data: { title: "ํ ์คํธ ๋ฐ์ดํฐ" } });
create ๋ก์ง์ ์ถ๊ฐํด์ฃผ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค ํ๋์ฉ ์ถ๊ฐ๊ฐ ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ ๋๋ง์ ์์ฑํ ์ฝ๋๊ฐ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ก ์ ํ์์ผ์ฃผ๋ ๊ฒ์ด๋ค.
๋ ๋๋ง์ ํ๊ฒฝ์ ํฌ๊ฒ Client์ Server ๋ ํ์ ์ผ๋ก ๋๋ ์ ธ์ ์ค๋ช ์ด ๊ฐ๋ฅํ๋ค.
- Client ๋ ๋๋ง : ๋๋ฐ์ด์ค์ ์๋ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ฒ ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ๋ฐ์์ UI๋ฅผ ๊ทธ๋ ค์ฃผ๋ ๋ฐฉ์
- Server ๋ ๋๋ง : ์ปดํจํฐ๊ฐ ์ฐ๋ฆฌ๋ค์ ์ฝ๋์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ์ ํ๊ณ ์๊ณ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์์ฒญ์ ๋ฐ์์ ๋ ๊ทธ ์ฐ์ฐ์ ์ดํ๋ฆฌ์ผ์ด์ ๋ด์์ ์ฒ๋ฆฌ๋ฅผ ํด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์๋ต์ ์ฃผ๋ ๋ฐฉ์
- ํด๋น ์๋ฒ์์์ ์ด๋ฏธ ๋ฐ์ดํฐ ์๋ต ์์ฒญ์ ๋ค ์ฒ๋ฆฌํ ์ ์๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ํตํด์ ์๋ฒ์์ ๋ฐ์ดํฐ ํจ์นญ์ ํ๋ค. ์ฌ๋ฌ๊ฐ์ ๋ฐ์ดํฐ๋ค์ ๋์์ ๋ฐ์์ฌ ์ ์๋๋ก ๋ฐ์ดํฐ ํจ์นญ์ ํ ์ ์๋ค.
ํญํฌ์ ์ฒ๋ผ ์ฐ์์ ์ผ๋ก ์ผ์ด๋๋ฉด์ ๋ก๋ฉํ์์ด ๊ธธ์ด์ง๋ ์ผ๋ค์ ์ต์ํํ๋ค.
์์ฒญ์ ํธ๋ฆฌ ํํ๋ก ๋ณต์ ๋ฅผ ํ๋ค. ํ๋ฉด์ ๋ก๋ฉํ๋ ๊ณผ์ ์์ Streaming and Susepense๋ฅผ ํตํด์ ํ์ด์ง๋ฅผ ๋ ๋๋ง์ ํ๋ค. (์ฌ์ฉ์๋ค์๊ฒ ๋น ๋ฅด๊ฒ ํ๋ฉด์ ๋ณด์ฌ์ง ์ ์๋๋ก )