Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Next.js에 실시간 기능을 손쉽게 통합하는 새로운 방법: Upstash Realtime

Next.js에 실시간 기능 추가하기, 생각보다 까다롭습니다 💀

여기서 말하는 실시간 기능은 다음과 같은 것들입니다.

  • Vercel AI SDK의 실시간 스트림 처리
  • 라이브 대시보드
  • 실시간 알림 및 상태 업데이트
  • 공유 커서 또는 '현재 접속 중인 사용자' 표시
  • 파일 업로드나 오래 걸리는 작업의 진행률 추적

물론 이런 용도로 쓸 수 있는 SaaS가 없어서는 아닙니다. 하지만 기존 서비스들을 살펴보면 한계가 분명합니다.

  • Ably — 비용이 비싸고 가격 정책도 불투명합니다.
  • Pusher — 타입 안전성이 부족하고 개발자 경험이 좋지 않습니다.
  • Convex, Supabase — 서비스 자체는 훌륭하지만, 실시간 기능 하나를 쓰려고 스택 전체를 마이그레이션해야 하는 부담이 있습니다.

Upstash Realtime이 지향하는 목표

개발자 입장에서 진짜 필요한 실시간 서비스의 조건은 명확했습니다.

  • 프론트엔드와 백엔드 모두 100% 타입 안전(Type-safe)
  • Vercel을 비롯한 다양한 서버리스 플랫폼 배포 지원
  • 연결 시간이 아닌 이벤트 기반 과금
  • Neon, PlanetScale 등 기존 데이터베이스와 그대로 함께 사용 가능

바로 이 조건들을 충족시키기 위해 Upstash Realtime을 만들고 있습니다. 어떤 Next.js 앱이든 실시간 기능을 추가할 수 있는 가장 쉬운 방법으로, 초기 설정은 단 2분이면 끝나고 Vercel, Cloudflare 등 어디에든 배포할 수 있습니다.

기존 해결책들이 모두 아쉬웠던 이유 🤡

Realtime에 대한 아이디어는 오픈소스 SaaS인 Contentport를 개발하면서 얻게 되었습니다. Contentport에서 트윗을 게시하면, 해당 게시 로직은 Upstash Workflow에서 실행되며 실패 시 자동으로 재시도됩니다.

덕분에 Twitter API가 다운되거나 AI 제공사에 장애가 발생하거나 그 외 어떤 문제가 생기더라도, 게시를 요청한 사용자에게 매우 높은 안정성을 보장할 수 있었습니다.

하지만 이 로직이 백그라운드 잡(background job)으로 동작하기 때문에 클라이언트와 게시 로직 사이에는 별도의 연결이 없습니다. 즉, '처리 중', '게시 중', '완료' 같은 현재 상태를 클라이언트에 전달할 방법이 없다는 문제가 있었죠.

Next.js에 실시간 기능을 손쉽게 통합하는 새로운 방법: Upstash Realtime

클라이언트에 상태 변화를 실시간으로 전달하려면 결국 메시지 브로커(message broker)가 필요했습니다. Pusher에는 만족스럽지 않았고, 실시간 기능 하나 때문에 기존에 쓰던 Neon Postgres 데이터베이스를 Convex로 옮기고 싶지도 않았습니다.

그런데 살펴보니, 가장 오래 검증된 제품인 Upstash Redis에 이미 필요한 모든 것이 갖춰져 있었습니다. 바로 Pub/Sub과 Redis Streams입니다.

간단한 사용 예제

서버 액션(server action)에서 클라이언트로 실시간 업데이트를 전송하는 간단한 예제를 만들어 보겠습니다.

먼저 패키지를 설치합니다.

npm install @upstash/realtime

다음으로 zod를 활용해 이벤트 스키마를 정의합니다.

lib/realtime.ts

import { InferRealtimeEvents, Realtime } from "@upstash/realtime";
import z from "zod/v4";
import { redis } from "./redis";
 
const schema = {
 notification: z.object({
 message: z.string(),
 }),
};
 
const realtime = new Realtime({ schema, redis });
export type RealtimeEvents = InferRealtimeEvents<typeof realtime>;

이제 어떤 라우트 핸들러(route handler)에서든 이벤트를 전송할 수 있습니다.

route.ts

import { realtime } from "@/lib/realtime";
 
await realtime.emit("notification", { message: "Hello world!" });

React 컴포넌트에서는 다음과 같이 이벤트를 구독합니다.

page.tsx

import { RealtimeEvents } from "@/lib/realtime";
import { useRealtime } from "@upstash/realtime/client";
 
useRealtime<RealtimeEvents>({
 event: "notification",
 onData: ({ message }) => {
 console.log(message);
 },
});

이게 전부입니다! 이제 서버에서 클라이언트로 완전히 타입 안전한 실시간 업데이트를 주고받을 수 있습니다.

알아두어야 할 점

Upstash Realtime은 Pusher의 1:1 대체재가 아닙니다. Pusher처럼 소켓(socket)이 아니라 HTTP 기반으로 동작하기 때문입니다.

HTTP 방식이 대체로 더 안정적이긴 하지만, 초당 15~20회를 넘는 초고빈도 업데이트가 필요하다면 소켓 연결을 제공하는 Pusher를 사용하는 것이 좋습니다.

반면 그 외의 대부분의 상황, 예를 들어 Vercel AI SDK의 고빈도 AI 청크(chunk) 스트리밍, 라이브 대시보드, 실시간 채팅 메시지 등에는 Upstash Realtime이 최선의 선택이 되도록 만들어 가고 있습니다.

직접 사용해 보세요. 시작하는 데 정말 몇 분도 걸리지 않습니다!

읽어주셔서 감사합니다! 🙌 궁금한 점이나 피드백이 있다면 @joshtriedcoding으로 언제든 연락해 주세요.