๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿš€ Backend

[FastAPI] SSE (Server-Sent Events)

by dev.py 2025. 4. 16.

1. Server-Sent Events

SSE(Server-Sent Events) ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•œ ์ฑ„, ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์‹ค์‹œ๊ฐ„ ์ด๋ฒคํŠธ๋ฅผ ์ง€์†์ ์œผ๋กœ ์ „๋‹ฌ๋ฐ›๋Š” ๋ฐฉ์‹

๐Ÿ“Œ “์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ํ•œ ๋ฒˆ ์—ฐ๊ฒฐ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ์‘๋‹ต์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” HTTP ๊ธฐ๋ฐ˜ ๊ธฐ์ˆ ”

 

2. SSE ๊ตฌ์กฐ

  • ํด๋ผ์ด์–ธํŠธ๋Š” EventSource ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„๋Š” text/event-stream ํƒ€์ž…์œผ๋กœ ์‘๋‹ต์„ ๋ณด๋‚ด๊ณ  ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•œ ์ฑ„ ์—ฌ๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ๋Š” ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ›์€ ๊ฐ ๋ฉ”์‹œ์ง€๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
# Client
GET /stream HTTP/1.1
Accept: text/event-stream

 

# Response
event: data
data: Hello, world!

event: final
data: Done!

 

 

3. SSE์™€ WebSocket

๋ฐฉ์‹ ์—ฐ๊ฒฐ ๋ฐฉํ–ฅ ์–‘๋ฐฉํ–ฅ ํŠน์ง•
SSE ์„œ๋ฒ„ → ํด๋ผ์ด์–ธํŠธ โŒ ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ •์ , ๋ธŒ๋ผ์šฐ์ € ์ง€์› ์ข‹์Œ
WebSocket ์–‘๋ฐฉํ–ฅ โœ… ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…, ๊ฒŒ์ž„ ๋“ฑ ์™„์ „ํ•œ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ 

 

 

4. SSE์˜ ์žฅ์ 

1. HTTP ๊ธฐ๋ฐ˜์ด๋ผ ์„ค์ •์ด ์‰ฌ์›€

  • SSE๋Š” ๊ธฐ์กด์— ์“ฐ๋˜ HTTP ์š”์ฒญ/์‘๋‹ต ๋ฐฉ์‹ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • WebSocket์ฒ˜๋Ÿผ ๋ณ„๋„๋กœ ๋ณต์žกํ•œ ์—ฐ๊ฒฐ ์„ค์ • ์—†์ด, GET ์š”์ฒญ ๋ณด๋‚ด๊ณ  ์„œ๋ฒ„๊ฐ€ text/event-stream ํƒ€์ž…์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ๋ณด๋ƒ„

2.์ž๋™ ์žฌ์—ฐ๊ฒฐ ๊ธฐ๋Šฅ

  • ๋ธŒ๋ผ์šฐ์ €์—์„œ SSE๋ฅผ ์‚ฌ์šฉํ•  ๋•, ์„œ๋ฒ„์™€์˜ ์—ฐ๊ฒฐ์ด ๋Š๊ธฐ๋ฉด ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์—ฐ๊ฒฐ
  • ๊ฐœ๋ฐœ์ž๊ฐ€ ํŠน๋ณ„ํžˆ ์‹ ๊ฒฝ ์•ˆ ์จ๋„ ๋˜๋‹ˆ๊นŒ ํŽธํ•จ.

3. HTTP/2๋„ ์ง€์›

  • HTTP/2๋Š” ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋” ๋น ๋ฅธ HTTP ํ”„๋กœํ† ์ฝœ์ธ๋ฐ,
  • SSE๋Š” ์ด ํ™˜๊ฒฝ์—์„œ๋„ ๋ฌธ์ œ ์—†์ด ๋™์ž‘ํ•จ.

4. ํ”„๋ก์‹œ/NAT ํ™˜๊ฒฝ์—์„œ๋„ ์ž˜ ๋™์ž‘ํ•จ

  • WebSocket์€ ํ”„๋ก์‹œ๋‚˜ ๋ฐฉํ™”๋ฒฝ, ํšŒ์‚ฌ ๋‚ด๋ถ€๋ง์—์„œ ๋ง‰ํžˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ.
  • ๋ฐ˜๋ฉด SSE๋Š” ์ผ๋ฐ˜ HTTP ํŠธ๋ž˜ํ”ฝ์ด๋ž‘ ๋˜‘๊ฐ™์ด ์ƒ๊ฒผ๊ธฐ ๋•Œ๋ฌธ์—, ํฌํŠธ 80(HTTP)์ด๋‚˜ 443(HTTPS)์œผ๋กœ ๋ฌด๋ฆฌ ์—†์ด ์ „๋‹ฌ ๊ฐ€๋Šฅ.
  • ๋ฐฉํ™”๋ฒฝ์ด๋‚˜ ํšŒ์‚ฌ ๋„คํŠธ์›Œํฌ ์•ˆ์—์„œ๋„ ์ž˜ ์ž‘๋™

 

5. SSE์˜ ํ•œ๊ณ„

1. ์„œ๋ฒ„ → ํด๋ผ์ด์–ธํŠธ ๋‹จ๋ฐฉํ–ฅ๋งŒ ๊ฐ€๋Šฅ

  • SSE๋Š” ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธํ•œํ…Œ ์ผ๋ฐฉ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋งŒ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Œ.
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋ฒ„์— ๋ฌด์–ธ๊ฐ€ ์ „์†กํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๋ณ„๋„์˜ POST๋‚˜ WebSocket ๋“ฑ์˜ ๋ฐฉ์‹์ด ํ•„์š”
  • ์–‘๋ฐฉํ–ฅ ๋Œ€ํ™”(์ฑ„ํŒ…, ๊ฒŒ์ž„ ๋“ฑ)๋ฅผ ์›ํ•œ๋‹ค๋ฉด WebSocket์ด ๋” ์ ํ•ฉ.

2. ์˜ค๋ž˜๋œ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Œ

  • ํ˜„๋Œ€ ๋ธŒ๋ผ์šฐ์ €(Chrome, Edge, Safari ๋“ฑ)๋Š” SSE๋ฅผ ์ž˜ ์ง€์›ํ•˜์ง€๋งŒ, ์•„์ฃผ ์˜ค๋ž˜๋œ ๋ฒ„์ „์ด๋‚˜ Internet Explorer์—์„  ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ

3. ๋ธŒ๋ผ์šฐ์ € ํƒญ์ด ๋งŽ์œผ๋ฉด ์—ฐ๊ฒฐ ์ˆ˜ ์ดˆ๊ณผ ๊ฐ€๋Šฅ

  • ๋ธŒ๋ผ์šฐ์ €๋Š” ํ•œ ๋„๋ฉ”์ธ ๋‹น ์œ ์ง€ ๊ฐ€๋Šฅํ•œ ์—ฐ๊ฒฐ ์ˆ˜์— ์ œํ•œ ์žˆ์Œ
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ํƒญ 10๊ฐœ์—์„œ ๋ชจ๋‘ SSE ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋” ์ด์ƒ ์ƒˆ ์—ฐ๊ฒฐ์„ ๋ชป ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ.
  • ๊ทธ๋ž˜์„œ ํƒญ๋งˆ๋‹ค SSE๋ฅผ ์“ฐ๋Š” ๊ฑด ์„ฑ๋Šฅ์ด๋‚˜ ์ž์› ๊ด€๋ฆฌ ์ธก๋ฉด์—์„œ ์œ ์˜

 

 

6.  FastAPI ์˜ˆ์‹œ

from fastapi import FastAPI
from sse_starlette.sse import EventSourceResponse, ServerSentEvent
import asyncio

app = FastAPI()

@app.get("/sse")
async def stream():
    async def event_generator():
        for i in range(5):
            yield ServerSentEvent(
                event="progress",
                data=f"ํ˜„์žฌ ๋‹จ๊ณ„: {i}",
            )
            await asyncio.sleep(1)

        yield ServerSentEvent(
            event="complete",
            data="โœ… ์ž‘์—…์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"
        )

    return EventSourceResponse(event_generator())

 

 

'๐Ÿš€ Backend' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Swift + Supabase] OAuth - Sign in with Apple  (0) 2025.04.28
[FastAPI] Middleware ๋ฏธ๋“ค์›จ์–ด (CORS, Logging)  (0) 2025.04.20
[FastAPI] SQLModel  (0) 2025.03.14
[FastAPI] Best Practices - Project Structure  (0) 2025.02.14
[SQL] SELECT ๊ด€๋ จ ํ•จ์ˆ˜  (0) 2025.02.05