🚀 Next.js 심화 14장: Real-time Strategies — WS와 SSE, 서버리스와 실시간의 동행
📋 개요
WebSocket과 SSE를 Next.js에서 구현하는 방법과 서버리스 환경의 실시간 통신 전략입니다.
📋 목차
- 📌 이 문서를 읽기 전에
- 🤔 왜 알아야 하는가
- 🏗️ 비유로 먼저 이해하기
- 🗺️ WebSockets vs SSE vs Polling 🟢
- 🏗️ Next.js + 외부 WS 서버 (Spring) 연동 아키텍처 🟡
- 🛡️ Route Handler에서 SSE(Server-Sent Events) 구현하기 🟡
- 🚀 서버리스 환경의 실시간 한계와 외부 서비스(Pusher, Ably) 🔴
- 🏁 이번에 배운 내용 총정리
- 📝 마무리 퀴즈
- 🔗 더 알아보기
📌 이 문서를 읽기 전에
⏱️ 예상 읽기 시간: 22분 (전체) / 핵심 파트만: 11분
🗺️ 이 문서의 배경 세계관: '영수네 커뮤니티'
- 영숙(디자이너): "영수 님, 우리 커뮤니티 채팅창요. 메시지가 오면 유저가 매번 새로고침을 눌러야 확인되는 건 너무 90년대 스타일 아니에요?"
- 영수(PM): "듣고 보니 그렇네요! 영철 님, 우리도 배달의민족처럼 메시지가 실시간으로 '뿅' 하고 나타나게 해주세요."
- 영철(주니어): "문제없죠! Next.js 안에 바로
socket.io서버를 구축해 보겠습니다!" - 영호(리드): "영철 님... 스톱! Next.js(Vercel)는 서버리스 환경이에요. 서버가 항상 켜져 있는 게 아니라 요청이 올 때만 잠깐 깨어나요. 상시 연결이 필요한 WebSocket 서버를 Next.js 안에 직접 넣는 건 매우 위험한 생각입니다."
🎯 이 문서를 다 읽으면 할 수 있는 것
- 실시간 데이터 성격에 따라 WebSockets(WS)과 Server-Sent Events(SSE) 중 무엇이 적합한지 판단할 수 있다
- Next.js 외부(Spring 등)의 WebSocket 서버와 안전하게 통신하는 구조를 설계할 수 있다
- Route Handler를 이용해 별도 서버 없이도 단방향 실시간 알림(SSE)을 구현할 수 있다
🤔 왜 알아야 하는가
Next.js는 기본적으로 요청-응답(Request-Response) 모델에 최적화된 프레임워크야.
하지만 현대적인 앱은 실시간 채팅, 주식 뉴스, 라이브 알림이 필수지.
핵심은 서버리스와의 충돌을 이해하는 거야:
- WebSockets: 항시 연결을 유지해야 함. 서버리스(Lambda 등)는 30초~1분 후 꺼지려고 함.
- 해결책: 상시 켜져 있는 외부 서버(Node.js, Spring)를 쓰거나, 서버리스용 실시간 서비스(Pusher 등)을 써야 해.
🏗️ 비유로 먼저 이해하기
🛵 음식 배달로 설명한다면?
- Short Polling: 5분마다 배달 앱을 열어서 "치킨 왔어요? 왔어요?" 하고 묻는 것. (서버/클라이언트 모두 피곤)
- Server-Sent Events (SSE): 가게 주인이 전화를 끊지 않고 들고 있다가, 치킨이 나오면 "나왔어요!" 하고 소리치는 것. (한쪽만 말함)
- WebSockets (WS): 손님과 가게 주인이 무전기를 들고 있는 것. 실시간으로 서로 수다 떨 수 있음. (양방향 연결)
🗺️ WebSockets vs SSE vs Polling 🟢
| 방식 | 통신 방향 | 특징 | 적합한 경우 |
|---|---|---|---|
| Short Polling | 양측 가능 | 가장 간단하지만 리소스 낭비 심함 | 성능이 전혀 중요하지 않은 관리자 페이지 |
| SSE | 단방향 (S -> C) | HTTP 기반이라 가볍고 자동 재연결 지원 | 주식 시세, SNS 타임라인, 간단한 알림 |
| WebSockets | 양방향 | TCP 기반 상시 연결, 가장 빠름 | 실시간 채팅, 멀티플레이어 게임 |
🏗️ Next.js + 외부 WS 서버 (Spring) 연동 아키텍처 🟡
가장 추천하는 실무 구조야: 인증은 Next.js가, 통신은 Spring WebSocket 서버가!
// hooks/useChat.ts (Client Component 전용)
'use client'
import { useEffect, useState } from 'react'
export function useChat(roomId: string) {
const [messages, setMessages] = useState<string[]>([])
const [socket, setSocket] = useState<WebSocket | null>(null)
useEffect(() => {
// ✅ Next.js 밖의 Spring 서버 주소
const ws = new WebSocket(`wss://api.youngsu.com/chat/${roomId}`)
ws.onmessage = (event) => {
setMessages((prev) => [...prev, event.data])
}
setSocket(ws)
// ✅ 컴포넌트 언마운트 시 연결 해제 (중요!)
return () => ws.close()
}, [roomId])
const sendMessage = (msg: string) => {
socket?.send(msg)
}
return { messages, sendMessage }
}🛡️ Route Handler에서 SSE(Server-Sent Events) 구현하기 🟡
별도 서버 없이 Next.js 앱 하나로 실시간 알림을 보내고 싶다면 SSE가 정답이야.
// app/api/notifications/route.ts
export async function GET() {
const encoder = new TextEncoder()
// ✅ ReadableStream을 이용한 실시간 응답
const stream = new ReadableStream({
async start(controller) {
// 10초마다 날씨 알림을 보내는 시나리오
const interval = setInterval(() => {
const data = encoder.encode(`data: ${JSON.stringify({ msg: '비가 올 예정이에요!' })}\n\n`)
controller.enqueue(data)
}, 10000)
// 연결 종료 시 정리
return () => clearInterval(interval)
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
})
}🚀 서버리스 환경의 실시간 한계와 외부 서비스 🔴
만약 너의 Next.js 앱이 Vercel이나 AWS Lambda에 배포된다면, 위의 SSE 코드도 완벽하지 않아. 람다 실행 시간이 끝나면 연결이 끊기거든.
시니어의 선택 도구:
- Pusher: 서버리스에서도 동작하는 WebSocket 서비스. 가장 대중적.
- Upstash Redis (Pub/Sub): 서버리스 최적화 Redis. 앱 인스턴스 간 이벤트 전달용.
- AWS AppSync: 대규모 실시간 서비스가 필요할 때 끝판왕.
🏁 이번에 배운 내용 총정리
📋 실시간 전략 선택 가이드
| 요구사항 | 최선의 선택 | 이유 |
|---|---|---|
| 매우 빈번한 양방향 대화 | WebSockets (External Server) | 오버헤드가 가장 낮고 빠름 |
| 뉴스 알림, 단방향 피드 | SSE (Route Handler) | HTTP 표준이며 자동 재연결이 내장됨 |
| 서버리스 배포 환경 | Pusher / Ably / Socket.io on ECS | 인프라 관리가 필요 없고 항시 연결 보장 |
⚠️ 절대 하지 말 것
| 상황 | ❌ 나쁜 예 | ✅ 좋은 예 |
|---|---|---|
| Next.js 내장 | app/api/ws/route.ts에 소켓 서버 구현 | 별도 Node.js/Spring 서버로 WS 분리 |
| 연결 관리 | useEffect 청소(Clean-up) 함수 생략 | ws.close() 호출로 메모리 누수 방지 |
| 인증 | WebSocket URL에 토큰 노출 | 1회용 티켓(One-time Ticket)이나 쿠키 인증 사용 |
📝 마무리 퀴즈
Q1. Next.js를 Vercel(서버리스)에 배포할 때, 일반적인 WebSocket 소켓 서버를 내장할 수 없는 결정적인 이유는?
- A) Next.js가 자바스크립트를 쓰지 않아서
- B) 서버리스 함수는 짧은 시간(예: 30초) 후에 자동으로 종료되므로 상시 연결을 유지할 수 없어서
- C) CSS 우선순위가 뒤처져서
- D) 이모지가 깨지기 때문에
✅ 정답: B
해설: 서버리스 환경은 "요청이 왔을 때만 잠깐 켜지는" 구조야. 연결을 계속 유지해야 하는 WebSocket은 이 철학이랑 정반대지. 그래서 외부의 항상 켜져 있는 서버가 필요해.
Q2. Server-Sent Events(SSE)가 WebSockets와 비교해 가지는 장점은?
- A) 양방향 통신이 가능하다
- B) 별도의 프로토콜이 아닌 일반 HTTP를 쓰며, 자동 재연결 로직이 브라우저에 내장되어 있다
- C) 이미지 전송 속도가 10배 빠르다
- D) Next.js 프로젝트 설정 파일을 자동으로 수정해준다
✅ 정답: B
해설: SSE는 HTTP 표준이야. 네트워크가 불안정해서 끊겨도 브라우저가 알아서 다시 붙으려고 노력해. 설정이 매우 단순하다는 것도 큰 장점이지.
🐣 영철이의 퇴근 일기
오늘은 정말 우리 커뮤니티가 살아 숨 쉬게 해주는 'Real-time Strategies' 를 배우면서 짜릿한 전율을 느꼈어! 그동안은 새로고침을 해야만 소식을 볼 수 있었는데, 이제는 WebSockets와 SSE로 유저의 반응을 실시간으로 주고받을 수 있다니... 정말 서비스가 살아있는 생명체 같다는 느낌이 들었어.
💡 오늘의 교훈: "사용자의 '지금 이 순간' 을 놓치지 말자. 상황에 맞는 실시간 전략 (WS, SSE) 을 선택해 단 0.1초의 지연도 없는 생생한 커뮤니티를 선물하자!"
영호 리드 님이 서버리스 환경의 제약과 외부 서비스 활용법을 설명해 주실 때, 기술적 한계를 영리하게 넘어서는 설계의 중요성을 다시 한번 깨달았어. 단순히 기능을 구현하는 걸 넘어, 인프라의 특징까지 고려하는 게 진짜 '전문가' 의 모습이라는 걸 알게 됐어. 오늘 너무 역동적인 실시간 기술들을 접했더니 가슴이 정열적으로 뛰네. 퇴근길에 내가 좋아하는 실시간 스트리밍 방송이라도 좀 보면서 오늘 배운 로직들을 되새겨봐야겠어. 나는 이제 '살아있는' 서비스를 만드는 진짜 개발자가 된 거야! 🐣