서버 액션
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
서버 액션을 사용하면 서버에서 함수를 정의하고 프레임워크가 네트워크 레이어를 추상화한 상태로 클라이언트 컴포넌트에서 직접 호출할 수 있습니다.
tRPC 프로시저를 사용해 서버 액션을 정의하면 입력 유효성 검사, 미들웨어를 통한 인증 및 권한 부여, 출력 유효성 검사, 데이터 변환기 등 tRPC의 모든 내장 기능을 활용할 수 있습니다.
서버 액션 통합은 experimental_ 접두사를 사용하며 아직 활발히 개발 중입니다. 향후 릴리스에서 API가 변경될 수 있습니다.
서버 액션 프로시저 설정하기
1. experimental_caller로 기본 프로시저 정의하기
프로시저 빌더에 experimental_nextAppDirCaller와 함께 experimental_caller를 사용하여 일반 함수(서버 액션)로 호출 가능한 프로시저를 생성하세요. pathExtractor 옵션을 사용하면 메타데이터로 프로시저를 식별할 수 있어, 서버 액션이 user.byId 같은 라우터 경로를 갖지 않기 때문에 로깅 및 관측 가능성에 유용합니다.
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),);
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),);
2. 미들웨어로 컨텍스트 추가하기
서버 액션은 HTTP 어댑터를 거치지 않으므로 컨텍스트를 주입하는 createContext가 없습니다. 대신 세션 데이터 같은 컨텍스트를 제공하려면 미들웨어를 사용하세요:
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';import {currentUser } from '../auth';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),).use (async (opts ) => {constuser = awaitcurrentUser ();returnopts .next ({ctx : {user } });});
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';import {currentUser } from '../auth';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),).use (async (opts ) => {constuser = awaitcurrentUser ();returnopts .next ({ctx : {user } });});
3. 보호된 액션 프로시저 생성하기
인증이 필요한 액션을 위한 재사용 가능한 베이스를 만들려면 권한 부여 미들웨어를 추가하세요:
server/trpc.tstsexport constprotectedAction =serverActionProcedure .use ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {...opts .ctx ,user :opts .ctx .user , // ensures type is non-nullable},});});
server/trpc.tstsexport constprotectedAction =serverActionProcedure .use ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {...opts .ctx ,user :opts .ctx .user , // ensures type is non-nullable},});});
서버 액션 정의하기
"use server" 지시자를 포함한 파일을 생성하고 프로시저 빌더를 사용해 액션을 정의하세요:
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// opts.ctx.user is typed as non-nullable// opts.input is typed as { title: string }// Create the post...});
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// opts.ctx.user is typed as non-nullable// opts.input is typed as { title: string }// Create the post...});
experimental_caller 덕분에 프로시저는 이제 서버 액션으로 사용할 수 있는 일반 비동기 함수가 됩니다.
클라이언트 컴포넌트에서 호출하기
서버 액션을 임포트하여 클라이언트 컴포넌트에서 사용하세요. 서버 액션은 점진적 향상을 위한 action 속성과 onSubmit을 통한 프로그래매틱 호출 모두와 호환됩니다:
app/post-form.tsxtsx'use client';import {createPost } from '../_actions';export functionPostForm () {return (<form onSubmit ={async (e ) => {e .preventDefault ();consttitle = newFormData (e .currentTarget ).get ('title') as string;awaitcreatePost ({title });}}><input type ="text"name ="title" /><button type ="submit">Create Post</button ></form >);}
app/post-form.tsxtsx'use client';import {createPost } from '../_actions';export functionPostForm () {return (<form onSubmit ={async (e ) => {e .preventDefault ();consttitle = newFormData (e .currentTarget ).get ('title') as string;awaitcreatePost ({title });}}><input type ="text"name ="title" /><button type ="submit">Create Post</button ></form >);}
메타데이터로 관측 가능성 추가하기
.meta() 메서드를 사용해 로깅 또는 추적을 위한 액션에 태그를 지정하세요. 메타데이터의 span 속성은 pathExtractor로 전달되므로 관측 가능성 도구에서 활용할 수 있습니다:
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .meta ({span : 'create-post' }).input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// ...});
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .meta ({span : 'create-post' }).input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// ...});
서버 액션 vs 뮤테이션 사용 시기
서버 액션은 모든 tRPC 뮤테이션을 대체하지 않습니다. 다음과 같은 장단점을 고려하세요:
-
서버 액션 사용: 점진적 향상(JavaScript 없이 동작하는 폼)이 필요하거나, 액션이 클라이언트 측 React Query 캐시를 업데이트할 필요가 없는 경우
-
useMutation사용: 클라이언트 측 캐시 업데이트, 낙관적 업데이트 표시, UI에서 복잡한 로딩/오류 상태 관리가 필요한 경우
기존 tRPC API와 함께 서버 액션을 점진적으로 도입할 수 있습니다. 전체 API를 재작성할 필요는 없습니다.