>웹 프론트엔드 >JS 튜토리얼 >반응 - 서버 작업

반응 - 서버 작업

WBOY
WBOY원래의
2024-08-09 07:34:121023검색

양식 작업에 반응합니다.

React는 기본 양식을 향상하고 클라이언트-서버 통신을 간소화하기 위해 새로운 양식 작업 및 관련 후크를 도입했습니다. 이러한 기능을 통해 개발자는 양식 제출을 보다 효과적으로 처리하여 사용자 경험과 코드 유지 관리성을 향상시킬 수 있습니다. React 양식 작업에 대한 심층적인 탐색은 React Form Actions에 대한 내 게시물의 자세한 게시물을 참조할 수 있습니다.

서버 작업

React 18에서는 서버 구성 요소 기능이 도입되었습니다. 서버 구성 요소는 SSR(서버 측 렌더링)이 아니며, 서버 구성 요소는 런타임 및 빌드 시간 동안 서버에서만 실행됩니다. 이러한 구성 요소는 데이터베이스 및 파일 시스템과 같은 서버 측 리소스에 액세스할 수 있지만 이벤트 리스너 또는 후크와 같은 클라이언트 측 작업을 수행할 수는 없습니다.

전제 조건

서버 구성 요소와 서버 작업의 기능을 보여주기 위해 Next.js와 Prisma를 사용하겠습니다.

Next.js는 풀스택 웹 애플리케이션 구축을 위한 React 프레임워크입니다. React 구성 요소를 사용하여 사용자 인터페이스를 구축하고 Next.js를 사용하여 추가 기능과 최적화를 수행합니다. 내부적으로 Next.js는 번들링, 컴파일 등과 같이 React에 필요한 도구를 추상화하고 자동으로 구성합니다. 이를 통해 구성에 시간을 낭비하는 대신 애플리케이션 구축에 집중할 수 있습니다. 자세히 알아보기

Prisma는 데이터베이스 액세스 및 운영을 단순화하는 ORM으로, SQL을 작성하지 않고도 데이터를 쿼리하고 조작할 수 있습니다. 자세히 알아보기

초기 설정
새로운 Next.js 애플리케이션을 만드는 것부터 시작하세요.
원사 생성 다음 앱 서버 예시

초기 폴더 구조는 다음과 같습니다.

React - Server Actions

서버 작업을 포함한 React 19 기능에 액세스하려면 Canary 릴리스로 업그레이드하세요.

yarn add next@rc react@rc react-dom@rc

Prisma 설치

yarn add prisma

프리즈마 구성
src/lib/prisma/schema.prisma에 Prisma 스키마 파일을 생성하세요:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  age Int
}

시연 목적으로 SQLite를 사용하고 있습니다. 프로덕션에서는 더욱 강력한 데이터베이스를 사용해야 합니다.

다음으로 src/lib/prisma/prisma.ts에 Prisma 클라이언트 파일을 추가하세요

// ts-ignore 7017 is used to ignore the error that the global object is not
// defined in the global scope. This is because the global object is only
// defined in the global scope in Node.js and not in the browser.

import { PrismaClient } from '@prisma/client'

// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
//
// Learn more:
// https://pris.ly/d/help/next-js-best-practices

const globalForPrisma = global as unknown as { prisma: PrismaClient }

export const prisma = globalForPrisma.prisma || new PrismaClient()

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

export default prisma

package.json에서 Prisma 구성:

{
  //other settings
  "prisma": {
    "schema": "src/lib/prisma/schema.prisma",
    "seed": "ts-node src/lib/prisma/seed.ts"
  }
}

tsconfig.json에서 TypeScript 설정을 업데이트하세요.

{
  //Other settings here...

  "ts-node": {
    // these options are overrides used only by ts-node
    // same as the --compilerOptions flag and the 
    // TS_NODE_COMPILER_OPTIONS environment variable
    "compilerOptions": {
      "module": "commonjs"
    }
  }
}

전역적으로 ts-node 설치:

yarn global add ts-node

초기 데이터 시드
초기 데이터를 채우려면 src/lib/prisma/seed.ts에 시드 파일을 추가하세요.

import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
  await prisma.user.create({
    email: "anto@prisma.io",
    name: "Anto",
    age: 35,
  });
  await prisma.user.create({
    email: "vinish@prisma.io",
    name: "Vinish",
    age: 32,
  });
}
main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

Prisma 클라이언트 설치

yarn add @prisma/client

마이그레이션 명령 실행:

yarn prisma migrate dev --name init

시드 데이터가 반영되지 않은 경우 수동으로 추가하세요.

yarn prisma db seed

좋아요! 설치가 준비되었으므로 데이터베이스 작업을 수행하는 액션 파일을 생성할 수 있습니다.

서버 작업 생성
서버 작업은 원활한 클라이언트-서버 상호 통신을 가능하게 하는 강력한 기능입니다. src/actions/user.ts에 데이터베이스 작업을 위한 파일을 생성해 보겠습니다:

"use server";
import prisma from '@/lib/prisma/prisma'
import { revalidatePath } from "next/cache";

// export type for user
export type User = {
  id: number;
  name: string | null;
  email: string;
  age: number;
};


export async function createUser(user: any) {
  const resp = await prisma.user.create({ data: user });
  console.log("server Response");
  revalidatePath("/");
  return resp;
}

export async function getUsers() {
  return await prisma.user.findMany();
}

export async function deleteUser(id: number) {
  await prisma.user.delete({
    where: {
      id: id,
    },
  });
  revalidatePath("/");
}

서버 구성 요소 구현

데이터베이스에서 데이터를 읽고 렌더링하는 React 서버 구성 요소를 만들어 보겠습니다. src/app/serverexample/page.tsx 만들기:

import UserList from "./Users";
import "./App.css"

export default async function ServerPage() {
  return (
    <div classname="app">
      <header classname="App-header">
        <userlist></userlist>
      </header>
    </div>
  );
}

src/app/serverexample/App.css에 일부 스타일 추가

.App {
  text-align: center;
}

.App-logo {
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

input {
  color: #000;
}

.App-link {
  color: #61dafb;
}

사용자 목록을 가져오고 렌더링하는 구성 요소 만들기:
src/app/serverexample/UserList.tsx

import { getUsers } from "@/actions/user";
import { UserDetail } from "./UserDetail";

export default async function UserList() {
  //Api call to fetch User details
  const users = await getUsers();

  return (
    <div classname="grid grid-cols-3 gap-5">
      {users.length ? (
        users.map((user) => <userdetail user="{user}"></userdetail>)
      ) : (
        <div classname="col-span3 opacity-60 text-sm text-center">
          No User found
        </div>
      )}
    </div>
  );
}

src/app/serverexample/UserDetail.tsx

export function UserDetail({ user }) {
  return (
    <div classname="flex items-center gap-4 border border-gray-600 py-1 px-4">
      <img classname="w-10 h-10 rounded-full" src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" alt="반응 - 서버 작업">
      <div classname="font-medium text-base dark:text-white">
        <div>{user.name}</div>
        <div classname="text-sm text-gray-500 dark:text-gray-400">
          {user.email}
        </div>
      </div>
    </div>
  );
}

개발 서버 실행:

yarn dev

렌더링된 사용자 목록을 보려면 http://localhost:3000/serverexample로 이동하세요.
React - Server Actions

기본적으로 Next.js의 구성 요소는 "클라이언트 사용" 지시문을 지정하지 않는 한 서버 구성 요소입니다. 두 가지 중요한 사항을 확인하세요.

  1. 비동기 구성 요소 정의: 서버 구성 요소는 다시 렌더링되지 않고 한 번만 생성되므로 비동기적일 수 있습니다.
  2. 데이터 가져오기: 줄 const users = wait getUsers(); 서버에서 데이터를 가져와 런타임에 렌더링합니다.

서버 작업 탐색

서버 작업을 통해 원활한 클라이언트-서버 상호 통신이 가능합니다. 신규 사용자 생성을 위한 양식을 추가해 보겠습니다.

src/app/serverexample/AddUser.tsx에서 새 파일을 생성하세요.

"use client";

import "./app.css";
import { useActionState } from "react";
import { createUser } from "../../actions/user";

const initialState = {
  error: undefined,
};

export default function AddUser() {
  const submitHandler = async (_previousState: object, formData: FormData) => {
    try {
      // This is the Server Action method that transfers the control 
      // Back to the server to do DB operations and get back the result.
      const response = await createUser({
        name: formData.get("name") as string,
        email: formData.get("email") as string,
        age: parseInt(formData.get("age") as string),
      });
      return { response };
    } catch (error) {
      return { error };
    }
  };
  const [state, submitAction, isPending] = useActionState(
    submitHandler,
    initialState
  );

  return (
    <div classname="mt-10">
      <h4 classname="text-center">Add new User</h4>{" "}
      <form action="%7BsubmitAction%7D" classname="text-base">
        <div classname="mt-6 text-right">
          Name:{" "}
          <input classname="ml-2" required name="name" type="text" placeholder="Name">
        </div>
        <div classname="mt-6 text-right">
          Email:{" "}
          <input classname="ml-2" name="email" type="email" placeholder="Email">
        </div>
        <div classname="mt-6 text-right">
          Age:{" "}
          <input classname="ml-2" name="age" type="text" placeholder="Age">
        </div>
        <div classname="mt-6 text-right">
          <button disabled classname="bg-green-600 text-white px-5 py-1 text-base disabled:opacity-30">
            {isPending ? "Adding" : "Add User"}
          </button>
        </div>

        {(state?.error as string) && <p>{state.error as string}</p>}
      </form>
    </div>
  );
}

AddUser 구성 요소를 포함하도록 src/app/serverexample/page.tsx를 업데이트하세요.

import UserList from "./UserList";
// Import new line
import AddUser from "./AddUser";
import "./App.css"

export default async function ServerPage() {
  return (
    <div classname="app">
      <header classname="App-header">
        <userlist></userlist>
        {/* insert Add User here */}
        <adduser></adduser>
      </header>
    </div>
  );
}

이제 애플리케이션을 실행하면 서버 측 처리가 원활하게 처리되면서 양식을 통해 새 사용자를 추가할 수 있습니다.
React - Server Actions

The AddUser Component and Seamless Client-Server Interaction

The AddUser component is at the heart of this example, showcasing how React Server Actions can revolutionize the way we handle client-server interactions. This component renders a form for adding new users and leverages the useActionState hook to create a smooth and seamless bridge between the client-side interface and server-side operations.

How It Works

  1. Form Rendering and Data Handling:
  • The AddUser component provides a form where users can input their name, email, and age.
  • Upon form submission, the data is captured and prepared to be sent to the server.
  1. useActionState Hook:
  • The useActionState hook is a crucial part of this setup. It simplifies the complexity of managing client-side state and server-side actions by abstracting them into a unified interface.
  • This hook accepts an asynchronous handler function, which processes the form data and then calls a Server Action method.
  • The brilliance of this approach lies in its abstraction: it feels as though you’re invoking a regular function within the same file, even though it actually triggers a server-side operation.
  1. Server Action Method:
  • The createUser function, defined as a Server Action, executes on the server side. It takes the user data from the form, performs the necessary database operations via Prisma, and returns the result.
  • This server-side method is crucial for maintaining a clean separation between the client and server, while still enabling them to communicate effectively.
  1. Seamless Integration:

From the perspective of a developer working on the client side, it appears as if the form submission is handled locally. However, the heavy lifting such as database manipulation occurs on the server.
The useActionState hook encapsulates this process, managing the state transitions and handling errors, while maintaining an intuitive API for developers.

Server Actions Without Forms

So that's with forms, now lets test an example without forms.
update src/app/serverexample/UserDetail.tsx

"use client";
import { deleteUser } from "@/actions/user";
import { useTransition } from "react";

export function UserDetail({ user }) {
  const [pending, startTransition] = useTransition();

  const handleDelete = () => {
    startTransition(() => {
      deleteUser(user.id);
    });
  };

  return (
    <div classname="flex items-center gap-4 border border-gray-600 py-1 px-4">
      {pending ? (
        <p>Deleting...</p>
      ) : (
        
          <img classname="w-10 h-10 rounded-full" src="https://api.dicebear.com/9.x/personas/svg?seed=Shadow" alt="반응 - 서버 작업">
          <div classname="font-medium text-base dark:text-white">
            <div>{user.name}</div>
            <div classname="text-sm text-gray-500 dark:text-gray-400">
              {user.email}
            </div>
          </div>
          <button classname="ml-auto" onclick="{handleDelete}">
            <img classname="w-4 h-4" src="/delete.png" alt="">
          </button>
        >
      )}
    </div>
  );
}

Key Points:

  • Server Action: deleteUser(user.id) is a server action that removes the user from the database. This operation is triggered without any form submission.
  • useTransition: This hook allows you to manage the asynchronous state of the deletion process, showing a "Deleting..." message while the operation is in progress.
  • User Interface: The component maintains a clean UI, dynamically updating based on the action status.

Now, you can seamlessly delete a user within the application:
React - Server Actions

Conclusion

This approach is transformative because it abstracts away the complexities of client-server communication. Traditionally, such interactions would require handling API endpoints, managing asynchronous requests, and carefully coordinating client-side state with server responses. With React Server Actions and the useActionState hook, this complexity is reduced, allowing developers to focus more on building features rather than worrying about the underlying infrastructure.

By using this pattern, you gain:

  • Cleaner Code: The client-side code remains simple and focused, without the need for explicit API calls.
  • Improved Developer Experience: Server-side operations are seamlessly integrated, reducing cognitive load and potential for errors.
  • Enhanced Performance: Server Actions are optimized for performance, reducing unnecessary client-server round trips and ensuring that server-side resources are used efficiently.

You can find the full code in the repository

위 내용은 반응 - 서버 작업의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:서비스 워커다음 기사:서비스 워커