Frontend/Next.js

Next.js(14๋ฒ„์ „)- SSE ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ ๊ตฌํ˜„

2-doooo-2 2025. 1. 31. 22:04
728x90
๋ฐ˜์‘ํ˜•

๐Ÿท๏ธ๋“ค์–ด๊ฐ€๋ฉฐ 

ํ•ด๋‹น ๊ธ€์€ "next": "14.2.6" ๋ฒ„์ „์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. 
SSE๋Š” HTTP ํ‘œ์ค€ ๊ธฐ์ˆ  ์ค‘ ํ•˜๋‚˜์ด๋ฏ€๋กœ ํŠน์ • ๋ฒ„์ „์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 

 

 

โ“SSE๋ฅผ ์„ ํƒํ•œ ์ด์œ ๋Š”?

์šฐ๋ฆฌFIS์•„์นด๋ฐ๋ฏธ์—์„œ ์ง„ํ–‰ํ•œ ์ตœ์ด ํ”„๋กœ์ ํŠธ์—์„œ ์šฐ๋ฆฌ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์€ '์ •์‚ฐ'์ด์—ˆ๋‹ค. ์ด๋•Œ ์‹ ์†ํ•œ ์ •์‚ฐ์„ ์œ„ํ•ด ‘์ •์‚ฐ ์š”์ฒญ’, ‘์ •์‚ฐ ์‹œ์ž‘’, ‘์ •์‚ฐ ์™„๋ฃŒ’ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์—ฌํ–‰์› ๊ฐ„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ณต์œ ํ•  ํ•„์š”์„ฑ์„ ๋А๊ผˆ๊ณ  ์šฐ๋ฆฌ๋Š” ์‹ค์‹œ๊ฐ„์œผ๋กœ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•  ๊ธฐ์ˆ ์„ ์ฐพ๊ณ  ์žˆ๋‹ค. 

 

SSE(Server-Sent Events) VS Polling VS WebSocket

๋ฐฉ์‹ ์—ฐ๊ฒฐ  ๋ฐฉ์‹ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ์‹ค์‹œ๊ฐ„์„ฑ ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋ธŒ๋ผ์šฐ์ € ์ง€์› ์‚ฌ์šฉ ์‚ฌ๋ก€
SSE (Server-Sent Events) ๋‹จ๋ฐฉํ–ฅ (์„œ๋ฒ„ → ํด๋ผ์ด์–ธํŠธ) ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ๋งŒ ๋ฐ์ดํ„ฐ ์ „์†ก ์ค‘๊ฐ„ (์—ฐ๊ฒฐ ์œ ์ง€) ๋‚ฎ์Œ ๋Œ€๋ถ€๋ถ„์˜ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € ์ง€์› (IE ๋ฏธ์ง€์›) ์ฃผ๊ฐ€ ์ •๋ณด, ์•Œ๋ฆผ ์‹œ์Šคํ…œ, ๋Œ€์‹œ๋ณด๋“œ
Polling (Short/Long) ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฃผ๊ธฐ์ ์œผ๋กœ ์š”์ฒญ ํด๋ผ์ด์–ธํŠธ → ์„œ๋ฒ„ ์š”์ฒญ, ์„œ๋ฒ„ ์‘๋‹ต ๋‚ฎ์Œ (์ง€์—ฐ ๋ฐœ์ƒ) ๋†’์Œ (๋ฐ˜๋ณต์ ์ธ ์š”์ฒญ) ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ฐ€๋Šฅ ์ฑ„ํŒ…, ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ
WebSocket ์–‘๋ฐฉํ–ฅ (์„œ๋ฒ„ โ‡„ ํด๋ผ์ด์–ธํŠธ) ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž์œ ๋กญ๊ฒŒ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ๋†’์Œ (์‹ค์‹œ๊ฐ„) ๋‚ฎ์Œ (์—ฐ๊ฒฐ ์œ ์ง€, ํšจ์œจ์ ) ๋Œ€๋ถ€๋ถ„์˜ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € ์ง€์› ์‹ค์‹œ๊ฐ„ ๊ฒŒ์ž„, ์ฑ„ํŒ…, ์ฃผ์‹ ๊ฑฐ๋ž˜

 

Polling์€ ์ฃผ๊ธฐ์ ์œผ๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹, ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๊ฐ€ ์‹ฌํ•˜๋‹ค๊ณ  ํŒ๋‹จ, ๋˜ํ•œ ์›น์†Œ์ผ“์—ญ์‹œ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•  ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ์— ์“ฐ๊ธฐ์—๋Š” ์˜ค๋ฒ„ ์ŠคํŽ™์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์„œ๋ฒ„์—์„œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋‹จ๋ฐฉํ–ฅ ํ†ต์‹ ์ธ SSE๋ฅผ ์„ ํƒํ•ด์„œ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. 

 

++ SSE์™€ WebSocket์˜ ์ฐจ์ด 

SSE๋Š” ์›น ๊ธฐ์ˆ ์ด๊ธฐ ๋•Œ๋ฌธ์— HTTP ํ”„๋กœํ† ์ฝœ ์œ„์—์„œ ๋™์ž‘ํ•œ๋‹ค.๋˜ํ•œ, HTTP ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•œ ์ƒํƒœ์—์„œ ์žฌ์—ฐ๊ฒฐ์ด๋‚˜ ์ถ”๊ฐ€ ์„ค์ • ์—†์ด ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ง€์†์ ์ธ ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋ฐ˜๋ฉด, WebSocket์€ ๋…๋ฆฝ์ ์ธ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๊ณ , HTTP์™€๋Š” ๋ณ„๋„์˜ ์—ฐ๊ฒฐ์„ ๋งŒ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š”๋‹ค.

 

SSE๋Š” CORS(Cross-Origin Resource Sharing)์„ ํ†ตํ•ด ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์—์„œ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. WebSocket๋„ ๋™์ผํ•œ ๋„๋ฉ”์ธ ๊ฐ„ ํ†ต์‹ ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ์ถ”๊ฐ€ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿš€ Next.js์— SSE ์ ์šฉํ•˜๊ธฐ

Next.js์—์„œ๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง ์ง€์›ํ•ด์„œ SSE API Route๋„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ๋А Spring์œผ๋กœ ์„œ๋ฒ„๋ฅผ ๊ตฌํ˜„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‚˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋ฉด ๋๋‹ค! 

 

์šฐ๋ฆฌ ์„œ๋น„์Šค์—์„œ๋Š” ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ ์„œ๋น„์Šค๋ฅผ ๋กœ๊ทธ์ธ๋œ ํšŒ์›๋งŒ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์„œ๋ฒ„๋ž‘ SSE ์—ฐ๊ฒฐ ์‹œ Authorization ํ—ค๋”์— ํ† ํฐ๊ฐ’์„ ํฌํ•จํ•˜์—ฌ ์ธ์ฆ๋œ ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๊ตฌํ˜„ํ•ด์•ผํ–ˆ๊ณ  EventSource API๋Š” ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ ์ปค์Šคํ…€ ํ—ค๋”๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์—†์–ด์„œ event-source-polyfill๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

 

๋ฌธ์ œ ์ฝ”๋“œ 

import { EventSourcePolyfill } from 'event-source-polyfill';
import { auth } from '@withbee/auth-config';

export const connectSSE = async (url: string, onMessage: (data: any) => void, onConnect?: () => void, onError?: (error: any) => void, token?: string) => {
  // ์ธ์ฆ ์ •๋ณด ์–ป๊ธฐ
  const session = await auth();
  const accessToken = session?.user!.accessToken;
  if (!accessToken) throw new Error('Authentication required');

  // SSE ์—ฐ๊ฒฐ ์„ค์ •
  const eventSource = new EventSourcePolyfill(url, {
    headers: { Authorization: `Bearer ${accessToken}` },
    heartbeatTimeout: 30000,  // 30์ดˆ๋งˆ๋‹ค heartbeat ์ฒดํฌ
  });

  // ์—ฐ๊ฒฐ ์„ฑ๊ณต ์ด๋ฒคํŠธ
  eventSource.addEventListener('connect', (event: any) => {
    if (event.data === 'SSE ์—ฐ๊ฒฐ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.') {
      onConnect?.(); // ์—ฐ๊ฒฐ ์™„๋ฃŒ ์ฝœ๋ฐฑ ํ˜ธ์ถœ
    }
  });

  // ๋ฉ”์‹œ์ง€ ์ˆ˜์‹  ์ด๋ฒคํŠธ
  eventSource.addEventListener('message', (event: MessageEvent) => {
    onMessage(event.data); // ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ
  });

  // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
  eventSource.onerror = (error) => {
    console.error('SSE Error:', error);
    onError?.(error); // ์—๋Ÿฌ ์ฝœ๋ฐฑ ํ˜ธ์ถœ
    eventSource.close(); // ์—ฐ๊ฒฐ ์ข…๋ฃŒ
  };

  return eventSource; // ์ด๋ฒคํŠธ ์†Œ์Šค ๋ฐ˜ํ™˜
};

 

์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๋ฅผ ์™„์„ฑํ•œ ํ›„์— ๋Œ๋ ค๋ดค๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋–ด๋‹ค. 

 

headers ๊ฐ์ฒด๊ฐ€ ์„œ๋ฒ„ ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋ฐ–์—์„œ ํ˜ธ์ถœ๋จ

์ฒ˜์Œ์—๋Š” ๋ฌด์Šจ ์†Œ๋ฆฌ์ธ๊ฐ€ ํ–ˆ๋Š”๋ฐ  ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ๋ธŒ๋ผ์šฐ์ € ์ „์šฉ API์ธ EventSource ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด์„œ ๋‚œ ์—๋Ÿฌ์˜€๋‹ค!.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋ฐ–์—์„œ ํ˜ธ์ถœ๋๋‹ค๋Š” ๊ฒŒ ๋ฌด์Šจ ๋ง์ผ๊นŒ?

  • connectSSE ํ•จ์ˆ˜๊ฐ€ 'use client' ์ง€์‹œ๋ฌธ ์—†์ด ์ž‘์„ฑ๋จ
  • Next.js๋Š” ์ด ์ฝ”๋“œ๋ฅผ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ธ์‹
  • ๋นŒ๋“œ/์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ์ ์— ์ด ์ฝ”๋“œ๊ฐ€ ํ‰๊ฐ€๋จ
  • ์ด ์‹œ์ ์—๋Š” ์‹ค์ œ HTTP ์š”์ฒญ์ด ์—†์Œ (= ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋ฐ–)
  • headers ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜์ง€๋งŒ ์‹ค์ œ ์š”์ฒญ ์ปจํ…์ŠคํŠธ๊ฐ€ ์—†์–ด์„œ ์—๋Ÿฌ ๋ฐœ์ƒ

๊ทธ๋Ÿฌ๋‹ˆ๊น ๋Œ€๊ฐ• ์š”์•ฝํ•ด๋ณด๋ฉด ๋ธŒ๋ผ์šฐ์ € ์ „์šฉ api์•ˆ์— header๊ฐ€ ์žˆ๋Š”๋ฐ ์„œ๋ฒ„์ปดํฌ๋„ŒํŠธ์—์„œ ์ด๋ฅผ ๋ถ€๋ฅผ๋ ค๊ณ  ํ•˜๋‹ˆ๊น ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋ฐ–์—์„œ header๊ฐ์ฒด๊ฐ€ ํ˜ธ์ถœ๋๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. 

 

๊ผฌ๊ผฌ์งˆ(๊ผฌ๋ฆฌ์˜ ๊ผฌ๋ฆฌ๋ฅผ ๋ฌด๋Š” ์งˆ๋ฌธ ใ…Žใ…Ž ๋‚ด๊ฐ€ ๋งŒ๋“  ์šฉ์–ด)

๊ทธ๋ ‡๋‹ค๋ฉด ์š”์ฒญ ์Šค์ฝ”ํ”„๋Š” ์ •ํ™•ํžˆ ์–ธ์ œ ํ‘œํ˜„๋˜๋Š” ๋‹จ์–ด์ผ๊นŒ? 

 

์š”์ฒญ ์Šค์ฝ”ํ”„๋Š” ํ•˜๋‚˜์˜ HTTP ์š”์ฒญ์ด ์‹œ์ž‘๋˜์–ด ์‘๋‹ต์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋Š” ์‹คํ–‰ ์ปจํ…์ŠคํŠธ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.  Next.js์—์„œ๋Š” ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€ ์ปจํ…์ŠคํŠธ๊ฐ€ ์žˆ๋‹ค. 

 

์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง(SSR) ์ปจํ…์ŠคํŠธ

// ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ
// headers(), cookies() ๋“ฑ์˜ API ์‚ฌ์šฉ ๊ฐ€๋Šฅ
export default function Page() {
  // ์—ฌ๊ธฐ๋Š” ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋‚ด๋ถ€
}
 

ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ์ปจํ…์ŠคํŠธ

'use client'
// ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ
// window, document ๋“ฑ ๋ธŒ๋ผ์šฐ์ € API ์‚ฌ์šฉ ๊ฐ€๋Šฅ

 

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋นŒ๋“œ ํƒ€์ž„ ์ปจํ…์ŠคํŠธ

// ๋นŒ๋“œํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ
// ์š”์ฒญ ์Šค์ฝ”ํ”„ ๋ฐ–
const config = {
  // ์—ฌ๊ธฐ์„œ headers ์ ‘๊ทผ ๋ถˆ๊ฐ€
}

 

ํ•ด๊ฒฐ์ฑ…์€ ๊ฐ„๋‹จํ–ˆ๋‹ค. EventSourcePolyfill์ด ํ•„์š”๋กœ ํ•˜๋Š” ๋ธŒ๋ผ์šฐ์ € ์ปจํ…์ŠคํŠธ๊ฐ€ ์—†์—ˆ๊ณ  headers ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ ์‹ค์ œ HTTP ์š”์ฒญ ์ปจํ…์ŠคํŠธ๊ฐ€ ์—†์–ด ์—๋Ÿฌ๊ฐ€ ๋‚œ ๊ฑฐ๊ธฐ ๋•Œ๋ฌธ์— ์ € ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ๊น 'use client'๋งŒ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค. 

 

๋ฌธ์ œ ํ•ด๊ฒฐ ์ฝ”๋“œ 

'use client';
// import ์ƒ๋žต

export default function RealTimeMsg() {
  const { data: session, status } = useSession();
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [accessToken, setAccessToken] = useState<string | null>(null);
  const [isVisible, setIsVisible] = useState(false);
  const [isAnimatingOut, setIsAnimatingOut] = useState(false);

  useEffect(() => {
    if (
      status === 'authenticated' &&
      session?.user?.accessToken &&
      !accessToken
    ) {
      setAccessToken(session.user.accessToken);
    }
  }, [session, status, accessToken]);

 useEffect(() => {
  let eventSource: EventSourcePolyfill;
  if (accessToken) {
    (async () => {
      eventSource = await connectSSE(
        `${BASE_URL}/api/notifications/stream`,
        (rawData) => {
          const parsedNotification: Notification = JSON.parse(rawData);
          setNotifications((prev) => [...prev, parsedNotification]);
          // ์•Œ๋ฆผ ํ‘œ์‹œ ๋ฐ ์ž๋™ ์ œ๊ฑฐ ํƒ€์ด๋จธ ์„ค์ •
        }
      );
    })();
  }
  // ํด๋ฆฐ์—…
  return () => {
    eventSource?.close();
  };
}, [accessToken]);

  const handleClose = () => {
    setIsAnimatingOut(true);
    setTimeout(() => {
      setIsVisible(false);
      setNotifications((prev) => prev.slice(1));
    }, 1000); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ๊ฐ„๊ณผ ๋งž์ถค
  };

  if (!isVisible || notifications.length === 0) {
    return null;
  }

  const notification = notifications[0];
  if (!notification) {
    return null;
  }

  return (
    <div className={styles.background}>
      // ์ƒ๋žต
    </div>
  );
}

 

 

์ตœ์ข… ํ™”๋ฉด 

 

 

 

๐Ÿ’ซ๋งˆ์น˜๋ฉฐ 

์‚ฌ์‹ค ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ฐ”๊พธ๋ฉด ํ•ด๊ฒฐ๋˜๋Š” ๋ฌธ์ œ์˜€์ง€๋งŒ ์—๋Ÿฌ ๋กœ๊ทธ์—์„œ Next.js์˜ SSR๊ณผ CSR์˜ ์ถฉ๋ถ„ํ•œ ์ดํ•ด๊ฐ€ ์—†์œผ๋ฉด ํ—ท๊ฐˆ๋ฆฌ๋Š” ๋ฌธ์ œ์˜€๊ธฐ์— ์ถฉ๋ถ„ํ•œ ์‹œ๊ฐ„์„ ๋“ค์—ฌ์„œ ๊ณ ๋ฏผํ•ด๋ณด์•˜๋‹ค,, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋’ค๋กœ ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •์—์„œ ๋กœ์ปฌ์—์„œ ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” SSE ์—ฐ๊ฒฐ์ด ์—๋Ÿฌ ์—†์ด ์ž˜ ์ž‘๋™ํ–ˆ๋Š”๋ฐ ๋ฐฐํฌ ํ›„ SSE์—ฐ๊ฒฐํ•˜๋Š” ํŽ˜์ด์ง€์—์„œ 502 Bad Gateway์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์—ˆ๋‹ค. SSEํ†ต์‹ ์„ ์ง€์†์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด nginx์ชฝ์—์„œ๋„ ๋”ฐ๋กœ ๋˜ ์„ค์ •์„ ํ•ด์ค˜์•ผ ํ–ˆ๋‹ค. ๋‹คํ–‰ํžˆ ํŒ€์›๋“ค์˜ ๋„์›€์œผ๋กœ ๋งˆ๋ฌด๋ฆฌ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ์ƒˆ๋กœ์šด ๊ธฐ์ˆ  ํ•™์Šตํ•ด์„œ ์งœ๋ฆฟํ•˜๋‹ค~~๐Ÿช“

 

 

 

 

 

์ฐธ๊ณ ์ž๋ฃŒ 

728x90
๋ฐ˜์‘ํ˜•