프로그래밍공부(Programming Study)/네트워크(Network)

서버와 클라이언트 통신 프로토콜의 모든 것: 모바일 푸시알림, 롱폴링, 웹소켓, 서버 전송 이벤트 비교

Chann._.y 2024. 12. 5.
728x90

 

1. 서버와 클라이언트 통신 프로토콜 개요

현대 애플리케이션은 사용자와의 실시간 상호작용이 중요합니다. 이를 위해 서버와 클라이언트가 데이터를 교환하는 다양한 프로토콜이 존재하며, 각 방식은 용도와 상황에 따라 다르게 사용됩니다. 이 글에서는 **모바일 푸시알림, 롱폴링, 웹소켓, 서버 전송 이벤트(SSE)**를 중심으로 특징과 장단점을 알아보겠습니다.


2. 모바일 푸시알림 (Push Notification)

개념

모바일 푸시알림은 서버에서 클라이언트 디바이스로 메시지를 전달하는 프로토콜입니다. 앱이 열려 있지 않더라도 디바이스에 알림을 전송할 수 있어 사용자와의 소통이 가능합니다.

특징

  • 클라우드 기반 서비스: Firebase Cloud Messaging(FCM), Apple Push Notification Service(APNs)와 같은 플랫폼 의존.
  • 백그라운드 작동: 클라이언트 디바이스가 활성 상태가 아니어도 알림 전송 가능.
  • 알림 유형: 긴급 공지, 마케팅 메시지, 채팅 알림 등.

장점

  • 사용자가 앱을 실행 중이지 않아도 메시지 전달 가능.
  • 간단한 API로 알림 시스템 구축 가능.
  • 대량 알림 전송에 적합.

단점

  • 푸시 수신 실패 가능: 네트워크 상태나 디바이스 설정에 따라 수신되지 않을 수 있음.
  • 제한된 데이터 크기: 알림의 데이터 페이로드 크기가 제한됨.

구체적인 사례

Firebase Cloud Messaging(FCM)을 활용한 푸시알림:

from firebase_admin import messaging, initialize_app

# Firebase 초기화
initialize_app()

# 푸시알림 메시지 생성
message = messaging.Message(
    notification=messaging.Notification(
        title="알림 제목",
        body="알림 내용을 작성하세요.",
    ),
    token="클라이언트 디바이스의 FCM 토큰",
)

# 메시지 전송
response = messaging.send(message)
print('푸시알림 전송 완료:', response)

3. 롱폴링 (Long Polling)

개념

롱폴링은 클라이언트가 서버에 HTTP 요청을 보내면, 서버가 새로운 데이터가 생길 때까지 응답을 지연시키는 방식입니다. 이를 통해 마치 실시간 통신처럼 구현할 수 있습니다.

특징

  • 클라이언트는 서버에 주기적으로 요청을 보냄.
  • 서버는 요청이 올 때까지 데이터가 준비될 때까지 대기.
  • 데이터가 준비되면 응답을 클라이언트에 전송.

장점

  • HTTP를 사용하므로 브라우저 호환성이 높음.
  • 서버가 데이터 준비 상태를 클라이언트에 알려줄 수 있음.

단점

  • 클라이언트의 빈번한 요청으로 서버 부하 증가.
  • 네트워크 자원 소모가 큼.

구체적인 사례

Flask를 활용한 롱폴링 구현:

from flask import Flask, jsonify, request
import time

app = Flask(__name__)

@app.route('/long-polling')
def long_polling():
    while True:
        new_data = check_for_new_data()
        if new_data:
            return jsonify(new_data)
        time.sleep(1)

def check_for_new_data():
    # 데이터 갱신 확인 로직
    return {"message": "새 데이터가 도착했습니다."}

if __name__ == '__main__':
    app.run(debug=True)

4. 웹소켓 (WebSocket)

개념

웹소켓은 서버와 클라이언트 간 양방향 통신이 가능한 프로토콜입니다. 연결을 유지하면서 데이터를 교환할 수 있어 실시간성이 매우 뛰어납니다.

특징

  • 지속 연결: 클라이언트와 서버 간 연결이 열려 있는 동안 다중 메시지 교환 가능.
  • 이벤트 기반: 요청 없이도 서버나 클라이언트에서 자유롭게 메시지 전송 가능.

장점

  • 낮은 지연 시간.
  • 서버-클라이언트 간 양방향 데이터 전송 가능.

단점

  • 초기 연결 설정이 복잡할 수 있음.
  • 브라우저와 네트워크 방화벽에 따라 제한이 있을 수 있음.

구체적인 사례

Python websockets 라이브러리를 사용한 웹소켓 구현:

import asyncio
import websockets

async def echo(websocket, path):
    async for message in websocket:
        await websocket.send(f"Echo: {message}")

start_server = websockets.serve(echo, "localhost", 6789)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

5. 서버 전송 이벤트 (SSE)

개념

SSE는 클라이언트가 서버에 연결을 열고 서버가 데이터를 스트림 형식으로 지속적으로 전송하는 단방향 통신 프로토콜입니다. HTML5에서 기본적으로 지원합니다.

특징

  • HTTP 기반이므로 브라우저 호환성이 높음.
  • 클라이언트는 지속적으로 데이터 스트림을 수신.

장점

  • 구현이 단순하며 브라우저에서 기본 지원.
  • HTTP 기반으로 방화벽 문제를 피할 수 있음.

단점

  • 클라이언트-서버 간 단방향 통신만 가능.
  • 데이터 전송량이 많은 경우 성능 저하 가능.

구체적인 사례

Flask를 사용한 SSE 구현:

from flask import Flask, Response
import time

app = Flask(__name__)

@app.route('/sse')
def sse():
    def event_stream():
        while True:
            yield f"data: {time.ctime()}\n\n"
            time.sleep(1)
    return Response(event_stream(), content_type='text/event-stream')

if __name__ == '__main__':
    app.run(debug=True)

6. 프로토콜 비교와 선택 기준

프로토콜 특징 장점 단점

모바일 푸시알림 클라이언트 디바이스로 알림 전송 백그라운드에서도 작동 가능 클라우드 서비스에 의존
롱폴링 HTTP 요청을 통해 데이터 실시간 확인 쉬운 구현 높은 서버 부하
웹소켓 양방향 통신 가능 낮은 지연 시간, 실시간성 초기 설정 복잡
SSE 서버에서 클라이언트로 데이터 스트림 전송 단순 구현, HTTP 호환성 단방향 통신만 가능

7. 구체적인 구현 사례

Python으로 웹소켓SSE를 혼합한 서버 구현:

from flask import Flask, Response
import asyncio
import websockets

app = Flask(__name__)

@app.route('/sse')
def sse():
    def event_stream():
        yield "data: Hello SSE\n\n"
    return Response(event_stream(), content_type='text/event-stream')

async def websocket_handler(websocket, path):
    await websocket.send("Hello WebSocket")

start_server = websockets.serve(websocket_handler, "localhost", 6789)

if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(start_server)
    app.run(debug=True)
728x90

댓글