// server.ts"use server";export async function createNote() { await db.notes.create();}// client.ts"use client";// 컴파일러에 의해 다음과 같은 참조로 변환됩니다:// {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNote'}import { createNote } from './server';function EmptyNote() { return ( <button onClick={() => createNote()}>Create Note</button> );}fetch 요청을 작성하고, 데이터 직렬화 및 역직렬화를 처리해야 합니다. Server Actions는 이 모든 것을 추상화하여 원격 프로시저 호출(RPC)을 단순한 함수 호출처럼 느껴지게 합니다.'use cache' 지시문은 함수를 메모이제이션하고 그 결과를 캐싱하기 위한 강력한 도구입니다. 캐시 키는 함수의 인자와 그 클로저 값(주변 범위에서 "기억"하는 변수)으로부터 자동으로 생성됩니다.export async function Bookings({ type = 'haircut' }: BookingsProps) { 'use cache' async function getBookingsData() { const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`); return data; } return //...}'use workflow' 지시문은 내구성을 기본 언어 개념으로 만드는 것을 목표로 합니다.export async function aiAgentWorkflow(query: string) { "use workflow"; const response = await generateResponse(query); const facts = await researchFacts(response); const refined = await refineWithFacts(response, facts); return { response: refined, sources: facts };}'use workflow'를 통해 언어에 내구성을 내장함으로써 엄청난 인프라 복잡성을 추상화합니다.function serlializeClosure(fn) { const { closureData, fnCode } = extractClosedOverVariables(fn); return { code: fnCode, closure: closureData };}createNote 함수는 직렬화되어 클라이언트에 참조로 전송되고, 호출될 때 실행이 서버에서 트리거됩니다.// 단순화된 개념:// 1. 서버에서 함수와 그 클로저가 캡처됩니다.const { closure, code } = serializeClosure(createNote);const fnId = storeCodeOnServer(code);// 2. 참조가 클라이언트로 전송됩니다.sendToClient({ fnId, closure });// 3. 클라이언트는 참조를 사용하여 서버에서 함수를 호출합니다.invokeServerFunction({ fnId, closure, fnArgs: [...] });function getCacheKey(fn, args) { const { closure, code } = serializeClosure(createNote); return hash(fnCode, closureData, args);}await 지점에서 일시 중지되는 직렬화 가능한 클로저로 볼 수 있습니다. 함수의 상태와 그 연속(함수의 "나머지")이 직렬화되고 지속되어 나중에 재개될 준비가 됩니다.aiAgentWorkflow 함수는 각 단계에서 직렬화된 클로저의 시리즈로 시각화될 수 있습니다:function aiAgentWorkflow(query: string) { return generateResponse(query, (response) => { // 직렬화 클로저 1 researchFacts(response, (facts) => { // 직렬화 클로저 2 refineWithFacts(response, facts, (refined) => { // 직렬화 클로저 3 return { response: refined, sources: facts }; }); }); })}'use workflow' 환경은 완벽한 예입니다. Math.random과 Date.now 같은 API는 결정론적으로 동작해야 합니다. 효과 핸들러는 Date.now 호출을 가로채서 시스템 시간을 반환하는 대신 워크플로우의 이벤트 로그에서 타임스탬프를 반환하여 재생 가능성을 보장할 수 있습니다.// 데이터베이스 접근을 위한 효과 정의function dbAccessEffect(userQuery) { const user = yield* db.fetchEffect<User, Error, ServerContext>(userQuery); return user;}// 서버에서 핸들러는 데이터베이스 컨텍스트를 제공합니다const user = serverHandler.runEffect(dbAccessEffect, query); // OK// 클라이언트에서 핸들러는 오류를 던질 것입니다. db가 없기 때문입니다// clientHandler.runEffect(dbAccessEffect, query); // Throws Error'use cache' 같은 지시문은 명시적인 형태입니다."use cache"는 증분 계산의 단순한 형태로 볼 수 있습니다. 더 고급 시스템은 더 세밀한 수준에서 의존성을 추적하여 일부 데이터만 변경될 때 부분 재계산을 허용할 수 있습니다. 이 개념은 JavaScript 세계에서 skiplabs와 Skip langs와 같은 프로젝트로 탐색되었습니다.아직 댓글이 없습니다.
첫 번째 댓글을 작성해보세요!

Partial Prerendering
Inkyu Oh • Front-End

URL은 상태를 나타냅니다
Inkyu Oh • Front-End

React와 Remix가 선택한 서로 다른 미래
Inkyu Oh • Front-End

환영합니다!
Inkyu Oh • 공지사항