본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 19. 11:30

React Async 시대의 프레임워크 fate 사용 후기

요약

React용 데이터 클라이언트인 fate 1.0 버전을 사용한 후기로, 기존의 요청 중심 방식이 아닌 'View' 중심의 데이터 취득 패러다임을 소개합니다. fate는 컴포넌트가 필요한 데이터를 View로 선언하면 이를 합성하여 한 번에 요청하고, 데이터를 Entity 단위로 정규화하여 캐싱하는 특징을 가집니다.

핵심 포인트

  • Thinking in Views: 요청(Request)이 아닌 뷰(View)를 중심으로 데이터 취득 설계
  • View 합성: 각 컴포넌트의 View를 root에서 집약하여 단일 요청으로 처리
  • Entity 정규화 캐시: 데이터를 ID 기반의 사전(Dictionary) 형태로 분해하여 효율적으로 관리
  • 선언적 데이터 관리: Suspense와 Error Boundary를 활용한 로딩 및 에러 처리

서론

최근 React용 데이터 클라이언트인 fate의 1.0 버전이 프로덕션 환경에서 사용 가능해져서 실제로 사용해 보았습니다.

실제로 작은 게시글 앱을 만들어 보았을 때, 가장 경험이 좋았던 점은 요청(Request)이 아니라, 뷰(View)를 중심으로 데이터 취득을 생각한다는 점이었습니다.

일반적인 React 앱에서는 데이터 취득 위치, loading/error 처리, 취득한 데이터의 전달 방식을 고민하는 경우가 많습니다.

fate에서는 먼저 "이 컴포넌트는 어떤 View를 필요로 하는가"부터 생각합니다.

이 기사에서는 fate의 컨셉과 구조를 정리하면서, 실제로 만든 샘플에서 좋았던 점과 현시점에서 조금 신경 쓰였던 점을 써 내려가고자 합니다.

이하가 이번에 작성한 샘플의 소스입니다.

fate란

fate는 React용 데이터 클라이언트입니다.

공식 Core Concepts에서는 fate의 중심에 있는 사고방식으로 Thinking in Views를 설명하고 있습니다.

React 앱에서는 컴포넌트(Component)와 훅(Hooks)을 중심으로 생각하는 경우가 많습니다.

fate는 여기에 제3의 프리미티브(Primitive)로서 views를 추가합니다.

View는 컴포넌트가 필요로 하는 데이터를 선언하는 것입니다.

export const PostListItemView = view<Post>()({
  id: true,
  title: true,
  ...

그리고 root 부근에서 useRequest에 전달합니다.

const { posts } = useRequest({
  posts: { list: PostListItemView },
});

여기서 fate가 해주는 일은 단순한 fetch가 아닙니다.

  • 컴포넌트가 필요한 View를 합성함
  • 화면에 필요한 데이터를 하나의 request로서 취득함
  • 취득한 PostComment를 Entity 단위로 정규화하여 캐시함 (자세한 내용은 후술)
  • useView를 통해 해당 컴포넌트가 선언한 View의 범위만 읽음
  • loading은 Suspense, error는 Error Boundary로 흘려보냄

즉, fate는 "API 응답을 어떻게 받을 것인가"보다 "컴포넌트가 어떤 데이터를 필요로 하고, 그것을 Entity로서 어떻게 동기화할 것인가"를 중심으로 하고 있다고 할 수 있습니다.

View는 GraphQL에서 말하는 fragment와 유사하다고 생각하면 이미지를 떠올리기 쉬울 것입니다.

컴포넌트로부터 View가 합성됨

fate의 흥미로운 점은 각 컴포넌트가 필요한 View를 가지고, 그것들이 root로 집약된다는 점입니다.

공식 문서의 그림에 있는 것처럼 다음과 같은 이미지입니다.

최종적으로 View는 root 부근의 useRequest로 모이며, fate가 한꺼번에 데이터를 취득합니다.

이를 통해 자식 컴포넌트만을 위해 부모에서 큰 타입을 정의하거나, props로 큰 데이터 바구니 전달(Prop Drilling)을 하는 상황을 줄일 수 있습니다.

Entity와 정규화 캐시

fate는 취득한 데이터를 Entity 단위로 정규화하여 캐시합니다.

fate의 Entity는 앱에서 다루는 의미 있는 데이터의 단위입니다.

예를 들어, Post, Comment, User 등의 데이터가 Entity입니다.

정규화 캐시는 API 응답 형식을 그대로 저장하는 것이 아니라, 취득한 데이터를 Entity별로 분해하여 저장하는 방식입니다.

대략적으로 말하면, 응답 전체를 통째로 저장하는 것이 아니라 Post:post-1과 같이 ID로 조회할 수 있는 사전(Dictionary) 형태로 캐시를 가집니다.

예를 들어, 게시글 상세 응답에 게시글과 댓글이 포함되어 있는 경우, 내부적으로는 다음과 같이 관리됩니다.

Post:post-1
  title
  likeCount
  ...

목록용 View, 상세용 View, 좋아요 버튼용 View가 각각 다르더라도, 같은 게시글이라면 내부적으로는 동일한 Post:post-1을 바라봅니다.

즉, fate에서는 동일한 Post는 화면 내 어디에서 읽히더라도 같은 Entity로 취급됩니다.

따라서 좋아요 mutation으로 Post.likeCount

이 변경되었을 때, 목록용 캐시, 상세용 캐시, 좋아요 버튼용 캐시를 각각 수동으로 동기화할 필요가 없으므로 명시적인 캐시 관리(Cache Management)가 불필요해집니다.

Entity(엔티티)를 알면 View(뷰)가 구체적으로 어떤 모습인지도 보이게 됩니다.

단도직입적으로 말해, View란 "해당 Entity의 어떤 필드(Field)를 읽을 것인가"를 나타내는 것입니다.

즉, PostListItemViewPostDetailView가 서로 다르더라도, 동일한 Post:post-1을 읽고 있다면 원본 데이터는 같은 곳에 있습니다.

덕분에 mutation(뮤테이션)이나 optimistic update(낙관적 업데이트)를 통해 Post:post-1이 업데이트되면, 동일한 게시물을 읽고 있는 다른 View에도 반영됩니다.

요약하자면, 정규화 (Normalization)란 응답(Response) 전체를 통째로 가지는 것이 아니라, Post:post-1과 같이 ID로 조회할 수 있는 단위로 나누어 가진다는 의미입니다.

Data Masking (데이터 마스킹)

fate에는 Data Masking이 있습니다. 이는 View에서 선언한 필드만 컴포넌트에서 다룰 수 있도록 하는 메커니즘입니다.

View에서 선택하지 않은 필드는 컴포넌트에서 다룰 수 없습니다.

예를 들어, 목록용 PostListItemViewcontent를 포함하지 않았다면, 목록 카드에서는 post.content를 전제로 한 구현을 할 수 없습니다.

이는 언뜻 제약 사항처럼 보일 수 있지만, 컴포넌트가 암묵적으로 불필요한 데이터에 의존하는 것을 방지해주기 때문에 유지보수 측면에서 상당히 효과적이라고 느꼈습니다.

Suspense와 Error Boundary에 맞추기

fate는 Async React(비동기 React)를 전제로 합니다.

데이터 로딩 중인 상태는 Suspense로 흘려보내고, 에러는 Error Boundary로 흘려보냅니다.

따라서 컴포넌트 내부에서 다음과 같이 매번 상태를 가지는 설계와는 조금 다릅니다.

if (isLoading) return <Loading />;
if (error) return <Error />;

물론 애플리케이션에 따라 명시적으로 상태를 다루고 싶은 상황도 있을 것입니다.

다만, 화면 단위의 데이터 호출에서는 Suspense / Error Boundary를 활용하는 것이 매우 자연스럽다고 생각합니다.

Mutation은 useActionState로 작성

fate는 데이터 변경을 위한 독자적인 hook을 제공하지 않습니다. 대신 React의 useActionState를 그대로 사용합니다.

const fate = useFateClient();
const [result, like] = useActionState(fate.actions.post.like, null);

optimistic update 역시 useActionState의 action에 optimistic을 전달하기만 하면 됩니다.

const LikeButton = ({ post }) => {
const fate = useFateClient();
const [result, like] = useActionState(fate.actions.post.like, null);
...

fate 고유의 mutation hook을 외울 필요가 없으며, React가 제공하는 useActionState 사용법을 알고 있다면 그대로 응용할 수 있습니다.

이러한 기존 hook을 알고 있다면 바로 구현할 수 있다는 점은 훌륭한 설계 철학이라고 생각합니다.

AI Agent와의 궁합

fate는 AI Agent와의 궁합도 좋아 보였습니다.

사실 이는 우연이 아니라 fate의 설계 철학에 명시적으로 포함되어 있습니다. 공식 블로그에서는 다음과 같이 언급하고 있습니다.

As coding agents write more and more of our code, reducing imperative cache management and request-centric state handling becomes more important, not less. fate is designed to eliminate those patterns at the framework level.
(코딩 에이전트가 점점 더 많은 코드를 작성함에 따라, 명령형 캐시 관리와 요청 중심의 상태 처리를 줄이는 것은 중요성이 낮아지는 것이 아니라 오히려 더 커지고 있습니다. fate는 프레임워크 레벨에서 이러한 패턴을 제거하도록 설계되었습니다.)

즉, 명령형(imperative) 캐시 관리나 요청 중심(request-centric)의 상태 관리를 줄이는 것은, 코딩 에이전트(coding agents)가 코드를 작성하는 비율이 높아질수록 중요해진다는 생각이 fate의 설계에 반영되어 있습니다.

구체적으로는 데이터 구조나 조작 가능한 뮤테이션(mutation)이 상당히 명시적이라는 점을 들 수 있습니다.

client.generated.ts를 통해 root / mutation / entity relation을 파악할 수 있습니다.

  • client view를 보면 컴포넌트가 필요한 데이터를 알 수 있습니다.
  • server DataView를 보면 DB에서 어떻게 해결되는지 알 수 있습니다.
  • mutation을 보면 Live event나 권한 체크를 추적하기 쉽습니다.

또한, 정규화된 캐시(normalized cache) 덕분에 AI 에이전트가 캐시 키(cache key) 설계나 캐시 업데이트 누락을 신경 쓸 필요가 없습니다. mutation을 작성하면 엔티티(Entity)가 자동으로 동기화되므로, "이 변경 후에 어떤 쿼리(query)를 무효화(invalidate)해야 하는가"라는 판단을 생략할 수 있습니다.

나아가, mutation에 useActionState를 사용하고 있기 때문에 AI 에이전트가 React의 학습 데이터를 그대로 활용할 수 있습니다. fate 고유의 API를 따로 학습시킬 필요 없이, React를 알고 있다면 바로 작성할 수 있습니다.

물론, AI 에이전트가 client.generated.ts만 본다고 해서 모든 것을 알 수 있는 것은 아닙니다.

다만 구조의 명시성, 캐시 관리의 자동화, 표준 API의 활용이라는 세 가지 요소가 맞물림으로써, AI 에이전트가 길을 잃지 않도록 설계되어 있다고 느낍니다.

fate 1.0에서 무엇이 좋아졌는가

지금까지의 내용만으로도 fate의 철학이 매우 매력적이라는 점을 이해하셨을 것입니다.

fate는 1.0에서 더욱 강력한 업데이트가 이루어졌습니다.

기능개요
Live Views & ListsServer-Sent Events (SSE)를 통한 실시간 업데이트. useLiveView / useLiveListView로 대응
Drizzle support1.0 이전에는 Prisma만 지원했으나, Drizzle 어댑터(adapter)가 추가됨
native HTTP transport1.0 이전에는 tRPC만 지원했으나, tRPC 없이 사용할 수 있는 HTTP 트랜스포트(transport)로서 Hono 등에 그대로 마운트할 수 있는 핸들러(Handler)가 제공됨
Vite plugin이전에는 CLI를 통한 수동 생성이 필요했으나, 서버 측 변경 시 client.generated.ts를 자동으로 재생성하는 Vite 플러그인이 추가됨
Cache lifetime & GC최근 10개의 요청분을 유지하면서 불필요한 데이터를 해제. 페이지 전환 시 뒤로 가기 속도를 높임
stable view refs동일한 엔티티(Entity)의 ViewRef를 안정화하여 불필요한 재렌더링(re-rendering)을 줄임

그중에서도 Live Views & Lists는 최근 실시간 기능에 대한 수요를 고려할 때 반드시 알아두어야 합니다.

Live Views & Lists

실시간 기능은 useViewuseLiveView로 교체하는 것만으로 구현할 수 있습니다.

이를 통해 해당 엔티티(Entity)가 SSE를 통해 실시간으로 업데이트됩니다.

const post = useView(PostDetailView, postRef); // 자신의 조작만 반영
const post = useLiveView(PostDetailView, postRef); // 다른 클라이언트의 변경 사항도 반영

댓글 목록과 같은 리스트(list)에는 useLiveListView를 사용합니다. 댓글의 추가 및 삭제가 실시간으로 리스트에 반영됩니다.

const [commentItems] = useLiveListView(
postCommentsConnection,
post.comments,
...

클라이언트 측에서 SSE를 직접 다루는 코드는 나타나지 않습니다.

서버(server) 측에서는 변경이 발생했을 때 대상 엔티티(Entity)나 리스트(list)를 지정하여 통지합니다.

liveEventBus.update("Post", postId, {
changed: ["likeCount", "likedByViewer"],
});

댓글 추가의 경우, Post 본체의 업데이트와 Post.comments 리스트에 대한 추가를 통지합니다.

liveEventBus.update("Post", postId, {
changed: ["comments", "commentCount"],
});

알림(Notification)이 request가 아니라 정규화된 Entity나 list에 연결되어 있기 때문에, 변경이 발생한 Entity를 읽고 있는 컴포넌트만 업데이트됩니다.

여기서도 역시, 쿼리(Query) 전체를 다시 가져올 필요는 없습니다.

지금까지 fate의 컨셉과 1.0 업데이트에 대해 소개했습니다.

이제부터는 실제로 만든 샘플을 보면서 해설해 나가고자 합니다.

만든 것

이번에는 다음과 같은 기능을 가진 간단한 게시물 앱을 만들었습니다.

  • 사용자 등록 / 로그인
  • 게시물 작성
  • 게시물 목록
  • 게시물 상세
  • 댓글 추가 / 삭제
  • 좋아요
  • Live 업데이트
  • 게시물 삭제

기술 스택(Tech Stack)은 다음과 같습니다.

  • Hono
  • Drizzle ORM
  • Turso / libSQL
  • Better Auth
  • React
  • TanStack Router
  • fate

디렉토리 구성은 다음과 같은 모노레포(Monorepo) 구성으로 되어 있습니다.

packages/
api/
src/
...

이 샘플에서의 fate 구성

이 샘플의 구현에서 fate의 구성은 다음과 같은 관계를 가집니다.

PostDetailViewPostLikeView를 분리한 것이 포인트입니다.

좋아요와 같이 업데이트 빈도가 높은 부분은 작은 View로 만들어 두면 영향 범위를 좁히기 쉬워집니다.

View 정의

다음은 이 샘플에서 실제로 정의한 View입니다.

import type { User } from "@app/api/features/auth/views";
import type { Comment } from "@app/api/features/comments/views";
import type { Post } from "@app/api/features/posts/views";
...

이제부터 View 단위로 살펴보겠습니다.

fate에서 편리한 점은 동일한 Entity라도 용도에 따라 View를 나눌 수 있다는 것입니다.

게시물 목록에서는 목록 카드에 필요한 필드만 선택합니다.

export const PostListItemView = view<Post>()({
id: true,
title: true,
...

상세 화면에서는 본문과 댓글 목록도 포함합니다.

export const PostDetailView = view<Post>()({
id: true,
title: true,
...

좋아요 버튼의 View는 업데이트 빈도가 높기 때문에 전용의 작은 View로 분리했습니다.

export const PostLikeView = view<Post>()({
id: true,
likedByViewer: true,
...

처음에는 상세 화면의 PostDetailViewlikeCount도 포함되어 있었습니다.

하지만 좋아요가 업데이트될 때마다 상세 화면 전체가 재평가(Re-evaluation)되고, 상세 화면 전체에 렌더링(Rendering)이 발생하여, 좋아요를 누를 때마다 화면이 깜빡이는 등 UX 측면에서 좋지 않은 상태였습니다.

그래서 좋아요 버튼 쪽에서만 PostLikeView를 읽도록 개선했습니다.

const { post: postRef } = useRequest({
post: { id: postId, view: PostLikeView },
});
...

이와 같이 View를 작게 나눔으로써 업데이트 범위를 좁히기 쉬워지며, 캐시(Cache) 등을 고려하지 않더라도 각 컴포넌트에 맞춰 이러한 조정을 쉽게 구현할 수 있다는 점이 매우 큰 포인트라고 다시 한번 느꼈습니다.

API 요청 설계로부터 조금은 자유로워질 수 있음

일반적인 REST API라면 다음과 같은 API를 생각하는 경우가 많을 것입니다.

GET /posts

GET /posts/:id

GET /posts/:id/comments

POST /posts/:id/comments

POST /posts/:id/like

물론, 이러한 방식도 심플하고 좋습니다.

한편, 화면이 늘어나면 「목록용 API」, 「상세용 API」, 「일부만 업데이트하는 API」, 「댓글 포함 API」 등이 늘어나기 쉽습니다.

fate에서는 서버 (server) 측에서 DataView를 정의해 두면, 클라이언트 (client) 측은 View에서 필요한 필드를 선택할 수 있습니다.

다음은 서버 (server) 측 DataView 정의의 발췌입니다.

export const postDataView = dataView<PostRow>("Post")({
id: true,
title: true,
...

API가 불필요해지는 것은 아닙니다. 다만, 화면마다 세세한 응답 형태의 API를 만드는 것보다 Entity와 View를 중심으로 생각하기가 쉬워집니다.

이를 통해 새로운 화면을 추가할 때도 서버 (server) 측의 DataView는 그대로 둔 채, 클라이언트 (client) 측의 View를 새로 정의하는 것만으로 대응할 수 있습니다.

Mutation과 optimistic update (낙관적 업데이트)

fate에서는 앞서 언급한 바와 같이, React의 useActionState를 사용하여 mutation을 기술합니다.

import type { Post } from "@app/api/features/posts/views";
import { useActionState } from "react";
import { useFateClient } from "react-fate";
...

'좋아요' 기능에서는 낙관적 업데이트 (optimistic update)를 수행하는데, 이 또한 간단하게 구현할 수 있습니다.

void like({
input: { postId: String(post.id) },
optimistic: {
...

이 업데이트는 fate의 정규화 캐시 (normalized cache)에 반영되므로, 동일한 Entity를 읽고 있는 View에도 반영됩니다.

live 기능을 구현한 페이지에서는 useLiveView에 전달할 ref를 useRequest로 가져옵니다.

function LiveLikeButton({ postId }: { postId: string }) {
// useRequest로 ref를 가져와 useLiveView에 전달
const { post: postRef } = useRequest({ post: { id: postId, view: PostLikeView } });
...

useLiveView는 데이터를 fetch하는 것이 아니라, 정규화 캐시 (normalized cache)에 있는 Entity에 대한 변경 통지를 받는 훅 (hook)입니다.

ref는 해당 Entity가 캐시의 어디에 있는지를 가리키는 포인터이므로, 먼저 useRequest로 캐시에 올려두어야 합니다. ID만 전달하는 설계로 하지 않는 이유는, 데이터 마스킹 (Data Masking)을 위해 「어떤 View에서 가져왔는지」에 대한 정보가 필요하기 때문입니다.

지금까지 구현 내용을 살펴보았는데, fate 사용 여부를 판단하는 데 있어 React 데이터 페칭 (data fetching)에서 널리 사용되는 TanStack Query와의 비교도 정리해 두고자 합니다.

TanStack Query와의 비교

TanStack Query는 완성도가 매우 높으며, API 요청을 중심으로 한 설계에서는 지금도 제1순위 후보가 되기 쉽습니다.

반면, fate는 사고방식이 조금 다릅니다.

관점TanStack Queryfate
중심queryKeyView / Entity
...

예를 들어 TanStack Query로 실시간 업데이트를 다룰 경우, 다음과 같은 사항들을 고민하는 경우가 많습니다.

  • queryKey를 어떻게 설계할 것인가
  • WebSocket / SSE 이벤트를 어떤 query에 반영할 것인가
  • invalidateQueries를 할 것인가 - setQueryData로 수동 업데이트를 할 것인가
  • 목록과 상세를 어떻게 동시에 동기화할 것인가

fate에서는 다음과 같이 Entity나 connection에 대해 event를 보냅니다.

liveEventBus.update("Post", postId, {
changed: ["likeCount"],
});
liveEventBus
.connection("Post.comments", { id: postId })
.appendNode("Comment", commentId);

요약하자면, TanStack Query는 「API를 어떻게 캐싱할 것인가」에 특화되어 있습니다.

반면, fate는 「화면에 필요한 데이터를 어떻게 선언하고, Entity (엔티티)로서 동기화할 것인가」에 특화되어 있습니다.

이 차이는 상당히 크기 때문에, 애플리케이션의 요구사항에 따라 적절히 나누어 사용하는 것이 좋아 보입니다.

예를 들어, TanStack Query가 적합한 케이스로는 다음과 같은 것들이 있습니다.

  • 기존의 REST API가 있으며, 이를 그대로 활용하고 싶은 경우
  • 실시간 업데이트가 필요하지 않거나, 단순한 invalidate (무효화)만으로 충분한 경우

fate의 경우에는 다음과 같은 케이스가 적합하다고 생각합니다.

  • 컴포넌트 레벨에서 최적화하고 싶은 경우 (오버페칭 (Over-fetching) 없이 퍼포먼스를 높이고 싶은 경우)
  • 실시간 업데이트가 있으며, 그 외의 실시간 기능을 가진 솔루션 (Convex, Instant DB, TanStack DB, Electric 등의 Sync Engine 등)을 사용할 수 없는 경우

개인적으로는, Entity 중심의 데이터 설계와 실시간 업데이트를 양립하고 싶은 프로젝트에서 fate는 상당히 유력한 선택지라고 생각합니다.

반면, 기존 API가 있거나 사례의 풍부함을 우선시한다면 TanStack Query가 안정적인 선택지가 됩니다.

気になったところ (궁금했던 점)

마지막으로, 실제로 사용해 보면서 어려움을 겪었던 부분 등을 포함하여, 사전에 알아두면 좋을 점들에 대해 소개하겠습니다.

ViewRef의 제약을 이해해야 함

예를 들어, PostDetailView로 만들어진 postRefPostLikeView로 읽으려고 하면 에러가 발생합니다.

Invalid view reference

이 경우에는, 부모 View에 필요한 View를 spread 하거나, 별도로 useRequest를 통해 해당 View용 ref를 가져와야 합니다.

저의 경우, 좋아요 버튼 쪽에서 PostLikeView용으로 별도의 request를 하는 방식으로 구현했습니다.

버튼만 작은 View로 읽을 수 있어서 업데이트 범위도 좁게 유지하기 쉬웠습니다.

이는 구조적으로 타당하지만, 처음에는 직관과 다소 어긋나는 부분이었습니다.

Live list는 명시적인 connection event가 필요함

Post 본체의 업데이트라면 다음과 같이 하면 됩니다.

liveEventBus.update("Post", postId);

하지만 댓글 목록과 같은 list를 업데이트할 경우에는 connection event도 필요합니다.

liveEventBus
.connection("Post.comments", { id: postId })
.appendNode("Comment", commentId);

삭제라면 다음과 같이 해야 합니다.

liveEventBus
.connection("Post.comments", { id: postId })
.deleteEdge("Comment", commentId);

요컨대, Entity의 업데이트와 list의 업데이트를 나누어 생각해야 한다는 뜻입니다.

TanStack Query의 queryKey 관리보다 편하게 느껴지는 부분도 있지만, update()만으로 list까지 전부 알아서 업데이트되는 것은 아니라는 점에 주의가 필요합니다.

복합 주키(Composite Primary Key) 주변은 직접 구현이 필요할 수 있음

이번에 like 테이블은 postId + userId의 복합 주키로 설정했습니다.

이 경우, fate의 count("likes")가 제대로 작동하지 않았기 때문에, likeCount는 명시적으로 Drizzle을 사용하여 count했습니다.

likeCount: computed<PostRow, number>({
resolve: async (item) => {
const [row] = await db
...

Drizzle adapter가 많은 부분을 해결해 주지만, 이러한 케이스에서는 명시적으로 SQL을 작성해야 하는 상황도 있습니다.

아직 생태계가 넓지 않음

fate 1.0에서 Drizzle support와 native HTTP transport가 도입되어 상당히 시도해 보기 좋아졌습니다.

다만, 현 시점에서는 TanStack Query처럼 주변 지식이나 사례가 많지는 않습니다.

예를 들어, ElysiaJS를 위한 공식 adapter는 아직 없습니다.

기존의 큰 애플리케이션에 도입하는 경우에는, 우선 일부 화면이나 신규 기능에서 테스트해보는 것이 현실적이라고 생각합니다.

요약

"API 요청 중심"이 아니라 Core Concepts에 있는 "View 중심"으로 데이터 취득을 생각하는 경험(Thinking in Views)은, 실제로 만들어보니 상당히 납득이 가는 부분이 있었습니다.

우려되는 점(ViewRef의 제약, Live list의 connection event, 생태계의 좁음)은 있지만, Entity 중심의 캐시 설계와 실시간 업데이트를 자연스럽게 다룰 수 있다는 점은 상당한 강점입니다.

특히, AI Agent에게 코드를 작성하게 하는 것을 전제로 생각하면, client.generated.ts나 View 정의로부터 앱의 데이터 구조를 읽어내기 쉽다는 점과 캐시 키(Cache Key) 관리 및 캐시 업데이트 처리의 구현 누락을 신경 쓰지 않아도 된다는 점은 매우 큰 강점이 될 것 같습니다.

이러한 이유로 앞으로 fate의 생태계가 확장되어, 유력한 선택지가 될 것이라고 저는 생각합니다.

이번 기회에 꼭 한번 접해보시는 것은 어떨까요?

본 기사가 조금이라도 도움이 되기를 바랍니다.

참고 문헌

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
1

댓글

0