AJAX, SSE 방식 비교

AI GPT하고 놀아보자!·2024년 8월 28일
0

Content-Type :text/event-stream 방식은 PC나 서버에 부하가 많이 가는 방식인가?

Content-Type: text/event-stream 방식, 즉 서버 전송 이벤트(Server-Sent Events, SSE)는 특정 상황에서 서버와 클라이언트 간의 실시간 데이터 전송을 가능하게 하지만, 이 방식이 서버나 PC에 부하를 줄 수 있는 몇 가지 요인이 있습니다. 다음은 SSE의 부하와 관련된 주요 요소들입니다:

  1. 연결 유지
    SSE는 클라이언트와 서버 간의 지속적인 연결을 유지합니다. 각 클라이언트는 서버에 대한 연결을 열어두고, 서버는 이 연결을 통해 데이터를 전송합니다. 클라이언트 수가 많아질수록 서버는 더 많은 연결을 관리해야 하므로, 서버의 리소스(메모리, CPU 등)에 부하가 증가할 수 있습니다.
  2. 데이터 전송 빈도
    서버가 클라이언트에 데이터를 얼마나 자주 전송하는지에 따라 부하가 달라집니다. 예를 들어, 1초마다 데이터를 전송하는 경우와 10초마다 전송하는 경우의 부하는 크게 다릅니다. 데이터 전송 빈도가 높을수록 서버의 부하가 증가합니다.
  3. 데이터 크기
    전송하는 데이터의 크기도 부하에 영향을 미칩니다. 작은 데이터 패킷을 자주 전송하는 것과 큰 데이터 패킷을 드물게 전송하는 것의 부하는 다를 수 있습니다. 데이터 크기가 크면 네트워크 대역폭과 서버의 처리 능력에 더 많은 부담을 줄 수 있습니다.
  4. 서버의 성능
    서버의 하드웨어 성능과 소프트웨어 최적화 상태에 따라 부하를 처리하는 능력이 달라집니다. 고성능 서버는 더 많은 클라이언트와 연결을 처리할 수 있지만, 저성능 서버는 쉽게 과부하될 수 있습니다.
  5. 네트워크 대역폭
    클라이언트와 서버 간의 네트워크 대역폭도 중요한 요소입니다. 대역폭이 제한적일 경우, 많은 클라이언트가 동시에 연결되어 데이터를 요청하면 네트워크 병목 현상이 발생할 수 있습니다.
  6. 자동 재연결
    클라이언트가 연결이 끊어졌을 때 자동으로 재연결을 시도하는 기능이 있습니다. 이로 인해 연결이 자주 끊기고 다시 연결되는 경우, 서버에 추가적인 부하가 발생할 수 있습니다.

결론
SSE는 실시간 데이터 전송을 위한 유용한 방법이지만, 서버와 클라이언트의 부하를 고려해야 합니다. 적절한 설계와 최적화를 통해 부하를 최소화할 수 있으며, 필요에 따라 WebSocket과 같은 다른 기술을 고려할 수도 있습니다. WebSocket은 양방향 통신을 지원하며, 더 많은 클라이언트와의 연결을 효율적으로 처리할 수 있는 경우가 많습니다.

동일한 프로그램의 경우 ajax 방식으로 계속 호출하는 방식과 비교하면 stream 방식이 더 나은가?

AJAX 방식과 서버 전송 이벤트(Server-Sent Events, SSE) 방식은 각각의 장단점이 있으며, 특정 상황에 따라 더 적합한 방법이 다를 수 있습니다. 두 방식의 비교를 통해 어떤 경우에 SSE가 더 나은 선택인지 살펴보겠습니다.

AJAX 방식
단방향 요청: 클라이언트가 서버에 요청을 보내고, 서버는 응답을 반환합니다. 클라이언트가 데이터를 요청할 때마다 새로운 HTTP 요청이 발생합니다.

주기적 호출: 실시간 데이터 업데이트가 필요할 경우, 클라이언트는 일정한 간격으로 서버에 AJAX 요청을 보내야 합니다. 이 방식은 "폴링(polling)"이라고 하며, 서버에 불필요한 요청이 많아질 수 있습니다.

부하: 클라이언트가 주기적으로 요청을 보내기 때문에, 서버에 부하가 증가할 수 있습니다. 특히 많은 클라이언트가 동시에 요청을 보내면 서버의 리소스가 소모될 수 있습니다.

지연: 요청과 응답 사이에 지연이 발생할 수 있으며, 실시간성이 떨어질 수 있습니다.

SSE 방식
지속적인 연결: 클라이언트와 서버 간의 지속적인 연결을 유지하며, 서버는 클라이언트에게 실시간으로 데이터를 푸시할 수 있습니다.

효율적인 데이터 전송: 서버는 클라이언트에게 필요한 데이터만 전송할 수 있으며, 클라이언트는 서버로부터 새로운 데이터가 있을 때만 업데이트를 받습니다. 이로 인해 불필요한 요청이 줄어듭니다.

자동 재연결: 클라이언트가 연결이 끊어지면 자동으로 재연결을 시도하므로, 안정적인 데이터 전송이 가능합니다.

단방향 통신: SSE는 서버에서 클라이언트로의 단방향 통신만 지원하므로, 클라이언트가 서버에 데이터를 보내려면 별도의 AJAX 요청이 필요합니다.

어떤 경우에 SSE가 더 나은가?

실시간 데이터 업데이트: 데이터가 자주 변경되고 클라이언트가 이를 실시간으로 받아야 하는 경우, SSE가 더 적합합니다. 예를 들어, 채팅 애플리케이션, 주식 가격 업데이트, 실시간 알림 등에서 유용합니다.

서버 부하 감소: 많은 클라이언트가 동시에 연결되어야 하는 경우, SSE는 불필요한 요청을 줄여 서버의 부하를 감소시킬 수 있습니다.

간단한 구현: SSE는 클라이언트와 서버 간의 연결을 유지하므로, 주기적으로 요청을 보내는 AJAX 방식보다 구현이 간단할 수 있습니다.

결론
AJAX는 클라이언트가 서버에 요청을 보내고 응답을 받는 방식으로, 특정 상황에서는 유용하지만, 실시간성이 떨어질 수 있습니다.

SSE는 실시간 데이터 전송에 적합하며, 서버의 부하를 줄이고 클라이언트에게 즉각적인 업데이트를 제공할 수 있습니다.

따라서, 애플리케이션의 요구 사항에 따라 적절한 방식을 선택하는 것이 중요합니다. 실시간성이 중요한 경우 SSE가 더 나은 선택이 될 수 있습니다. 반면, 클라이언트가 서버에 데이터를 자주 보내야 하는 경우 AJAX 방식이 더 적합할 수 있습니다.

SSE 방식의 채팅 예졔를 PHP로 작성해줘

SSE(Server-Sent Events)를 사용하여 PHP로 간단한 채팅 애플리케이션을 구현하는 예제를 제공하겠습니다. 이 예제는 클라이언트가 서버에 메시지를 보내고, 서버가 모든 클라이언트에게 실시간으로 메시지를 전송하는 방식으로 작동합니다.

  1. 데이터베이스 설정
    먼저, 채팅 메시지를 저장할 데이터베이스를 설정해야 합니다. MySQL을 사용한다고 가정하겠습니다.
CREATE DATABASE chat_app;

USE chat_app;

CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    message TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  1. PHP 서버 코드
    2.1. 메시지 전송 (send_message.php)
    클라이언트가 메시지를 서버에 전송하는 PHP 스크립트입니다.
<?php
$mysqli = new mysqli("localhost", "username", "password", "chat_app");

if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $message = $mysqli->real_escape_string($_POST['message']);
    $mysqli->query("INSERT INTO messages (message) VALUES ('$message')");
}

$mysqli->close();
?>

2.2. 메시지 수신 (receive_messages.php)
서버가 클라이언트에게 실시간으로 메시지를 전송하는 PHP 스크립트입니다.

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$mysqli = new mysqli("localhost", "username", "password", "chat_app");

if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

$lastId = isset($_GET['lastId']) ? (int)$_GET['lastId'] : 0;

while (true) {
    $result = $mysqli->query("SELECT * FROM messages WHERE id > $lastId ORDER BY id ASC");

    while ($row = $result->fetch_assoc()) {
        echo "data: " . json_encode($row) . "\n\n";
        $lastId = $row['id'];
    }

    ob_flush();
    flush();
    sleep(1); // 1초 대기
}

$mysqli->close();
?>
  1. 클라이언트 코드 (index.html)
    클라이언트 측 HTML 및 JavaScript 코드입니다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat Application</title>
    <style>
        #messages {
            border: 1px solid #ccc;
            height: 300px;
            overflow-y: scroll;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <h1>Chat Application</h1>
    <div id="messages"></div>
    <input type="text" id="messageInput" placeholder="Type your message here..." />
    <button id="sendButton">Send</button>

    <script>
        const messagesDiv = document.getElementById('messages');
        const messageInput = document.getElementById('messageInput');
        const sendButton = document.getElementById('sendButton');
        let lastId = 0;

        // SSE 연결 설정
        const eventSource = new EventSource('receive_messages.php?lastId=' + lastId);

        eventSource.onmessage = function(event) {
            const data = JSON.parse(event.data);
            lastId = data.id; // 마지막 메시지 ID 업데이트
            messagesDiv.innerHTML += `<p>${data.created_at}: ${data.message}</p>`;
            messagesDiv.scrollTop = messagesDiv.scrollHeight; // 스크롤을 아래로 이동
        };

        sendButton.onclick = function() {
            const message = messageInput.value;
            if (message) {
                // 메시지 전송
                fetch('send_message.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: 'message=' + encodeURIComponent(message)
                });
                messageInput.value = ''; // 입력 필드 비우기
            }
        };
    </script>
</body>
</html>

설명
데이터베이스 설정: messages 테이블을 생성하여 채팅 메시지를 저장합니다.

메시지 전송 (send_message.php): 클라이언트가 POST 요청으로 메시지를 서버에 전송하면, 이 스크립트가 메시지를 데이터베이스에 저장합니다.

메시지 수신 (receive_messages.php): 클라이언트가 이 스크립트에 연결하면, 서버는 새로운 메시지를 지속적으로 확인하고, 새로운 메시지가 있을 경우 클라이언트에게 전송합니다.

클라이언트 코드 (index.html): 클라이언트는 메시지를 입력하고 전송할 수 있으며, 서버로부터 실시간으로 메시지를 수신하여 화면에 표시합니다.

주의사항
데이터베이스 연결 정보(username, password)를 실제 환경에 맞게 수정해야 합니다.

이 예제는 간단한 구현으로, 실제 애플리케이션에서는 보안, 오류 처리, 성능 최적화 등을 고려해야 합니다.

여러명이 사용하는 것을 고려하여 테이블에 userid 를 추가하여 다시 작성해줘

여러 사용자가 사용하는 채팅 애플리케이션을 위해 messages 테이블에 user_id를 추가하여 각 메시지가 어떤 사용자에 의해 전송되었는지를 기록하도록 하겠습니다. 아래는 수정된 데이터베이스 설정, PHP 서버 코드, 클라이언트 코드입니다.

  1. 데이터베이스 설정
    먼저, messages 테이블에 user_id 컬럼을 추가합니다. 사용자 정보를 저장할 users 테이블도 추가하겠습니다.
CREATE DATABASE chat_app;

USE chat_app;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL
);

CREATE TABLE messages (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    message TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);
  1. PHP 서버 코드
    2.1. 사용자 추가 (add_user.php)
    사용자를 추가하는 PHP 스크립트입니다.
<?php
$mysqli = new mysqli("localhost", "username", "password", "chat_app");

if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $mysqli->real_escape_string($_POST['username']);
    $mysqli->query("INSERT INTO users (username) VALUES ('$username')");
}

$mysqli->close();
?>

2.2. 메시지 전송 (send_message.php)
클라이언트가 메시지를 서버에 전송하는 PHP 스크립트입니다. 이제 user_id를 포함합니다.

<?php
$mysqli = new mysqli("localhost", "username", "password", "chat_app");

if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $user_id = (int)$_POST['user_id'];
    $message = $mysqli->real_escape_string($_POST['message']);
    $mysqli->query("INSERT INTO messages (user_id, message) VALUES ('$user_id', '$message')");
}

$mysqli->close();
?>

2.3. 메시지 수신 (receive_messages.php)
서버가 클라이언트에게 실시간으로 메시지를 전송하는 PHP 스크립트입니다. 사용자 이름도 포함하여 전송합니다.

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

$mysqli = new mysqli("localhost", "username", "password", "chat_app");

if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

$lastId = isset($_GET['lastId']) ? (int)$_GET['lastId'] : 0;

while (true) {
    $result = $mysqli->query("SELECT messages.*, users.username FROM messages JOIN users ON messages.user_id = users.id WHERE messages.id > $lastId ORDER BY messages.id ASC");

    while ($row = $result->fetch_assoc()) {
        echo "data: " . json_encode($row) . "\n\n";
        $lastId = $row['id'];
    }

    ob_flush();
    flush();
    sleep(1); // 1초 대기
}

$mysqli->close();
?>
  1. 클라이언트 코드 (index.html)
    클라이언트 측 HTML 및 JavaScript 코드입니다. 사용자가 메시지를 보낼 때 사용자 ID를 포함합니다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat Application</title>
    <style>
        #messages {
            border: 1px solid #ccc;
            height: 300px;
            overflow-y: scroll;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <h1>Chat Application</h1>
    <div id="messages"></div>
    <input type="text" id="usernameInput" placeholder="Enter your username" />
    <input type="text" id="messageInput" placeholder="Type your message here..." />
    <button id="sendButton">Send</button>

    <script>
        const messagesDiv = document.getElementById('messages');
        const usernameInput = document.getElementById('usernameInput');
        const messageInput = document.getElementById('messageInput');
        const sendButton = document.getElementById('sendButton');
        let lastId = 0;
        let userId = null;

        // 사용자 추가
        sendButton.onclick = function() {
            const username = usernameInput.value;
            if (username) {
                fetch('add_user.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: 'username=' + encodeURIComponent(username)
                }).then(response => {
                    return response.text();
                }).then(data => {
                    userId = data; // 사용자 ID를 저장
                    usernameInput.disabled = true; // 사용자 이름 입력 비활성화
                });
            }
        };

        // SSE 연결 설정
        const eventSource = new EventSource('receive_messages.php?lastId=' + lastId);

        eventSource.onmessage = function(event) {
            const data = JSON.parse(event.data);
            lastId = data.id; // 마지막 메시지 ID 업데이트
            messagesDiv.innerHTML += `<p><strong>${data.username}</strong>: ${data.message}</p>`;
            messagesDiv.scrollTop = messagesDiv.scrollHeight; // 스크롤을 아래로 이동
        };

        sendButton.onclick = function() {
            const message = messageInput.value;
            if (message && userId) {
                // 메시지 전송
                fetch('send_message.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: 'user_id=' + encodeURIComponent(userId) + '&message=' + encodeURIComponent(message)
                });
                messageInput.value = ''; // 입력 필드 비우기
            }
        };
    </script>
</body>
</html>

설명
데이터베이스 설정: users 테이블을 추가하여 사용자 정보를 저장하고, messages 테이블에 user_id를 추가하여 각 메시지가 어떤 사용자에 의해 전송되었는지를 기록합니다.

사용자 추가 (add_user.php): 클라이언트가 사용자 이름을 입력하면, 이 스크립트가 사용자 정보를 데이터베이스에 저장합니다.

메시지 전송 (send_message.php): 클라이언트가 메시지를 전송할 때 사용자 ID를 포함하여 메시지를 데이터베이스에 저장합니다.

메시지 수신 (receive_messages.php): 서버가 클라이언트에게 실시간으로 메시지를 전송하며, 각 메시지에 해당하는 사용자 이름도 포함합니다.

클라이언트 코드 (index.html): 사용자가 사용자 이름을 입력하고 메시지를 전송할 수 있으며, 서버로부터 실시간으로 메시지를 수신하여 화면에 표시합니다.

주의사항
데이터베이스 연결 정보(username, password)를 실제 환경에 맞게 수정해야 합니다.

이 예제는 간단한 구현으로, 실제 애플리케이션에서는 보안, 오류 처리, 성능 최적화 등을 고려해야 합니다.

0개의 댓글