
사용자가 브라우저에 접근하여 상호작용하는 Client와 클라이언트의 요청에 의해 서버에서 적절한 데이터를 응답하는 Sever로 이루어진 요청-응답 라이프사이클(Request-Response Lifecycle)을 대부분의 웹사이트에서 따르고 있다.
Next.js는 서버와 클라이언트를 분리하여 렌더링하도록 Client Component와 Server Component로 나누었다.
맨 끝단에 클라이언트 컴포넌트를 배치하는 것이 원칙이며, 클라이언트 컴포넌트 하위에 서버 컴포넌트가 올 수 없다.
모든 페이지에서 title의 형식을 유지하고 싶을 때

하위 페이지는 %s에 들어갈 title을 정하면 된다.
const [data,data2] = await Promise.all([getData(), getData2()]);
단점: 한 데이터가 먼저 처리되더라도 모든 페이지가 처리되기 전까지 페이지를 렌더링하지 못함.
<Suspense fallback={<Loading/>}>
<Component1/>
</Suspense>
<Suspense fallback={<Loading/>}>
<Component2/>
</Suspense>
각각의 컴포넌트가 로딩 될 때마다, 렌더링하여 보여준다. 오래 걸리는 작업이 처리 될 때 까지 기다릴 필요가 없다.
Suspense의 fallback 속성은 로딩 중 임을 표시할 컴포넌트를 넣을 수 있다.
특정 app 라우트 폴더에 loading.tsx를 만들면 페이지를 렌더링 할 때 loading.tsx를 보여준다.
페이지를 찾지 못했을 때 출력하는 컴포넌트
현재 app 라우트 경로로 부터 가장 가까운 not-found를 호출한다.
에러가 발생하였을 때 출력하는 컴포넌트
현재 app 라우트 경로로 부터 가장 가까운 error.tsx를 호출한다.
request를 받기 위해 app route 내부에 route.ts(js) 파일을 정의하여 api를 custom 할 수 있다.
GET, POST, PUT, PATCH, DELETE 등의 HTTP 메서드 사용가능 하다.
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
headers: {
'Content-Type': 'application/json',
'API-Key': process.env.DATA_API_KEY,
},
})
const data = await res.json()
return Response.json({ data })
}
next.revalidate 옵션으로 cached date를 revalidate 설정 가능
route.ts 파일과 page.tsx 파일이 같은 곳에 존재할 경우 충돌
동적 경로(Dynamic Route)를 params를 통해 참조 할 수 있다.
// app/items/[slug]/route.js
export async function GET(
request: Request,
{ params }: { params: { slug: string } }
) {
const slug = params.slug // 'a', 'b', or 'c'
}
쿼리스트링을 NextRequest 인스턴스를 통해 받아 올 수 있다.
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
request.formData() 를 통해 Formdata를 읽어 올 수 있다.
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
-> 서버 액션으로 Routehandler 거치지 않고 Formdata를 바로 POST 할 수도 있다.
성능적으로 컴포넌트 내에서 처리해도 같으나 추상화적인 면에서 다름
추상화가 잘 되어 있는 경우 Restful 하다고 함
on-demand 방식으로 cached data를 삭제한다.
revalidatePath(path: string, type?: 'page' | 'layout'): void;
특정 page나 layout의 캐시데이터를 삭제
on-demand 방식으로 cache tag에 대한 캐시 데이터를 삭제
tag 지정하기
fetch(url, { next: { tags: [...] } });
revalidateTag(tag: string): void;
서버에서 실행되는 비동기 함수의 일종.
HTML form 태그 요소를 확장하여 action prop에 server action 호출하여 formdata 받는 것.
기존의 form data는 리엑트 상에서 useState와 같은 Hook과 onSubmithandler를 통해 데이터를 받아 왔지만 이 과정을 Server action이 대체
export default function Page() {
async function createInvoice(formData: FormData) {
'use server'
const rawFormData = {
customerId: formData.get('customerId'),
amount: formData.get('amount'),
status: formData.get('status'),
}
// mutate data
// revalidate cache
}
return <form action={createInvoice}>...</form>
}
// getting-started.js
const mongoose = require('mongoose');
main().catch(err => console.log(err));
async function main() {
await mongoose.connect('mongodb://127.0.0.1:27017/test');
const contactSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
subject: {
type: String,
required: true,
},
message: {
type: String,
required: true,
},
});
export const Contact =
mongoose.models?.Contact || mongoose.model("Contact", contactSchema);
// model이 이미 생성되어 있으면 그대로 사용하고 그렇지 않으면 model을 생성한다.'
단 Model로 컴파일되기 이전에 method를 추가하여야 한다.
contactSchema.methods.intro = function intro() {
const email = this.email;
console.log(`This Message from: ${email}`);
};
새로운 데이터를 추가하는 것이라고 이해하면 됨
const data = new Contact({email: "abcd@gmail.com"});
몽고DB에 저장하기 위해 save 메서드를 사용한다.
await data.save();
Mongo DB에서 데이터를 찾기위해 model의 find 메서드를 사용하면 된다.
findOne, findById, findByIdAndDelete 등 다양한 메서드를 지원한다.
const data = await Contact.find();