Next.js / Server Component үндэс

Server Component үндэс

Next.js App Router-н хамгийн гол шинэлэг зүйл бол React Server Component — серверт ажилладаг React component. Энэ ойлголтыг сайн эзэмшвэл Next.js-г үнэхээр хүчтэй хэрэглэж чадна.

Server Component гэж яг юу вэ?

Ердийн React component (Vite дахь) хөтөч дээр ажилладаг. Server Component харин сервер дээр ажилладаг бөгөөд хөтөч рүү зөвхөн HTML очдог — JavaScript очдоггүй.

tsx
// app/courses/page.tsx — Server Component (анхны төлөв)
// 'use client' байхгүй тул сервер дээр ажиллана

import { readFile } from "fs/promises";
import path from "path";

export default async function CoursesPage() {
  // Файл системээс шууд уншиж болно — хөтчөөс боломжгүй!
  const filePath = path.join(process.cwd(), "content/courses.json");
  const data = await readFile(filePath, "utf-8");
  const courses = JSON.parse(data);

  return (
    <main>
      <h1>Сургалтууд</h1>
      <ul>
        {courses.map((course: { slug: string; title: string }) => (
          <li key={course.slug}>{course.title}</li>
        ))}
      </ul>
    </main>
  );
}

async/await шууд component дотор ашиглаж болно — Server Component-н хамгийн том давуу тал энэ!

Server Component-н давуу талууд

Bundle хэмжээ багасна — component-н код хөтөч рүү очдоггүй тул JavaScript bundle жижигсэнэ. Хэрэглэгч хуудсыг хурдан ачаална.

Аюулгүй байдал — database connection string, API key зэрэг нууц мэдээллийг server component дотор шууд ашиглаж болно. Хөтөч рүү алддаггүй.

Шууд өгөгдлийн сан — Supabase-с өгөгдөл татахад API route дамжуулах шаардлагагүй. Server component дотор шууд хийнэ:

tsx
// app/profile/page.tsx
import { createClient } from "@/lib/supabase/server";
import { redirect } from "next/navigation";

export default async function ProfilePage() {
  const supabase = await createClient();

  const {
    data: { user },
  } = await supabase.auth.getUser();

  if (!user) {
    redirect("/login"); // Нэвтрээгүй бол login руу шилжүүлнэ
  }

  const { data: profile } = await supabase
    .from("profiles")
    .select("username, xp, streak")
    .eq("id", user.id)
    .single();

  return (
    <main>
      <h1>Сайн байна уу, {profile?.username}!</h1>
      <p>XP: {profile?.xp}</p>
      <p>Streak: {profile?.streak} өдөр</p>
    </main>
  );
}

Энэ бүх код сервер дээр ажиллана — хөтөч Supabase-н connection-г харахгүй.

Server Component ба Client Component харьцуулалт

| | Server Component | Client Component | | ----------------------- | ---------------- | ---------------- | | Ажилладаг газар | Сервер | Хөтөч | | async/await | ✅ Болно | ❌ Болохгүй | | Database шууд | ✅ Болно | ❌ Болохгүй | | useState, useEffect | ❌ Болохгүй | ✅ Болно | | Event handler | ❌ Болохгүй | ✅ Болно | | Bundle-д орох | ❌ Ордоггүй | ✅ Ордог | | Тэмдэглэгч | Байхгүй | 'use client' |

Хэзээ Client Component ашиглах вэ?

Зөвхөн дараах тохиолдолд 'use client' нэмнэ:

tsx
"use client";

import { useState } from "react";

// useState hook ашиглаж байгаа тул client component
export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Тоо: {count}</p>
      <button onClick={() => setCount(count + 1)}>Нэмэх</button>
    </div>
  );
}

'use client' нэмэх шалтгаанууд:

  • useState, useEffect, useRef зэрэг hook ашиглах
  • onClick, onChange зэрэг event handler ашиглах
  • window, localStorage зэрэг browser API ашиглах
  • usePathname, useRouter зэрэг navigation hook ашиглах

Зөв загвар — Server + Client хослуулах

Хамгийн зөв хандлага бол server component-г аль болох их, client component-г аль болох бага ашиглах. Ихэвчлэн server component дотор client component байрлуулна:

tsx
// app/courses/[courseSlug]/page.tsx — Server Component
import LessonSidebar from "@/components/lesson/LessonSidebar"; // Client
import { getCourse } from "@/lib/courses";

interface Props {
  params: Promise<{ courseSlug: string }>;
}

export default async function CoursePage({ params }: Props) {
  const { courseSlug } = await params;
  const course = await getCourse(courseSlug); // Серверт өгөгдөл татна

  return (
    <div className="flex">
      {/* Client component-д өгөгдлийг prop-оор дамжуулна */}
      <LessonSidebar lessons={course.lessons} />
      <main>
        <h1>{course.title}</h1>
      </main>
    </div>
  );
}

Server component-д татсан өгөгдлийг client component-д prop-оор дамжуулна — энэ бол зөв загвар.

Дараагийн хичээлд:

Client Component-г нарийвчлан судална. 'use client' хэзээ нэмэх, хэрэглэгчийн харилцан үйлчлэлийг хэрхэн зохицуулах тухай практик жишээгээр ойлгоно.