요청 1회시 연결을 유지한 채 실시간으로 서버로부터 계속 데이터를 받을 수 있는 방법.

기존 웹 백엔드에 가볍게 추가하면 되므로,

양방향 통신이 필요한 경우가 아니라면 부담스럽게 서버 구성하고 연결 유지해야 하는 websocket 대신 쓰기 좋을 듯.

 

다음은 차트 페이지에서 차트를 그리기 전 데이터를 가져오는게 좀 오래걸리는 상황에서 실시간으로 진행률을 progress bar로 표시해주는 예제.

 

 

front-end (html)

<!doctype html>
<html>
<head>
<script src="/PATH/TO/draw_chart.js"></script>
</head>
<body> 
    <progress id="progress_bar" value="0" max="100"></progress>
    <span id="progress_percentage">0 %</span>
    <br>   
    <button id="btn">Get</button>
</body>
</html>

 

 

front-end (js)

window.addEventListener('DOMContentLoaded', ()=>{
    const progress_bar = document.getElementById('progress_bar');
    const progress_percentage = document.getElementById('progress_percentage');
 
    document.getElementById('btn').addEventListener('click', ()=>{
        const evtSrc = new EventSource('/PATH/TO/get_chart_data.php');
     
        evtSrc.addEventListener('error', (err)=>{
            // 기본적으로 네트워크가 끊어지면 자동 재접속하므로 방지하려면 여기서 명시적으로 close 해줘야 함
            evtSrc.close();
        });
 
        // 전송되는 데이터에 "event: " 가 없고 "data: " 만 있으면 여기로 옴
        evtSrc.addEventListener('message', (evt)=>{
            console.log(`message = ${evt.data}`);
        });
 
        // event: progress_value
        evtSrc.addEventListener('progress_value', (evt)=>{
            const progress_value = evt.data;
            progress_bar.value = progress_value;
            progress_percentage.textContent = `${progress_value} %`;
        });
        // event: complete
        evtSrc.addEventListener('complete', (evt)=>{
            // TODO 차트 그리기
            // drawChart(JSON.parse(evt.data));
        });
    });
});

 

 

 

back-end (php)

<?php
 
header("Cache-Control: no-store");                               
header("Content-Type: text/event-stream");
 
 
// 기간은 한달
$endDate = date('Ymd', strtotime('yesterday'));
$startDate = date('Ymd', strtotime('-1 month'));
$dateDiff = date_diff(new DateTime($startDate), new DateTime($endDate))->days + 1;
 
$chartData = [];
 
$dt = $startDate;
$i = 0;
// 데이터를 하루치씩 가져오면서 클라이언트에 진행률 전송
while ($dt <= $endDate) {
    // 특정 날짜에 해당하는 차트 데이터 가져오기
    $chartData[$dt] = getChartData($dt);
 
    // data만 있는 경우 테스트
    if (rand(0,10) === 5) {
        send('', 'no event, data only');
    }
 
    // 진행률 계산, 전송
    ++$i;
    $progressValue = sprintf("%.1f", ($i / $dateDiff) * 100);
    send('progress_value', $progressValue);
 
    $dt = date('Ymd', strtotime($dt)+86400);
}
 
// 완료시 차트 데이터 전송하고 종료
send('complete', json_encode($chartData));
 
 
// 하루치 차트 데이터 가져오기. 찔끔 시간끄는 pseudo code
function getChartData($dt)
{
    usleep(100000);
    return [];
}
 
// 클라이언트에 데이터 전송
function send($msg, $data)
{  
    if (trim($msg)) {
        echo "event: ".$msg."\n";
    }
     
    echo "data: ".$data."\n\n";
    ob_end_flush();
    flush();
}

 

 

[참고]

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events

 

Posted by bloodguy
,