풀스택 애플리케이션 구축
종합 프로젝트를 진행하며 지금까지 학습한 모든 개념을 완전한 풀스택 Next.js 애플리케이션에 통합합니다.
풀스택 애플리케이션 구축은(는) CoddyKit의 무료 Next.js 15 Fullstack (App Router + Server Actions) 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Next.js 15 Fullstack (App Router + Server Actions) 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Next.js 15 Fullstack (App Router + Server Actions) 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Fullstack App: Task Manager
Welcome to building a real-world fullstack application with Next.js 15! In this lesson, we'll integrate all the concepts you've learned to create a simple task manager.
Our app will allow users to:
- Sign in securely.
- View their personal tasks.
- Add new tasks.
- Mark tasks as complete.
This will demonstrate how Server Components, Client Components, Server Actions, data fetching, and authentication work together.
Project Init & Database Setup
First, let's set up our project and define the database schema using Prisma. We'll need models for users and tasks.
- Initialize Next.js:
npx create-next-app@latest my-fullstack-app - Install Prisma:
npm install prisma @prisma/client - Initialize Prisma:
npx prisma init --datasource-provider sqlite(or your preferred DB)
Here's a basic schema.prisma for our task manager:
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite" // or "postgresql", "mysql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
email String @unique
name String?
password String // Hashed password
tasks Task[]
}
model Task {
id String @id @default(cuid())
title String
completed Boolean @default(false)
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Implementing NextAuth.js
Authentication is a core part of any fullstack application. We'll integrate NextAuth.js to handle user sign-in, sign-up, and session management. This involves creating an API route for NextAuth.
Our [...nextauth] route defines how users authenticate:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcrypt";
const prisma = new PrismaClient();
const handler = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) return null;
const user = await prisma.user.findUnique({
where: { email: credentials.email }
});
if (!user || !(await bcrypt.compare(credentials.password, user.password))) {
return null;
}
return { id: user.id, email: user.email, name: user.name };
},
}),
],
session: { strategy: "jwt" },
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.id = user.id;
token.email = user.email;
}
return token;
},
session: async ({ session, token }) => {
if (token) {
session.user.id = token.id as string;
session.user.email = token.email;
}
return session;
},
},
pages: {
signIn: "/auth/signin",
},
});
export { handler as GET, handler as POST };Displaying User Tasks (RSC)
Our main dashboard will display tasks specific to the logged-in user. We'll use a React Server Component (RSC) to fetch this data directly from the database, reducing client-side JavaScript.
This page.tsx fetches tasks and passes them to a Client Component for rendering:
// app/dashboard/page.tsx
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route"; // Adjust path
import { PrismaClient } from "@prisma/client";
import TaskList from "./TaskList"; // This will be a Client Component
const prisma = new PrismaClient();
export default async function DashboardPage() {
const session = await getServerSession(authOptions);
if (!session || !session.user?.id) {
return <p>Please sign in to view your tasks.</p>;
}
const tasks = await prisma.task.findMany({
where: { userId: session.user.id },
orderBy: { createdAt: "desc" },
});
return (
<div>
<h1>Welcome, {session.user.name || session.user.email}!</h1>
<h2>Your Tasks</h2>
<TaskList initialTasks={tasks} />
</div>
);
}Adding Tasks with Server Actions
To add new tasks, we'll leverage Next.js Server Actions. This allows us to handle form submissions directly on the server, ensuring security and automatically revalidating data in our UI.
Here's a simplified Server Action to create a new task:
// app/actions/task-actions.ts
"use server";
import { revalidatePath } from "next/cache";
import { getServerSession } from "next-auth";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export async function addTask(formData: FormData) {
const session = await getServerSession(authOptions);
if (!session || !session.user?.id) {
throw new Error("Unauthorized");
}
const title = formData.get("title") as string;
if (!title || title.trim() === "") {
throw new Error("Task title cannot be empty.");
}
await prisma.task.create({
data: {
title,
userId: session.user.id,
},
});
revalidatePath("/dashboard"); // Revalidate dashboard to show new task
}Interactive Task Management (RCC)
While Server Components handle initial data, Client Components (RCC) are essential for interactivity. We can use them to update the UI instantly, like marking a task complete, often with optimistic updates.
This TaskList component uses client-side state and a Server Action:
// app/dashboard/TaskList.tsx (Client Component)
"use client";
import { useState } from "react";
// Assume toggleTaskCompletion is another server action
import { toggleTaskCompletion } from "@/app/actions/task-actions";
export default function TaskList({ initialTasks }) {
const [tasks, setTasks] = useState(initialTasks);
const handleToggle = async (taskId: string, currentStatus: boolean) => {
// Optimistic UI update
setTasks(tasks.map(task =>
task.id === taskId ? { ...task, completed: !currentStatus } : task
));
try {
await toggleTaskCompletion(taskId, !currentStatus);
} catch (error) {
console.error("Failed to toggle task:", error);
// Revert if error
setTasks(initialTasks); // Revert to initial state on error
}
};
return (
<ul>
{tasks.map(task => (
<li key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={() => handleToggle(task.id, task.completed)}
/>
<span style={{ textDecoration: task.completed ? "line-through" : "none" }}>
{task.title}
</span>
</li>
))}
</ul>
);
}Protecting Routes with Middleware
To secure our application, we must protect routes that require authentication. Next.js Middleware is ideal for this, allowing us to redirect unauthenticated users before they even reach a page.
Our middleware.ts can enforce authentication for specific paths:
// middleware.ts
import { withAuth } from "next-auth/middleware";
import { NextResponse } from "next/server";
export default withAuth(
// `withAuth` augments the Next.js request with the user's token.
function middleware(req) {
const { pathname } = req.nextUrl;
// Example: Only allow authenticated users to access /dashboard
if (pathname.startsWith("/dashboard") && !req.nextauth.token) {
return NextResponse.redirect(new URL("/auth/signin", req.url));
}
// Allow access to public pages or authenticated pages
return NextResponse.next();
},
{
callbacks: {
authorized: ({ token }) => !!token, // Only allow if token exists
},
pages: {
signIn: "/auth/signin",
},
}
);
export const config = {
matcher: ["/dashboard/:path*", "/api/protected/:path*"], // Apply middleware to these paths
};Loading & Error States
A robust fullstack app provides clear feedback. Next.js App Router helps with dedicated files for loading and error states:
loading.js: Automatically displays a loading UI for a route segment while its content is being fetched on the server.error.js: Creates UI boundaries to catch and display errors gracefully for a route segment, preventing the whole app from crashing.
For Server Actions, you can also use client-side states like isPending from useFormStatus to show loading indicators.
The Fullstack Integration Flow
Let's visualize how these components work together in our task manager:
- A user tries to access
/dashboard. middleware.tschecks their authentication status via NextAuth.js.- If authenticated,
dashboard/page.tsx(an RSC) fetches user-specific tasks from Prisma. TaskList.tsx(an RCC) renders these tasks and allows interactive toggling of completion status via a Server Action.- A separate form (RCC) uses another Server Action to add new tasks, which then revalidates the
/dashboardpath.
This seamless interaction creates a powerful and efficient user experience.
Fullstack Integration Check
Consider a Next.js fullstack application. Which of the following statements correctly describe the typical use of Server Components, Client Components, Server Actions, or Middleware?
Fullstack App Recap
You've just walked through the process of building a comprehensive fullstack Next.js application! We integrated several key features:
- Prisma ORM for database interaction.
- NextAuth.js for secure authentication.
- React Server Components for efficient server-side data fetching.
- Next.js Server Actions for secure data mutations and revalidation.
- React Client Components for adding rich client-side interactivity.
- Next.js Middleware for protecting routes.
This lesson ties together many foundational concepts, providing you with a robust understanding of how to build complex, production-ready Next.js applications.
AI 튜터와 함께 TypeScript을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 22
- 레슨
- 88
자주 묻는 질문
“풀스택 애플리케이션 구축” 강의는 무료인가요?
네 — “풀스택 애플리케이션 구축” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Next.js 15 Fullstack (App Router + Server Actions) 강의 전체를 잠금 해제할 수 있습니다. Next.js 15 Fullstack (App Router + Server Actions) 강의에는 총 4개의 강의가 포함되어 있습니다.
“풀스택 애플리케이션 구축”에서 뭘 배우나요?
종합 프로젝트를 진행하며 지금까지 학습한 모든 개념을 완전한 풀스택 Next.js 애플리케이션에 통합합니다. 브라우저에서 직접 실행하는 실습 코드로 Next.js 15 Fullstack (App Router + Server Actions)을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Next.js 15 Fullstack (App Router + Server Actions)을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Next.js 15 Fullstack (App Router + Server Actions)은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“풀스택 애플리케이션 구축” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Next.js 15 Fullstack (App Router + Server Actions) 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Next.js 15 Fullstack (App Router + Server Actions) 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- Vercel에 배포하기
- 풀스택 애플리케이션 구축
- 프로젝트 검토 및 모범 사례
- 프로덕션 모니터링과 오류 추적