전체 프로젝트

GENERATIVE AI · REALTIME

생성되는 콘텐츠와 기다리는 사용자를 연결하다.

GenWave

콘텐츠 생성 결과는 SSE로 표시하고, 생성 완료 알림은 Next.js에서 분리한 Redis Pub/Sub·WebSocket 알림 서버로 사용자에게 전달했습니다.

PERIOD
2024.10 — 2025.04
ROLE
프론트엔드 선임 · 알림 백엔드 개발
PROJECT
팀 프로젝트
Next.jsTypeScriptNode.jsRedisWebSocketSSEPM2
GENWAVEPRODUCT SCREEN
genwave.ploonet.com
GenWave 멀티모달 AI 콘텐츠 생성 서비스 화면
생성 완료 알림Redis Pub/Sub + WebSocket
CREATE. NOTIFY. EXPLORE.↗

01 / CONTEXT & ROLE

어떤 문제를 풀었나요?

멀티모달 AI 콘텐츠는 생성에 시간이 걸려, 사용자가 기다리는 동안 다른 화면으로 가거나 서비스를 떠나기도 합니다. 생성 과정은 실시간으로 보여주고, 완료 소식은 사용자가 어디에 있든 놓치지 않게 전달해야 했습니다. 검색 유입과 서비스 운영도 함께 고려해야 했습니다.

제가 맡은 일

Next.js 기반 프론트엔드, SSE를 활용한 생성 결과 UI, Node.js·TypeScript 알림 서버(Express·ws·ioredis)와 Redis Pub/Sub·WebSocket 연동, SEO 최적화 및 배포 환경 구성을 담당했습니다.

02 / DEEP DIVE

완료 알림을 어떻게 놓치지 않게 전달했나요?

문제
생성이 끝나는 순간 사용자가 접속해 있다는 보장이 없습니다. 연결된 사용자에게만 바로 보내면, 그 사이 접속을 끊은 사용자는 결과가 나온 줄도 모르게 됩니다.
접근
완료 이벤트는 Redis Pub/Sub로 알림 서버에 전달했습니다. 알림 서버는 이벤트를 받으면 먼저 Redis에 저장하고(사용자별 정렬 집합 + 알림별 해시), 그 사용자가 WebSocket으로 연결돼 있으면 즉시 보냈습니다. 연결이 없으면 저장만 해두고, 다음에 알림 목록을 열 때 확인하게 했습니다. 생성 과정처럼 한 방향으로 흐르는 데이터에는 SSE를 사용해 역할을 나눴습니다.
결과
접속 여부와 상관없이 생성 완료 알림이 남아, 수천 명 규모의 동시 사용자에게 알림을 제공하면서도 접속하지 않은 사이 생긴 알림을 잃지 않게 되었습니다.

03 / ENGINEERING DECISIONS

구현을 이끈 기술적 결정.

01

생성 결과를 실시간으로 보여주기

Server-Sent Events(SSE)를 활용해 멀티모달 AI 콘텐츠의 생성 결과를 실시간으로 표시하는 동적 UI를 구현했습니다.

SSENext.js실시간 UI
02

알림 연결을 따로 빼내기

처음에는 알림도 SSE로 받았습니다. 그런데 생성 진행 표시도 SSE를 쓰고 있어, 알림 연결까지 계속 열어두면 HTTP/1.1에서 도메인당 6개로 제한된 브라우저 연결을 오래 점유해 다른 요청이 대기할 수 있었습니다. 알림 SSE는 Next.js 라우트를 거쳐 중계되어, 프록시 버퍼링과 서버의 연결 유지 부담도 함께 안고 있었습니다. 그래서 알림을 별도 Node.js 서버로 분리하고 브라우저가 WebSocket으로 직접 연결하도록 바꿨습니다. 요청마다 끝나는 생성 진행 표시는 SSE로 남겨 역할을 나눴습니다.

SSE → WebSocketHTTP/1.1 연결 제한서버 분리
03

보내기 전에 먼저 저장하기

알림 서버는 Redis 채널로 받은 이벤트를 사용자별 정렬 집합과 알림별 해시에 먼저 저장한 뒤, 연결된 사용자에게만 WebSocket으로 전송합니다. 접속 여부가 알림의 존재를 좌우하지 않도록 저장과 전송의 순서를 정했습니다.

Redis Pub/SubWebSocketZSET · HASH
04

전체 공지는 한 번만 저장하기

전체 공지를 사용자마다 복사해 저장하지 않고, 공지용 정렬 집합 하나에 저장한 뒤 알림 목록을 조회할 때 개인 알림과 합쳤습니다. 사용자가 늘어도 공지 발송 비용이 늘지 않습니다.

Fan-out on readRedisNode.js
05

쌓이지 않는 알림 저장소

알림마다 3일 보관 기한을 두고, 12시간마다 오래된 목록을 정리했습니다. 목록은 공지와 개인 알림을 합쳐 최대 100개로 제한하고, 미확인 여부 확인과 읽음 처리는 Redis 파이프라인으로 묶어 왕복 횟수를 줄였습니다.

TTLPipelineScheduled cleanup
06

발견되는 서비스, 이어지는 운영

Next.js SSR 기반으로 SEO를 최적화해 구글 검색을 통한 신규 사용자 유입률 향상에 기여했습니다. PM2를 도입해 무중단 배포 환경을 구성했습니다.

SSRSEOPM2

04 / HOW IT CONNECTS

기능을 연결하는 흐름.

  1. 01이벤트 발행Redis Pub/Sub
  2. 02알림 저장ZSET · HASH · 3일 보관
  3. 03즉시 전송WebSocket · 접속 중
  4. 04나중에 확인알림 목록 · 미확인 표시

생성 완료 알림의 개념 흐름입니다. 먼저 저장한 뒤 전송하므로, 접속하지 않은 사용자도 다음에 알림을 확인할 수 있습니다. 콘텐츠 생성 결과를 표시하는 SSE UI는 별도로 구현했습니다.

05 / OUTCOME

서비스에 남긴 변화.

  • 수천 명 동시 사용자 대상 콘텐츠 생성 완료 알림 제공
  • 저장 후 전송 구조로 접속하지 않은 사용자의 알림 유실 방지
  • 알림을 별도 서버·WebSocket으로 분리해 생성 스트림·API 요청과 브라우저 연결을 나눠 사용
  • SSE 기반 실시간 생성 결과 UI로 서비스 반응성 향상
  • SSR 기반 SEO 최적화와 PM2 무중단 배포 환경 구성

다시 만든다면

  • 알림 서버를 여러 대로 늘리면 모든 서버가 같은 채널을 구독해 알림이 서버 수만큼 저장됩니다. 저장은 발행하는 쪽 한 곳에서 하고, 서버는 전송만 맡도록 나누겠습니다.
  • 사용자당 연결을 하나만 관리해, 여러 탭을 열면 마지막 탭만 알림을 받습니다. 사용자마다 연결 목록을 두겠습니다.
  • 알림 목록을 열면 전부 읽음으로 처리했고, 공지의 읽음 상태는 모든 사용자가 공유했습니다. 읽음 상태를 사용자별로 분리하고, 실제로 확인한 항목만 읽음 처리하겠습니다.
  • 정리 작업에서 KEYS 명령으로 사용자를 찾았습니다. 데이터가 늘면 Redis 전체를 잠시 멈추게 하므로 SCAN이나 별도 사용자 인덱스로 바꾸겠습니다.

자료 안내

상단 이미지는 실제 GenWave 서비스 화면입니다.

NEXT CASE STUDY

삼성 E&A · CUBE 2.0

06 / LET’S CONNECT

함께 풀어갈 문제를
기다립니다.

새로운 기회나 기술에 관한 대화, 편하게 연락 주세요.